mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 10:33:44 +02:00
feat: combine UI screens with service layer
This commit is contained in:
parent
4113e9fea2
commit
e8822d92a3
8 changed files with 97 additions and 33 deletions
|
@ -4,3 +4,7 @@
|
||||||
|
|
||||||
///
|
///
|
||||||
library flutter_timeline_firebase;
|
library flutter_timeline_firebase;
|
||||||
|
|
||||||
|
export 'src/config/firebase_timeline_options.dart';
|
||||||
|
export 'src/service/firebase_timeline_service.dart';
|
||||||
|
export 'src/service/firebase_user_service.dart';
|
||||||
|
|
|
@ -8,8 +8,9 @@ import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_storage/firebase_storage.dart';
|
import 'package:firebase_storage/firebase_storage.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_timeline_firebase/config/firebase_timeline_options.dart';
|
import 'package:flutter_timeline_firebase/src/config/firebase_timeline_options.dart';
|
||||||
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class FirebaseTimelineService implements TimelineService {
|
class FirebaseTimelineService implements TimelineService {
|
||||||
FirebaseTimelineService({
|
FirebaseTimelineService({
|
||||||
|
@ -32,14 +33,17 @@ class FirebaseTimelineService implements TimelineService {
|
||||||
List<TimelinePost> _posts = [];
|
List<TimelinePost> _posts = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> createPost(TimelinePost post) async {
|
Future<TimelinePost> createPost(TimelinePost post) async {
|
||||||
var imageRef = _storage.ref().child('timeline/${post.id}');
|
var postId = const Uuid().v4();
|
||||||
|
var imageRef = _storage.ref().child('timeline/$postId');
|
||||||
var result = await imageRef.putData(post.image!);
|
var result = await imageRef.putData(post.image!);
|
||||||
var imageUrl = await result.ref.getDownloadURL();
|
var imageUrl = await result.ref.getDownloadURL();
|
||||||
var updatedPost = post.copyWith(imageUrl: imageUrl);
|
var updatedPost = post.copyWith(imageUrl: imageUrl, id: postId);
|
||||||
var postRef = _db.collection(_options.timelineCollectionName).doc(post.id);
|
var postRef =
|
||||||
|
_db.collection(_options.timelineCollectionName).doc(updatedPost.id);
|
||||||
_posts.add(updatedPost);
|
_posts.add(updatedPost);
|
||||||
return postRef.set(updatedPost.toJson());
|
await postRef.set(updatedPost.toJson());
|
||||||
|
return updatedPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -56,15 +60,17 @@ class FirebaseTimelineService implements TimelineService {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<TimelinePost>> fetchPosts(String? category) async {
|
Future<List<TimelinePost>> fetchPosts(String? category) async {
|
||||||
var snapshot = await _db
|
var snapshot = (category != null)
|
||||||
|
? await _db
|
||||||
.collection(_options.timelineCollectionName)
|
.collection(_options.timelineCollectionName)
|
||||||
.where('category', isEqualTo: category)
|
.where('category', isEqualTo: category)
|
||||||
.get();
|
.get()
|
||||||
|
: await _db.collection(_options.timelineCollectionName).get();
|
||||||
|
|
||||||
var posts = <TimelinePost>[];
|
var posts = <TimelinePost>[];
|
||||||
for (var doc in snapshot.docs) {
|
for (var doc in snapshot.docs) {
|
||||||
var data = doc.data();
|
var data = doc.data();
|
||||||
var user = await _userService.getUser(data['user_id']);
|
var user = await _userService.getUser(data['creator_id']);
|
||||||
var post = TimelinePost.fromJson(doc.id, data).copyWith(creator: user);
|
var post = TimelinePost.fromJson(doc.id, data).copyWith(creator: user);
|
||||||
posts.add(post);
|
posts.add(post);
|
||||||
}
|
}
|
||||||
|
@ -72,6 +78,11 @@ class FirebaseTimelineService implements TimelineService {
|
||||||
return posts;
|
return posts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<TimelinePost> getPosts(String? category) => _posts
|
||||||
|
.where((element) => category == null || element.category == category)
|
||||||
|
.toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> likePost(String userId, TimelinePost post) {
|
Future<void> likePost(String userId, TimelinePost post) {
|
||||||
// update the post with the new like
|
// update the post with the new like
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter_timeline_firebase/config/firebase_timeline_options.dart';
|
import 'package:flutter_timeline_firebase/src/config/firebase_timeline_options.dart';
|
||||||
import 'package:flutter_timeline_firebase/models/firebase_user_document.dart';
|
import 'package:flutter_timeline_firebase/src/models/firebase_user_document.dart';
|
||||||
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
|
|
||||||
class FirebaseUserService implements TimelineUserService {
|
class FirebaseUserService implements TimelineUserService {
|
|
@ -9,8 +9,9 @@ import 'package:flutter_timeline_interface/src/model/timeline_reaction.dart';
|
||||||
|
|
||||||
abstract class TimelineService {
|
abstract class TimelineService {
|
||||||
Future<void> deletePost(TimelinePost post);
|
Future<void> deletePost(TimelinePost post);
|
||||||
Future<void> createPost(TimelinePost post);
|
Future<TimelinePost> createPost(TimelinePost post);
|
||||||
Future<List<TimelinePost>> fetchPosts(String? category);
|
Future<List<TimelinePost>> fetchPosts(String? category);
|
||||||
|
List<TimelinePost> getPosts(String? category);
|
||||||
Future<TimelinePost> fetchPostDetails(TimelinePost post);
|
Future<TimelinePost> fetchPostDetails(TimelinePost post);
|
||||||
Future<void> reactToPost(
|
Future<void> reactToPost(
|
||||||
TimelinePost post,
|
TimelinePost post,
|
||||||
|
|
|
@ -7,15 +7,30 @@ import 'dart:typed_data';
|
||||||
import 'package:dotted_border/dotted_border.dart';
|
import 'package:dotted_border/dotted_border.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_image_picker/flutter_image_picker.dart';
|
import 'package:flutter_image_picker/flutter_image_picker.dart';
|
||||||
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
import 'package:flutter_timeline_view/src/config/timeline_options.dart';
|
import 'package:flutter_timeline_view/src/config/timeline_options.dart';
|
||||||
|
|
||||||
class TimelinePostCreationScreen extends StatefulWidget {
|
class TimelinePostCreationScreen extends StatefulWidget {
|
||||||
const TimelinePostCreationScreen({
|
const TimelinePostCreationScreen({
|
||||||
|
required this.userId,
|
||||||
|
required this.postCategory,
|
||||||
|
required this.onPostCreated,
|
||||||
|
required this.service,
|
||||||
required this.options,
|
required this.options,
|
||||||
this.padding = const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
|
this.padding = const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final String userId;
|
||||||
|
|
||||||
|
final String postCategory;
|
||||||
|
|
||||||
|
/// called when the post is created
|
||||||
|
final Function(TimelinePost) onPostCreated;
|
||||||
|
|
||||||
|
/// The service to use for creating the post
|
||||||
|
final TimelineService service;
|
||||||
|
|
||||||
/// The options for the timeline
|
/// The options for the timeline
|
||||||
final TimelineOptions options;
|
final TimelineOptions options;
|
||||||
|
|
||||||
|
@ -59,6 +74,23 @@ class _TimelinePostCreationScreenState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Future<void> onPostCreated() async {
|
||||||
|
var post = TimelinePost(
|
||||||
|
id: '',
|
||||||
|
creatorId: widget.userId,
|
||||||
|
title: titleController.text,
|
||||||
|
category: widget.postCategory,
|
||||||
|
content: contentController.text,
|
||||||
|
likes: 0,
|
||||||
|
reaction: 0,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
reactionEnabled: allowComments,
|
||||||
|
image: image,
|
||||||
|
);
|
||||||
|
var newPost = await widget.service.createPost(post);
|
||||||
|
widget.onPostCreated.call(newPost);
|
||||||
|
}
|
||||||
|
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
|
@ -214,12 +246,12 @@ class _TimelinePostCreationScreenState
|
||||||
child: (widget.options.buttonBuilder != null)
|
child: (widget.options.buttonBuilder != null)
|
||||||
? widget.options.buttonBuilder!(
|
? widget.options.buttonBuilder!(
|
||||||
context,
|
context,
|
||||||
() {},
|
onPostCreated,
|
||||||
widget.options.translations.checkPost,
|
widget.options.translations.checkPost,
|
||||||
enabled: editingDone,
|
enabled: editingDone,
|
||||||
)
|
)
|
||||||
: ElevatedButton(
|
: ElevatedButton(
|
||||||
onPressed: editingDone ? () {} : null,
|
onPressed: editingDone ? onPostCreated : null,
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.options.translations.checkPost,
|
widget.options.translations.checkPost,
|
||||||
style: theme.textTheme.bodyMedium,
|
style: theme.textTheme.bodyMedium,
|
||||||
|
|
|
@ -9,15 +9,21 @@ import 'package:flutter_timeline_view/src/widgets/timeline_post_widget.dart';
|
||||||
|
|
||||||
class TimelineScreen extends StatefulWidget {
|
class TimelineScreen extends StatefulWidget {
|
||||||
const TimelineScreen({
|
const TimelineScreen({
|
||||||
|
required this.userId,
|
||||||
required this.options,
|
required this.options,
|
||||||
required this.posts,
|
required this.posts,
|
||||||
required this.onPostTap,
|
required this.onPostTap,
|
||||||
|
required this.service,
|
||||||
this.controller,
|
this.controller,
|
||||||
this.timelineCategoryFilter,
|
this.timelineCategoryFilter,
|
||||||
this.timelinePostHeight = 100.0,
|
this.timelinePostHeight = 100.0,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final String userId;
|
||||||
|
|
||||||
|
final TimelineService service;
|
||||||
|
|
||||||
final TimelineOptions options;
|
final TimelineOptions options;
|
||||||
|
|
||||||
final ScrollController? controller;
|
final ScrollController? controller;
|
||||||
|
@ -44,10 +50,15 @@ class _TimelineScreenState extends State<TimelineScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => SingleChildScrollView(
|
Widget build(BuildContext context) => FutureBuilder(
|
||||||
|
// ignore: discarded_futures
|
||||||
|
future: widget.service.fetchPosts(widget.timelineCategoryFilter),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData && snapshot.data != null) {
|
||||||
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
for (var post in widget.posts)
|
for (var post in snapshot.data!)
|
||||||
if (widget.timelineCategoryFilter == null ||
|
if (widget.timelineCategoryFilter == null ||
|
||||||
post.category == widget.timelineCategoryFilter)
|
post.category == widget.timelineCategoryFilter)
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -62,4 +73,9 @@ class _TimelineScreenState extends State<TimelineScreen> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue