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
- 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 refresh functionality to the timeline with a pull to refresh callback to allow additional functionality when refreshing the timeline
## 3.0.1

View file

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

View file

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

View file

@ -57,6 +57,7 @@ class TimelineUserStoryConfiguration {
this.postOverviewOpenPageBuilder,
this.onPostTap,
this.onUserTap,
this.onRefresh,
this.onPostDelete,
this.filterEnabled = false,
this.postWidgetBuilder,
@ -127,6 +128,9 @@ class TimelineUserStoryConfiguration {
/// A callback function invoked when the user's profile is tapped.
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.
final Widget Function(BuildContext context, TimelinePost post)? onPostDelete;

View file

@ -16,6 +16,7 @@ class TimelineScreen extends StatefulWidget {
this.onPostTap,
this.scrollController,
this.onUserTap,
this.onRefresh,
this.posts,
this.timelineCategory,
this.postWidgetBuilder,
@ -45,6 +46,9 @@ class TimelineScreen extends StatefulWidget {
/// Called when a post is tapped
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
final Function(String userId)? onUserTap;
@ -218,72 +222,81 @@ class _TimelineScreenState extends State<TimelineScreen> {
height: 12,
),
Expanded(
child: SingleChildScrollView(
controller: controller,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// Add a optional custom header to the list of posts
widget.options.listHeaderBuilder?.call(context, category) ??
const SizedBox.shrink(),
...posts.map(
(post) => Padding(
padding: widget.options.postPadding,
child: widget.postWidgetBuilder?.call(post) ??
TimelinePostWidget(
service: service,
userId: widget.userId,
options: widget.options,
post: post,
onTap: () async {
if (widget.onPostTap != null) {
widget.onPostTap!.call(post);
child: RefreshIndicator(
onRefresh: () async {
await widget.onRefresh?.call(context, category);
await loadPosts();
},
child: SingleChildScrollView(
controller: controller,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// Add a optional custom header to the list of posts
widget.options.listHeaderBuilder
?.call(context, category) ??
const SizedBox.shrink(),
...posts.map(
(post) => Padding(
padding: widget.options.postPadding,
child: widget.postWidgetBuilder?.call(post) ??
TimelinePostWidget(
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(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
body: TimelinePostScreen(
userId: 'test_user',
service: service,
options: widget.options,
post: post,
onPostDelete: () {
service.postService.deletePost(post);
Navigator.of(context).pop();
},
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
body: TimelinePostScreen(
userId: 'test_user',
service: service,
options: widget.options,
post: post,
onPostDelete: () {
service.postService
.deletePost(post);
Navigator.of(context).pop();
},
),
),
),
),
);
},
onTapLike: () async => service.postService
.likePost(widget.userId, post),
onTapUnlike: () async => service.postService
.unlikePost(widget.userId, post),
onPostDelete: () async =>
service.postService.deletePost(post),
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,
),
);
},
onTapLike: () async => service.postService
.likePost(widget.userId, post),
onTapUnlike: () async => service.postService
.unlikePost(widget.userId, post),
onPostDelete: () async =>
service.postService.deletePost(post),
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,
),
),
),
],
),
),
),
),