feat: add a refreshindicator to the timeline with an extra callback onRefresh

This commit is contained in:
Freek van de Ven 2024-05-21 08:39:34 +02:00
parent 7fa33cdfb4
commit 035f795130
5 changed files with 80 additions and 60 deletions

View file

@ -7,6 +7,7 @@
- Set an optional max length on the default post title input field - Set an optional max length on the default post title input field
- Add a postCreationFloatingActionButtonColor to the timeline theme to set the color of the floating action button - Add a postCreationFloatingActionButtonColor to the timeline theme to set the color of the floating action button
- Add a post and a category to the postViewOpenPageBuilder function - Add a post and a category to the postViewOpenPageBuilder function
- Add a refresh functionality to the timeline with a pull to refresh callback to allow additional functionality when refreshing the timeline
## 3.0.1 ## 3.0.1

View file

@ -33,6 +33,7 @@ List<GoRoute> getTimelineStoryRoutes({
var timelineScreen = TimelineScreen( var timelineScreen = TimelineScreen(
userId: config.getUserId?.call(context) ?? config.userId, userId: config.getUserId?.call(context) ?? config.userId,
onUserTap: (user) => config.onUserTap?.call(context, user), onUserTap: (user) => config.onUserTap?.call(context, user),
onRefresh: config.onRefresh,
service: service, service: service,
options: config.optionsBuilder(context), options: config.optionsBuilder(context),
onPostTap: (post) async => onPostTap: (post) async =>

View file

@ -60,6 +60,7 @@ Widget _timelineScreenRoute({
), ),
), ),
), ),
onRefresh: config.onRefresh,
filterEnabled: config.filterEnabled, filterEnabled: config.filterEnabled,
postWidgetBuilder: config.postWidgetBuilder, postWidgetBuilder: config.postWidgetBuilder,
); );

View file

@ -57,6 +57,7 @@ class TimelineUserStoryConfiguration {
this.postOverviewOpenPageBuilder, this.postOverviewOpenPageBuilder,
this.onPostTap, this.onPostTap,
this.onUserTap, this.onUserTap,
this.onRefresh,
this.onPostDelete, this.onPostDelete,
this.filterEnabled = false, this.filterEnabled = false,
this.postWidgetBuilder, this.postWidgetBuilder,
@ -127,6 +128,9 @@ class TimelineUserStoryConfiguration {
/// A callback function invoked when the user's profile is tapped. /// A callback function invoked when the user's profile is tapped.
final Function(BuildContext context, String userId)? onUserTap; final Function(BuildContext context, String userId)? onUserTap;
/// A callback function invoked when the timeline is refreshed by pulling down
final Function(BuildContext context, String? category)? onRefresh;
/// A callback function invoked when a post deletion is requested. /// A callback function invoked when a post deletion is requested.
final Widget Function(BuildContext context, TimelinePost post)? onPostDelete; final Widget Function(BuildContext context, TimelinePost post)? onPostDelete;

View file

@ -16,6 +16,7 @@ class TimelineScreen extends StatefulWidget {
this.onPostTap, this.onPostTap,
this.scrollController, this.scrollController,
this.onUserTap, this.onUserTap,
this.onRefresh,
this.posts, this.posts,
this.timelineCategory, this.timelineCategory,
this.postWidgetBuilder, this.postWidgetBuilder,
@ -45,6 +46,9 @@ class TimelineScreen extends StatefulWidget {
/// Called when a post is tapped /// Called when a post is tapped
final Function(TimelinePost)? onPostTap; final Function(TimelinePost)? onPostTap;
/// Called when the timeline is refreshed by pulling down
final Function(BuildContext context, String? category)? onRefresh;
/// If this is not null, the user can tap on the user avatar or name /// If this is not null, the user can tap on the user avatar or name
final Function(String userId)? onUserTap; final Function(String userId)? onUserTap;
@ -218,72 +222,81 @@ class _TimelineScreenState extends State<TimelineScreen> {
height: 12, height: 12,
), ),
Expanded( Expanded(
child: SingleChildScrollView( child: RefreshIndicator(
controller: controller, onRefresh: () async {
child: Column( await widget.onRefresh?.call(context, category);
mainAxisSize: MainAxisSize.min, await loadPosts();
children: [ },
/// Add a optional custom header to the list of posts child: SingleChildScrollView(
widget.options.listHeaderBuilder?.call(context, category) ?? controller: controller,
const SizedBox.shrink(), child: Column(
...posts.map( mainAxisSize: MainAxisSize.min,
(post) => Padding( children: [
padding: widget.options.postPadding, /// Add a optional custom header to the list of posts
child: widget.postWidgetBuilder?.call(post) ?? widget.options.listHeaderBuilder
TimelinePostWidget( ?.call(context, category) ??
service: service, const SizedBox.shrink(),
userId: widget.userId, ...posts.map(
options: widget.options, (post) => Padding(
post: post, padding: widget.options.postPadding,
onTap: () async { child: widget.postWidgetBuilder?.call(post) ??
if (widget.onPostTap != null) { TimelinePostWidget(
widget.onPostTap!.call(post); service: service,
userId: widget.userId,
options: widget.options,
post: post,
onTap: () async {
if (widget.onPostTap != null) {
widget.onPostTap!.call(post);
return; return;
} }
await Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => Scaffold( builder: (context) => Scaffold(
body: TimelinePostScreen( body: TimelinePostScreen(
userId: 'test_user', userId: 'test_user',
service: service, service: service,
options: widget.options, options: widget.options,
post: post, post: post,
onPostDelete: () { onPostDelete: () {
service.postService.deletePost(post); service.postService
Navigator.of(context).pop(); .deletePost(post);
}, Navigator.of(context).pop();
},
),
), ),
), ),
), );
); },
}, onTapLike: () async => service.postService
onTapLike: () async => service.postService .likePost(widget.userId, post),
.likePost(widget.userId, post), onTapUnlike: () async => service.postService
onTapUnlike: () async => service.postService .unlikePost(widget.userId, post),
.unlikePost(widget.userId, post), onPostDelete: () async =>
onPostDelete: () async => service.postService.deletePost(post),
service.postService.deletePost(post), onUserTap: widget.onUserTap,
onUserTap: widget.onUserTap, ),
),
),
),
if (posts.isEmpty)
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
category == null
? widget.options.translations.noPosts!
: widget
.options.translations.noPostsWithFilter!,
style: widget.options.theme.textStyles.noPostsStyle,
),
), ),
), ),
], if (posts.isEmpty)
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
category == null
? widget.options.translations.noPosts!
: widget
.options.translations.noPostsWithFilter!,
style:
widget.options.theme.textStyles.noPostsStyle,
),
),
),
],
),
), ),
), ),
), ),