From 4f7fa834e44ed2968651496fdf1d2e9a7b8fea37 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 9 May 2024 21:45:24 +0200 Subject: [PATCH 01/24] feat: add serviceBuilder for userstory configuration to fetch the service when needed --- CHANGELOG.md | 5 +++ .../flutter_timeline_gorouter_userstory.dart | 38 +++++++++---------- .../src/models/timeline_configuration.dart | 4 ++ packages/flutter_timeline/pubspec.yaml | 6 +-- .../flutter_timeline_firebase/pubspec.yaml | 4 +- .../flutter_timeline_interface/pubspec.yaml | 2 +- packages/flutter_timeline_view/pubspec.yaml | 4 +- 7 files changed, 36 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ef9cf..9bee2c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.0.0 + +- Add a serviceBuilder to the userstory configuration + + ## 3.0.1 - Fixed postOverviewScreen not displaying the creators name. diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 41e68fc..c1c68db 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -28,10 +28,11 @@ List getTimelineStoryRoutes({ GoRoute( path: TimelineUserStoryRoutes.timelineHome, pageBuilder: (context, state) { + var service = config.serviceBuilder?.call(context) ?? config.service; var timelineScreen = TimelineScreen( userId: config.userId, onUserTap: (user) => config.onUserTap?.call(context, user), - service: config.service, + service: service, options: config.optionsBuilder(context), onPostTap: (post) async => config.onPostTap?.call(context, post) ?? @@ -129,13 +130,13 @@ List getTimelineStoryRoutes({ GoRoute( path: TimelineUserStoryRoutes.timelineView, pageBuilder: (context, state) { - var post = - config.service.postService.getPost(state.pathParameters['post']!); + var service = config.serviceBuilder?.call(context) ?? config.service; + var post = service.postService.getPost(state.pathParameters['post']!); var timelinePostWidget = TimelinePostScreen( userId: config.userId, options: config.optionsBuilder(context), - service: config.service, + service: service, post: post!, onPostDelete: () => config.onPostDelete?.call(context, post), onUserTap: (user) => config.onUserTap?.call(context, user), @@ -174,19 +175,19 @@ List getTimelineStoryRoutes({ path: TimelineUserStoryRoutes.timelinePostCreation, pageBuilder: (context, state) { var category = state.pathParameters['category']; + var service = config.serviceBuilder?.call(context) ?? config.service; var timelinePostCreationWidget = TimelinePostCreationScreen( userId: config.userId, options: config.optionsBuilder(context), - service: config.service, + service: service, onPostCreated: (post) async { - var newPost = await config.service.postService.createPost(post); - if (context.mounted) { - if (config.afterPostCreationGoHome) { - context.go(TimelineUserStoryRoutes.timelineHome); - } else { - await context - .push(TimelineUserStoryRoutes.timelineViewPath(newPost.id)); - } + var newPost = await service.postService.createPost(post); + if (!context.mounted) return; + if (config.afterPostCreationGoHome) { + context.go(TimelineUserStoryRoutes.timelineHome); + } else { + await context + .push(TimelineUserStoryRoutes.timelineViewPath(newPost.id)); } }, onPostOverview: (post) async => context.push( @@ -233,16 +234,15 @@ List getTimelineStoryRoutes({ path: TimelineUserStoryRoutes.timelinePostOverview, pageBuilder: (context, state) { var post = state.extra! as TimelinePost; - + var service = config.serviceBuilder?.call(context) ?? config.service; var timelinePostOverviewWidget = TimelinePostOverviewScreen( options: config.optionsBuilder(context), - service: config.service, + service: service, timelinePost: post, onPostSubmit: (post) async { - await config.service.postService.createPost(post); - if (context.mounted) { - context.go(TimelineUserStoryRoutes.timelineHome); - } + await service.postService.createPost(post); + if (!context.mounted) return; + context.go(TimelineUserStoryRoutes.timelineHome); }, ); var backButton = IconButton( diff --git a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart index 264be11..8ce93cf 100644 --- a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart +++ b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart @@ -48,6 +48,7 @@ class TimelineUserStoryConfiguration { const TimelineUserStoryConfiguration({ required this.service, required this.optionsBuilder, + this.serviceBuilder, this.userId = 'test_user', this.homeOpenPageBuilder, this.postCreationOpenPageBuilder, @@ -69,6 +70,9 @@ class TimelineUserStoryConfiguration { /// The TimelineService responsible for fetching user story data. final TimelineService service; + /// A function to get the timeline service only when needed and with a context + final TimelineService Function(BuildContext context)? serviceBuilder; + /// A function that builds TimelineOptions based on the given BuildContext. final TimelineOptions Function(BuildContext context) optionsBuilder; diff --git a/packages/flutter_timeline/pubspec.yaml b/packages/flutter_timeline/pubspec.yaml index bfa62ef..ecb9108 100644 --- a/packages/flutter_timeline/pubspec.yaml +++ b/packages/flutter_timeline/pubspec.yaml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later name: flutter_timeline description: Visual elements and interface combined into one package -version: 3.0.1 +version: 4.0.0 publish_to: none @@ -19,13 +19,13 @@ dependencies: git: url: https://github.com/Iconica-Development/flutter_timeline path: packages/flutter_timeline_view - ref: 3.0.1 + ref: 4.0.0 flutter_timeline_interface: git: url: https://github.com/Iconica-Development/flutter_timeline path: packages/flutter_timeline_interface - ref: 3.0.1 + ref: 4.0.0 dev_dependencies: flutter_lints: ^2.0.0 diff --git a/packages/flutter_timeline_firebase/pubspec.yaml b/packages/flutter_timeline_firebase/pubspec.yaml index 3b670d5..89ad8a8 100644 --- a/packages/flutter_timeline_firebase/pubspec.yaml +++ b/packages/flutter_timeline_firebase/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_timeline_firebase description: Implementation of the Flutter Timeline interface for Firebase. -version: 3.0.1 +version: 4.0.0 publish_to: none @@ -23,7 +23,7 @@ dependencies: git: url: https://github.com/Iconica-Development/flutter_timeline path: packages/flutter_timeline_interface - ref: 3.0.1 + ref: 4.0.0 dev_dependencies: flutter_lints: ^2.0.0 diff --git a/packages/flutter_timeline_interface/pubspec.yaml b/packages/flutter_timeline_interface/pubspec.yaml index 3960249..6e0fc04 100644 --- a/packages/flutter_timeline_interface/pubspec.yaml +++ b/packages/flutter_timeline_interface/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_timeline_interface description: Interface for the service of the Flutter Timeline component -version: 3.0.1 +version: 4.0.0 publish_to: none diff --git a/packages/flutter_timeline_view/pubspec.yaml b/packages/flutter_timeline_view/pubspec.yaml index 48f76bf..35f0c84 100644 --- a/packages/flutter_timeline_view/pubspec.yaml +++ b/packages/flutter_timeline_view/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_timeline_view description: Visual elements of the Flutter Timeline Component -version: 3.0.1 +version: 4.0.0 publish_to: none @@ -23,7 +23,7 @@ dependencies: git: url: https://github.com/Iconica-Development/flutter_timeline path: packages/flutter_timeline_interface - ref: 3.0.1 + ref: 4.0.0 flutter_image_picker: git: url: https://github.com/Iconica-Development/flutter_image_picker From 933386623a4e63d49c02b0c4d53ed44e36543006 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 9 May 2024 22:31:11 +0200 Subject: [PATCH 02/24] feat: add listHeaderBuilder --- CHANGELOG.md | 2 +- .../lib/src/config/timeline_options.dart | 6 ++++++ .../lib/src/screens/timeline_screen.dart | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bee2c8..a61398b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## 4.0.0 - Add a serviceBuilder to the userstory configuration - +- Add a listHeaderBuilder for showing a header at the top of the list of posts in the timeline ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart index e7c7c9b..b3c04d4 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart @@ -52,6 +52,7 @@ class TimelineOptions { this.maxContentLength, this.categorySelectorButtonBuilder, this.postOverviewButtonBuilder, + this.listHeaderBuilder, this.titleInputDecoration, this.contentInputDecoration, }); @@ -156,6 +157,11 @@ class TimelineOptions { String text, )? categorySelectorButtonBuilder; + /// This widgetbuilder is placed at the top of the list of posts and can be + /// used to add custom elements + final Widget Function(BuildContext context, String? category)? + listHeaderBuilder; + /// Builder for the post overview button /// on the timeline post overview screen final Widget Function( diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index 1d25524..a9d7575 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -223,6 +223,9 @@ class _TimelineScreenState extends State { 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, From deaca5b1269849ba0ffbf4bc18691fb06e663f3d Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 10 May 2024 09:20:43 +0200 Subject: [PATCH 03/24] feat: add getter for the userId in the timeline userstory configuration --- CHANGELOG.md | 1 + .../lib/src/flutter_timeline_gorouter_userstory.dart | 6 +++--- .../lib/src/flutter_timeline_navigator_userstory.dart | 6 +++--- .../lib/src/models/timeline_configuration.dart | 4 ++++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a61398b..7ff63f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Add a serviceBuilder to the userstory configuration - Add a listHeaderBuilder for showing a header at the top of the list of posts in the timeline +- Add a getUserId function to retrieve the userId when needed in the userstory configuration ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index c1c68db..fba23b9 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -30,7 +30,7 @@ List getTimelineStoryRoutes({ pageBuilder: (context, state) { var service = config.serviceBuilder?.call(context) ?? config.service; var timelineScreen = TimelineScreen( - userId: config.userId, + userId: config.getUserId?.call(context) ?? config.userId, onUserTap: (user) => config.onUserTap?.call(context, user), service: service, options: config.optionsBuilder(context), @@ -134,7 +134,7 @@ List getTimelineStoryRoutes({ var post = service.postService.getPost(state.pathParameters['post']!); var timelinePostWidget = TimelinePostScreen( - userId: config.userId, + userId: config.getUserId?.call(context) ?? config.userId, options: config.optionsBuilder(context), service: service, post: post!, @@ -177,7 +177,7 @@ List getTimelineStoryRoutes({ var category = state.pathParameters['category']; var service = config.serviceBuilder?.call(context) ?? config.service; var timelinePostCreationWidget = TimelinePostCreationScreen( - userId: config.userId, + userId: config.getUserId?.call(context) ?? config.userId, options: config.optionsBuilder(context), service: service, onPostCreated: (post) async { diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index da9325a..90465e4 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -45,7 +45,7 @@ Widget _timelineScreenRoute({ ); var timelineScreen = TimelineScreen( - userId: config.userId, + userId: config.getUserId?.call(context) ?? config.userId, onUserTap: (user) => config.onUserTap?.call(context, user), service: config.service, options: config.optionsBuilder(context), @@ -121,7 +121,7 @@ Widget _postDetailScreenRoute({ ); var timelinePostScreen = TimelinePostScreen( - userId: config.userId, + userId: config.getUserId?.call(context) ?? config.userId, options: config.optionsBuilder(context), service: config.service, post: post, @@ -176,7 +176,7 @@ Widget _postCreationScreenRoute({ ); var timelinePostCreationScreen = TimelinePostCreationScreen( - userId: config.userId, + userId: config.getUserId?.call(context) ?? config.userId, options: config.optionsBuilder(context), service: config.service, onPostCreated: (post) async { diff --git a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart index 8ce93cf..81938be 100644 --- a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart +++ b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart @@ -48,6 +48,7 @@ class TimelineUserStoryConfiguration { const TimelineUserStoryConfiguration({ required this.service, required this.optionsBuilder, + this.getUserId, this.serviceBuilder, this.userId = 'test_user', this.homeOpenPageBuilder, @@ -67,6 +68,9 @@ class TimelineUserStoryConfiguration { /// The ID of the user associated with this user story configuration. final String userId; + /// A function to get the userId only when needed and with a context + final String Function(BuildContext context)? getUserId; + /// The TimelineService responsible for fetching user story data. final TimelineService service; From 13960c4f1c300ae80424d2edaebab83efc8cc908 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 10 May 2024 09:40:07 +0200 Subject: [PATCH 04/24] fix: add nullcheck for timeline overview screen --- .../lib/src/screens/timeline_post_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 6d0c1bd..6b3c004 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -575,7 +575,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { ), ), ), - if (post.reactionEnabled && !widget.isOverviewScreen!) + if (post.reactionEnabled && !(widget.isOverviewScreen ?? false)) Align( alignment: Alignment.bottomCenter, child: ReactionBottom( From 8d13e4af277dcccc733239f246b9e4a5ab80d5e9 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 10 May 2024 10:07:15 +0200 Subject: [PATCH 05/24] fix: filter out timeline categories with key null when creating a new one --- CHANGELOG.md | 1 + .../flutter_timeline_view/lib/src/config/timeline_theme.dart | 5 +++++ .../lib/src/screens/timeline_selection_screen.dart | 5 +++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff63f0..2fd622a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Add a serviceBuilder to the userstory configuration - Add a listHeaderBuilder for showing a header at the top of the list of posts in the timeline - Add a getUserId function to retrieve the userId when needed in the userstory configuration +- Fix the timelinecategory selection by removing the categories with key null ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart index a2bb2c0..2bb32fe 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart @@ -15,6 +15,7 @@ class TimelineTheme { this.sendIcon, this.moreIcon, this.deleteIcon, + this.categorySelectionBorderColor, this.textStyles = const TimelineTextStyles(), }); @@ -38,5 +39,9 @@ class TimelineTheme { /// The icon for delete action (delete post) final Widget? deleteIcon; + /// The text style overrides for all the texts in the timeline final TimelineTextStyles textStyles; + + /// The color of the border around the category in the selection screen + final Color? categorySelectionBorderColor; } diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart index e9e16d0..2b32668 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart @@ -38,7 +38,7 @@ class TimelineSelectionScreen extends StatelessWidget { ), const SizedBox(height: 4), for (var category in categories.where( - (element) => element.canCreate, + (element) => element.canCreate && element.key != null, )) ...[ options.categorySelectorButtonBuilder?.call( context, @@ -55,7 +55,8 @@ class TimelineSelectionScreen extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), border: Border.all( - color: const Color(0xff71C6D1), + color: options.theme.categorySelectionBorderColor ?? + const Color(0xff71C6D1), width: 2, ), ), From 2c8d523f327db438af816326bcba7021ffb43acc Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 17 May 2024 14:38:13 +0200 Subject: [PATCH 06/24] fix: set the optional maxLength on the default inputfield for post titles --- CHANGELOG.md | 1 + .../lib/src/screens/timeline_post_creation_screen.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fd622a..479f846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Add a listHeaderBuilder for showing a header at the top of the list of posts in the timeline - Add a getUserId function to retrieve the userId when needed in the userstory configuration - Fix the timelinecategory selection by removing the categories with key null +- Set an optional max length on the default post title input field ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart index 2623912..b71a6d9 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart @@ -140,6 +140,7 @@ class _TimelinePostCreationScreenState '', ) ?? TextField( + maxLength: widget.options.maxTitleLength, controller: titleController, decoration: widget.options.contentInputDecoration ?? InputDecoration( From 24c779d43be518dda5987942dd8bbd4fb57dc772 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 17 May 2024 14:57:01 +0200 Subject: [PATCH 07/24] feat: add option to set postCreationFloatingActionButtonColor to override floatingbuttoncolor --- CHANGELOG.md | 1 + .../lib/src/flutter_timeline_gorouter_userstory.dart | 6 +++++- .../lib/src/flutter_timeline_navigator_userstory.dart | 6 +++++- .../lib/src/config/timeline_theme.dart | 4 ++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 479f846..bd4b83d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Add a getUserId function to retrieve the userId when needed in the userstory configuration - Fix the timelinecategory selection by removing the categories with key null - 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 ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index fba23b9..91b238c 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -44,7 +44,11 @@ List getTimelineStoryRoutes({ ); var button = FloatingActionButton( - backgroundColor: const Color(0xff71C6D1), + backgroundColor: config + .optionsBuilder(context) + .theme + .postCreationFloatingActionButtonColor ?? + const Color(0xff71C6D1), onPressed: () async => context.push( TimelineUserStoryRoutes.timelineCategorySelection, ), diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index 90465e4..b8f6d00 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -65,7 +65,11 @@ Widget _timelineScreenRoute({ ); var button = FloatingActionButton( - backgroundColor: const Color(0xff71C6D1), + backgroundColor: config + .optionsBuilder(context) + .theme + .postCreationFloatingActionButtonColor ?? + const Color(0xff71C6D1), onPressed: () async => Navigator.of(context).push( MaterialPageRoute( builder: (context) => _postCategorySelectionScreen( diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart index 2bb32fe..c5af788 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart @@ -16,6 +16,7 @@ class TimelineTheme { this.moreIcon, this.deleteIcon, this.categorySelectionBorderColor, + this.postCreationFloatingActionButtonColor, this.textStyles = const TimelineTextStyles(), }); @@ -44,4 +45,7 @@ class TimelineTheme { /// The color of the border around the category in the selection screen final Color? categorySelectionBorderColor; + + /// The color of the floating action button on the overview screen + final Color? postCreationFloatingActionButtonColor; } From 7fa33cdfb493acb73d7213bae451bd6b2196bd28 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Tue, 21 May 2024 07:23:49 +0200 Subject: [PATCH 08/24] feat: add a post and category to the postViewOpenPageBuilder --- CHANGELOG.md | 1 + .../src/flutter_timeline_gorouter_userstory.dart | 13 +++++++++++-- .../src/flutter_timeline_navigator_userstory.dart | 9 ++++++++- .../lib/src/models/timeline_configuration.dart | 2 ++ packages/flutter_timeline/pubspec.yaml | 2 ++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd4b83d..d16c78c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fix the timelinecategory selection by removing the categories with key null - 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 ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 91b238c..0b6da31 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: BSD-3-Clause +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_timeline/flutter_timeline.dart'; import 'package:flutter_timeline/src/go_router.dart'; @@ -136,6 +137,14 @@ List getTimelineStoryRoutes({ pageBuilder: (context, state) { var service = config.serviceBuilder?.call(context) ?? config.service; var post = service.postService.getPost(state.pathParameters['post']!); + var category = config.optionsBuilder + .call(context) + .categoriesOptions + .categoriesBuilder + ?.call(context) + .firstWhereOrNull( + (element) => element.key == post?.category, + ); var timelinePostWidget = TimelinePostScreen( userId: config.getUserId?.call(context) ?? config.userId, @@ -155,8 +164,8 @@ List getTimelineStoryRoutes({ return buildScreenWithoutTransition( context: context, state: state, - child: config.postViewOpenPageBuilder - ?.call(context, timelinePostWidget, backButton) ?? + child: config.postViewOpenPageBuilder?.call( + context, timelinePostWidget, backButton, post, category) ?? Scaffold( appBar: AppBar( leading: backButton, diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index b8f6d00..802876f 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -135,6 +135,13 @@ Widget _postDetailScreenRoute({ onUserTap: (user) => config.onUserTap?.call(context, user), ); + var category = config + .optionsBuilder(context) + .categoriesOptions + .categoriesBuilder + ?.call(context) + .firstWhere((element) => element.key == post.category); + var backButton = IconButton( color: Colors.white, icon: const Icon(Icons.arrow_back_ios), @@ -142,7 +149,7 @@ Widget _postDetailScreenRoute({ ); return config.postViewOpenPageBuilder - ?.call(context, timelinePostScreen, backButton) ?? + ?.call(context, timelinePostScreen, backButton, post, category) ?? Scaffold( appBar: AppBar( leading: backButton, diff --git a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart index 81938be..dffadeb 100644 --- a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart +++ b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart @@ -108,6 +108,8 @@ class TimelineUserStoryConfiguration { BuildContext context, Widget child, IconButton? button, + TimelinePost post, + TimelineCategory? category, )? postViewOpenPageBuilder; /// Open page builder function for the post overview page. This function diff --git a/packages/flutter_timeline/pubspec.yaml b/packages/flutter_timeline/pubspec.yaml index ecb9108..46c77ba 100644 --- a/packages/flutter_timeline/pubspec.yaml +++ b/packages/flutter_timeline/pubspec.yaml @@ -15,6 +15,8 @@ dependencies: sdk: flutter go_router: any + collection: any + flutter_timeline_view: git: url: https://github.com/Iconica-Development/flutter_timeline From 035f795130b5ffb4c6bde8fe4f8bea8e2cc96296 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Tue, 21 May 2024 08:39:34 +0200 Subject: [PATCH 09/24] feat: add a refreshindicator to the timeline with an extra callback onRefresh --- CHANGELOG.md | 1 + .../flutter_timeline_gorouter_userstory.dart | 1 + .../flutter_timeline_navigator_userstory.dart | 1 + .../src/models/timeline_configuration.dart | 4 + .../lib/src/screens/timeline_screen.dart | 133 ++++++++++-------- 5 files changed, 80 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d16c78c..b413cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 0b6da31..88a3927 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -33,6 +33,7 @@ List 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 => diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index 802876f..054cf4a 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -60,6 +60,7 @@ Widget _timelineScreenRoute({ ), ), ), + onRefresh: config.onRefresh, filterEnabled: config.filterEnabled, postWidgetBuilder: config.postWidgetBuilder, ); diff --git a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart index dffadeb..3d35d6c 100644 --- a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart +++ b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart @@ -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; diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index a9d7575..cdebf17 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -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 { 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, + ), + ), + ), + ], + ), ), ), ), From bca15b63073f917889bbdb163648f8b7b8fac112 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Tue, 21 May 2024 14:16:18 +0200 Subject: [PATCH 10/24] feat: use adaptive elements --- CHANGELOG.md | 1 + .../lib/src/flutter_timeline_gorouter_userstory.dart | 7 ++++++- .../lib/src/screens/timeline_post_screen.dart | 4 ++-- .../lib/src/screens/timeline_screen.dart | 4 ++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b413cb1..c04412a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - 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 +- Use the adaptive variants of the material elements in the timeline ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 88a3927..44140bf 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -166,7 +166,12 @@ List getTimelineStoryRoutes({ context: context, state: state, child: config.postViewOpenPageBuilder?.call( - context, timelinePostWidget, backButton, post, category) ?? + context, + timelinePostWidget, + backButton, + post, + category, + ) ?? Scaffold( appBar: AppBar( leading: backButton, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 6b3c004..24cd9b0 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -146,7 +146,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { if (isLoading) { const Center( - child: CircularProgressIndicator(), + child: CircularProgressIndicator.adaptive(), ); } if (this.post == null) { @@ -166,7 +166,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { return Stack( children: [ - RefreshIndicator( + RefreshIndicator.adaptive( onRefresh: () async { updatePost( await widget.service.postService.fetchPostDetails( diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index cdebf17..b3bf895 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -108,7 +108,7 @@ class _TimelineScreenState extends State { @override Widget build(BuildContext context) { if (isLoading && widget.posts == null) { - return const Center(child: CircularProgressIndicator()); + return const Center(child: CircularProgressIndicator.adaptive()); } // Build the list of posts @@ -222,7 +222,7 @@ class _TimelineScreenState extends State { height: 12, ), Expanded( - child: RefreshIndicator( + child: RefreshIndicator.adaptive( onRefresh: () async { await widget.onRefresh?.call(context, category); await loadPosts(); From ad5390fff84df992fd8eb8095b75186e80e65e1d Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Wed, 22 May 2024 11:25:49 +0200 Subject: [PATCH 11/24] feat: change blue color to the primary color as default --- CHANGELOG.md | 1 + .../flutter_timeline_gorouter_userstory.dart | 22 +++++++++--------- .../flutter_timeline_navigator_userstory.dart | 23 +++++++++---------- .../lib/src/config/timeline_theme.dart | 5 ++++ .../timeline_post_creation_screen.dart | 11 +++++---- .../timeline_post_overview_screen.dart | 9 ++++---- .../lib/src/screens/timeline_post_screen.dart | 18 +++++++-------- .../lib/src/screens/timeline_screen.dart | 2 ++ .../screens/timeline_selection_screen.dart | 4 +++- .../lib/src/widgets/category_selector.dart | 4 ++-- .../src/widgets/category_selector_button.dart | 10 ++++---- 11 files changed, 58 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c04412a..ac0409e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - 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 - Use the adaptive variants of the material elements in the timeline +- Change the default blue color to the primary color of the Theme.of(context) in the timeline ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 44140bf..d3247ce 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -50,7 +50,7 @@ List getTimelineStoryRoutes({ .optionsBuilder(context) .theme .postCreationFloatingActionButtonColor ?? - const Color(0xff71C6D1), + Theme.of(context).primaryColor, onPressed: () async => context.push( TimelineUserStoryRoutes.timelineCategorySelection, ), @@ -75,8 +75,8 @@ List getTimelineStoryRoutes({ .optionsBuilder(context) .translations .timeLineScreenTitle!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -121,8 +121,8 @@ List getTimelineStoryRoutes({ backgroundColor: const Color(0xff212121), title: Text( config.optionsBuilder(context).translations.postCreation!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -178,8 +178,8 @@ List getTimelineStoryRoutes({ backgroundColor: const Color(0xff212121), title: Text( post.category ?? 'Category', - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -237,8 +237,8 @@ List getTimelineStoryRoutes({ leading: backButton, title: Text( config.optionsBuilder(context).translations.postCreation!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -285,8 +285,8 @@ List getTimelineStoryRoutes({ backgroundColor: const Color(0xff212121), title: Text( config.optionsBuilder(context).translations.postOverview!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index 054cf4a..93f2348 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -70,7 +70,7 @@ Widget _timelineScreenRoute({ .optionsBuilder(context) .theme .postCreationFloatingActionButtonColor ?? - const Color(0xff71C6D1), + Theme.of(context).primaryColor, onPressed: () async => Navigator.of(context).push( MaterialPageRoute( builder: (context) => _postCategorySelectionScreen( @@ -93,8 +93,8 @@ Widget _timelineScreenRoute({ backgroundColor: const Color(0xff212121), title: Text( config.optionsBuilder(context).translations.timeLineScreenTitle!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -157,8 +157,8 @@ Widget _postDetailScreenRoute({ backgroundColor: const Color(0xff212121), title: Text( post.category ?? 'Category', - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -247,8 +247,8 @@ Widget _postCreationScreenRoute({ leading: backButton, title: Text( config.optionsBuilder(context).translations.postCreation!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -294,7 +294,6 @@ Widget _postOverviewScreenRoute({ ); } }, - isOverviewScreen: true, ); var backButton = IconButton( @@ -315,8 +314,8 @@ Widget _postOverviewScreenRoute({ backgroundColor: const Color(0xff212121), title: Text( config.optionsBuilder(context).translations.postOverview!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), @@ -374,8 +373,8 @@ Widget _postCategorySelectionScreen({ backgroundColor: const Color(0xff212121), title: Text( config.optionsBuilder(context).translations.postCreation!, - style: const TextStyle( - color: Color(0xff71C6D1), + style: TextStyle( + color: Theme.of(context).primaryColor, fontSize: 24, fontWeight: FontWeight.w800, ), diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart index c5af788..3462b2b 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart @@ -16,6 +16,7 @@ class TimelineTheme { this.moreIcon, this.deleteIcon, this.categorySelectionBorderColor, + this.categorySelectionButtonBackgroundColor, this.postCreationFloatingActionButtonColor, this.textStyles = const TimelineTextStyles(), }); @@ -46,6 +47,10 @@ class TimelineTheme { /// The color of the border around the category in the selection screen final Color? categorySelectionBorderColor; + /// The color of the background of the category selection button in the + /// selection screen + final Color? categorySelectionButtonBackgroundColor; + /// The color of the floating action button on the overview screen final Color? postCreationFloatingActionButtonColor; } diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart index b71a6d9..b212b49 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart @@ -285,7 +285,7 @@ class _TimelinePostCreationScreenState mainAxisAlignment: MainAxisAlignment.start, children: [ Checkbox( - activeColor: const Color(0xff71C6D1), + activeColor: theme.colorScheme.primary, value: allowComments, onChanged: (value) { setState(() { @@ -295,7 +295,7 @@ class _TimelinePostCreationScreenState ), Text(widget.options.translations.yes!), Checkbox( - activeColor: const Color(0xff71C6D1), + activeColor: theme.colorScheme.primary, value: !allowComments, onChanged: (value) { setState(() { @@ -318,9 +318,10 @@ class _TimelinePostCreationScreenState enabled: editingDone, ) : ElevatedButton( - style: const ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Color(0xff71C6D1)), + style: ButtonStyle( + backgroundColor: MaterialStatePropertyAll( + theme.colorScheme.primary, + ), ), onPressed: editingDone ? () async { diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart index 3d7e949..8f8d0dc 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart @@ -10,14 +10,12 @@ class TimelinePostOverviewScreen extends StatelessWidget { required this.options, required this.service, required this.onPostSubmit, - this.isOverviewScreen, super.key, }); final TimelinePost timelinePost; final TimelineOptions options; final TimelineService service; final void Function(TimelinePost) onPostSubmit; - final bool? isOverviewScreen; @override Widget build(BuildContext context) { @@ -30,7 +28,7 @@ class TimelinePostOverviewScreen extends StatelessWidget { post: timelinePost, onPostDelete: () async {}, service: service, - isOverviewScreen: isOverviewScreen, + isOverviewScreen: true, ), ), options.postOverviewButtonBuilder?.call( @@ -43,8 +41,9 @@ class TimelinePostOverviewScreen extends StatelessWidget { Padding( padding: const EdgeInsets.only(bottom: 30.0), child: ElevatedButton( - style: const ButtonStyle( - backgroundColor: MaterialStatePropertyAll(Color(0xff71C6D1)), + style: ButtonStyle( + backgroundColor: + MaterialStatePropertyAll(Theme.of(context).primaryColor), ), onPressed: () { onPostSubmit(timelinePost); diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 24cd9b0..a9e5d77 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -47,16 +47,14 @@ class TimelinePostScreen extends StatelessWidget { final bool? isOverviewScreen; @override - Widget build(BuildContext context) => Scaffold( - body: _TimelinePostScreen( - userId: userId, - service: service, - options: options, - post: post, - onPostDelete: onPostDelete, - onUserTap: onUserTap, - isOverviewScreen: isOverviewScreen, - ), + Widget build(BuildContext context) => _TimelinePostScreen( + userId: userId, + service: service, + options: options, + post: post, + onPostDelete: onPostDelete, + onUserTap: onUserTap, + isOverviewScreen: isOverviewScreen, ); } diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index b3bf895..38f7603 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -152,6 +152,8 @@ class _TimelineScreenState extends State { if (widget.filterEnabled) ...[ Padding( padding: EdgeInsets.symmetric( + // left: widget.options.padding.left, + // right: widget.options.padding.right, horizontal: widget.options.padding.horizontal, ), child: Row( diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart index 2b32668..f0b369e 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart @@ -56,9 +56,11 @@ class TimelineSelectionScreen extends StatelessWidget { borderRadius: BorderRadius.circular(10), border: Border.all( color: options.theme.categorySelectionBorderColor ?? - const Color(0xff71C6D1), + Theme.of(context).primaryColor, width: 2, ), + color: + options.theme.categorySelectionButtonBackgroundColor, ), margin: const EdgeInsets.symmetric(vertical: 4), child: Column( diff --git a/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart b/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart index 9953883..f62c482 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart @@ -37,7 +37,7 @@ class _CategorySelectorState extends State { SizedBox( width: widget.options.categoriesOptions .categorySelectorHorizontalPadding ?? - max(widget.options.padding.horizontal - 20, 0), + max(widget.options.padding.left - 20, 0), ), for (var category in categories) ...[ widget.options.categoriesOptions.categoryButtonBuilder?.call( @@ -61,7 +61,7 @@ class _CategorySelectorState extends State { SizedBox( width: widget.options.categoriesOptions .categorySelectorHorizontalPadding ?? - max(widget.options.padding.horizontal - 4, 0), + max(widget.options.padding.right - 4, 0), ), ], ), diff --git a/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart b/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart index d05c33d..0d19566 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart @@ -14,7 +14,7 @@ class CategorySelectorButton extends StatelessWidget { final TimelineCategory category; final bool selected; - final void Function() onTap; + final VoidCallback onTap; final TimelineOptions options; final bool isOnTop; @@ -36,15 +36,15 @@ class CategorySelectorButton extends StatelessWidget { ), fixedSize: MaterialStatePropertyAll(Size(140, isOnTop ? 140 : 20)), backgroundColor: MaterialStatePropertyAll( - selected ? const Color(0xff71C6D1) : Colors.transparent, + selected ? theme.colorScheme.primary : Colors.transparent, ), - shape: const MaterialStatePropertyAll( + shape: MaterialStatePropertyAll( RoundedRectangleBorder( - borderRadius: BorderRadius.all( + borderRadius: const BorderRadius.all( Radius.circular(8), ), side: BorderSide( - color: Color(0xff71C6D1), + color: theme.colorScheme.primary, width: 2, ), ), From ab0f96121e2c6cc4606482eefc184f780c3b640c Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Wed, 22 May 2024 11:43:55 +0200 Subject: [PATCH 12/24] feat: make all translations required for TimelineTranslations and provide an .empty() alternative --- CHANGELOG.md | 1 + .../flutter_timeline_gorouter_userstory.dart | 8 +- .../flutter_timeline_navigator_userstory.dart | 8 +- .../lib/src/config/timeline_options.dart | 2 +- .../lib/src/config/timeline_translations.dart | 105 ++++++++++++------ .../timeline_post_creation_screen.dart | 24 ++-- .../lib/src/screens/timeline_post_screen.dart | 14 +-- .../lib/src/screens/timeline_screen.dart | 4 +- .../screens/timeline_selection_screen.dart | 2 +- .../lib/src/widgets/reaction_bottom.dart | 2 +- .../lib/src/widgets/timeline_post_widget.dart | 6 +- 11 files changed, 110 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac0409e..8494fed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Add a refresh functionality to the timeline with a pull to refresh callback to allow additional functionality when refreshing the timeline - Use the adaptive variants of the material elements in the timeline - Change the default blue color to the primary color of the Theme.of(context) in the timeline +- Change the TimelineTranslations constructor to require all translations or use the TimelineTranslations.empty constructor if you don't want to specify all translations ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index d3247ce..8026163 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -74,7 +74,7 @@ List getTimelineStoryRoutes({ config .optionsBuilder(context) .translations - .timeLineScreenTitle!, + .timeLineScreenTitle, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, @@ -120,7 +120,7 @@ List getTimelineStoryRoutes({ leading: backButton, backgroundColor: const Color(0xff212121), title: Text( - config.optionsBuilder(context).translations.postCreation!, + config.optionsBuilder(context).translations.postCreation, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, @@ -236,7 +236,7 @@ List getTimelineStoryRoutes({ backgroundColor: const Color(0xff212121), leading: backButton, title: Text( - config.optionsBuilder(context).translations.postCreation!, + config.optionsBuilder(context).translations.postCreation, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, @@ -284,7 +284,7 @@ List getTimelineStoryRoutes({ leading: backButton, backgroundColor: const Color(0xff212121), title: Text( - config.optionsBuilder(context).translations.postOverview!, + config.optionsBuilder(context).translations.postOverview, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index 93f2348..48288e6 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -92,7 +92,7 @@ Widget _timelineScreenRoute({ appBar: AppBar( backgroundColor: const Color(0xff212121), title: Text( - config.optionsBuilder(context).translations.timeLineScreenTitle!, + config.optionsBuilder(context).translations.timeLineScreenTitle, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, @@ -246,7 +246,7 @@ Widget _postCreationScreenRoute({ backgroundColor: const Color(0xff212121), leading: backButton, title: Text( - config.optionsBuilder(context).translations.postCreation!, + config.optionsBuilder(context).translations.postCreation, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, @@ -313,7 +313,7 @@ Widget _postOverviewScreenRoute({ leading: backButton, backgroundColor: const Color(0xff212121), title: Text( - config.optionsBuilder(context).translations.postOverview!, + config.optionsBuilder(context).translations.postOverview, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, @@ -372,7 +372,7 @@ Widget _postCategorySelectionScreen({ leading: backButton, backgroundColor: const Color(0xff212121), title: Text( - config.optionsBuilder(context).translations.postCreation!, + config.optionsBuilder(context).translations.postCreation, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart index b3c04d4..dc20e32 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart @@ -12,7 +12,7 @@ import 'package:intl/intl.dart'; class TimelineOptions { const TimelineOptions({ this.theme = const TimelineTheme(), - this.translations = const TimelineTranslations(), + this.translations = const TimelineTranslations.empty(), this.imagePickerConfig = const ImagePickerConfig(), this.imagePickerTheme = const ImagePickerTheme(), this.timelinePostHeight, diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart b/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart index 61f2d27..dca146d 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart @@ -5,8 +5,50 @@ import 'package:flutter/material.dart'; @immutable + +/// Class that holds all the translations for the timeline component view and +/// the corresponding userstory class TimelineTranslations { + /// TimelineTranslations constructor where everything is required use this + /// if you want to be sure to have all translations specified + /// If you just want the default values use the empty constructor + /// and optionally override the values with the copyWith method const TimelineTranslations({ + required this.anonymousUser, + required this.noPosts, + required this.noPostsWithFilter, + required this.title, + required this.titleHintText, + required this.content, + required this.contentHintText, + required this.contentDescription, + required this.uploadImage, + required this.uploadImageDescription, + required this.allowComments, + required this.allowCommentsDescription, + required this.commentsTitleOnPost, + required this.checkPost, + required this.postAt, + required this.deletePost, + required this.deleteReaction, + required this.viewPost, + required this.likesTitle, + required this.commentsTitle, + required this.firstComment, + required this.writeComment, + required this.postLoadingError, + required this.timelineSelectionDescription, + required this.searchHint, + required this.postOverview, + required this.postIn, + required this.postCreation, + required this.yes, + required this.no, + required this.timeLineScreenTitle, + }); + + /// Default translations for the timeline component view + const TimelineTranslations.empty({ this.anonymousUser = 'Anonymous user', this.noPosts = 'No posts yet', this.noPostsWithFilter = 'No posts with this filter', @@ -41,45 +83,46 @@ class TimelineTranslations { this.timeLineScreenTitle = 'iconinstagram', }); - final String? noPosts; - final String? noPostsWithFilter; - final String? anonymousUser; + final String noPosts; + final String noPostsWithFilter; + final String anonymousUser; - final String? title; - final String? content; - final String? contentDescription; - final String? uploadImage; - final String? uploadImageDescription; - final String? allowComments; - final String? allowCommentsDescription; - final String? checkPost; - final String? postAt; + final String title; + final String content; + final String contentDescription; + final String uploadImage; + final String uploadImageDescription; + final String allowComments; + final String allowCommentsDescription; + final String checkPost; + final String postAt; - final String? titleHintText; - final String? contentHintText; + final String titleHintText; + final String contentHintText; - final String? deletePost; - final String? deleteReaction; - final String? viewPost; - final String? likesTitle; - final String? commentsTitle; - final String? commentsTitleOnPost; - final String? writeComment; - final String? firstComment; - final String? postLoadingError; + final String deletePost; + final String deleteReaction; + final String viewPost; + final String likesTitle; + final String commentsTitle; + final String commentsTitleOnPost; + final String writeComment; + final String firstComment; + final String postLoadingError; - final String? timelineSelectionDescription; + final String timelineSelectionDescription; - final String? searchHint; + final String searchHint; - final String? postOverview; - final String? postIn; - final String? postCreation; + final String postOverview; + final String postIn; + final String postCreation; - final String? yes; - final String? no; - final String? timeLineScreenTitle; + final String yes; + final String no; + final String timeLineScreenTitle; + /// Method to override the default values of the translations TimelineTranslations copyWith({ String? noPosts, String? noPostsWithFilter, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart index b212b49..ff08b94 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart @@ -128,7 +128,7 @@ class _TimelinePostCreationScreenState crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - widget.options.translations.title!, + widget.options.translations.title, style: const TextStyle( fontWeight: FontWeight.w800, fontSize: 20, @@ -149,7 +149,7 @@ class _TimelinePostCreationScreenState ), const SizedBox(height: 16), Text( - widget.options.translations.content!, + widget.options.translations.content, style: const TextStyle( fontWeight: FontWeight.w800, fontSize: 20, @@ -157,7 +157,7 @@ class _TimelinePostCreationScreenState ), const SizedBox(height: 4), Text( - widget.options.translations.contentDescription!, + widget.options.translations.contentDescription, style: theme.textTheme.bodyMedium, ), // input field for the content @@ -177,14 +177,14 @@ class _TimelinePostCreationScreenState ), // input field for the content Text( - widget.options.translations.uploadImage!, + widget.options.translations.uploadImage, style: const TextStyle( fontWeight: FontWeight.w800, fontSize: 20, ), ), Text( - widget.options.translations.uploadImageDescription!, + widget.options.translations.uploadImageDescription, style: theme.textTheme.bodyMedium, ), // image picker field @@ -271,14 +271,14 @@ class _TimelinePostCreationScreenState const SizedBox(height: 16), Text( - widget.options.translations.commentsTitle!, + widget.options.translations.commentsTitle, style: const TextStyle( fontWeight: FontWeight.w800, fontSize: 20, ), ), Text( - widget.options.translations.allowCommentsDescription!, + widget.options.translations.allowCommentsDescription, style: theme.textTheme.bodyMedium, ), Row( @@ -293,7 +293,7 @@ class _TimelinePostCreationScreenState }); }, ), - Text(widget.options.translations.yes!), + Text(widget.options.translations.yes), Checkbox( activeColor: theme.colorScheme.primary, value: !allowComments, @@ -303,7 +303,7 @@ class _TimelinePostCreationScreenState }); }, ), - Text(widget.options.translations.no!), + Text(widget.options.translations.no), ], ), const SizedBox(height: 120), @@ -314,7 +314,7 @@ class _TimelinePostCreationScreenState ? widget.options.buttonBuilder!( context, onPostCreated, - widget.options.translations.checkPost!, + widget.options.translations.checkPost, enabled: editingDone, ) : ElevatedButton( @@ -334,8 +334,8 @@ class _TimelinePostCreationScreenState padding: const EdgeInsets.all(12.0), child: Text( widget.enablePostOverviewScreen - ? widget.options.translations.checkPost! - : widget.options.translations.postCreation!, + ? widget.options.translations.checkPost + : widget.options.translations.postCreation, style: const TextStyle( color: Colors.white, fontSize: 20, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index a9e5d77..970949e 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -150,7 +150,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { if (this.post == null) { return Center( child: Text( - widget.options.translations.postLoadingError!, + widget.options.translations.postLoadingError, style: widget.options.theme.textStyles.errorTextStyle, ), ); @@ -219,7 +219,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { widget.options.nameBuilder ?.call(post.creator) ?? post.creator?.fullName ?? - widget.options.translations.anonymousUser!, + widget.options.translations.anonymousUser, style: widget.options.theme.textStyles .postCreatorTitleStyle ?? theme.textTheme.titleMedium, @@ -239,7 +239,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { child: Row( children: [ Text( - widget.options.translations.deletePost!, + widget.options.translations.deletePost, style: widget.options.theme.textStyles .deletePostStyle ?? theme.textTheme.bodyMedium, @@ -439,7 +439,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { const SizedBox(height: 20), if (post.reactionEnabled) ...[ Text( - widget.options.translations.commentsTitleOnPost!, + widget.options.translations.commentsTitleOnPost, style: theme.textTheme.titleMedium, ), for (var reaction @@ -467,7 +467,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { PopupMenuItem( value: 'delete', child: Text( - widget.options.translations.deleteReaction!, + widget.options.translations.deleteReaction, ), ), ], @@ -521,7 +521,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { ?.call(post.creator) ?? reaction.creator?.fullName ?? widget.options.translations - .anonymousUser!, + .anonymousUser, style: theme.textTheme.titleSmall, ), Padding( @@ -563,7 +563,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { if (post.reactions?.isEmpty ?? true) ...[ const SizedBox(height: 16), Text( - widget.options.translations.firstComment!, + widget.options.translations.firstComment, ), ], const SizedBox(height: 120), diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index 38f7603..bb0855f 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -289,9 +289,9 @@ class _TimelineScreenState extends State { padding: const EdgeInsets.all(8.0), child: Text( category == null - ? widget.options.translations.noPosts! + ? widget.options.translations.noPosts : widget - .options.translations.noPostsWithFilter!, + .options.translations.noPostsWithFilter, style: widget.options.theme.textStyles.noPostsStyle, ), diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart index f0b369e..39f3317 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart @@ -29,7 +29,7 @@ class TimelineSelectionScreen extends StatelessWidget { Padding( padding: EdgeInsets.only(top: size.height * 0.05, bottom: 8), child: Text( - options.translations.timelineSelectionDescription!, + options.translations.timelineSelectionDescription, style: const TextStyle( fontWeight: FontWeight.w800, fontSize: 20, diff --git a/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart b/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart index 8a62f7c..2118f60 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart @@ -65,7 +65,7 @@ class _ReactionBottomState extends State { ], ), ), - widget.translations.writeComment!, + widget.translations.writeComment, ), ), ), diff --git a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart index abbb8a4..0989380 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart @@ -94,7 +94,7 @@ class _TimelinePostWidgetState extends State { widget.options.nameBuilder ?.call(widget.post.creator) ?? widget.post.creator?.fullName ?? - widget.options.translations.anonymousUser!, + widget.options.translations.anonymousUser, style: widget.options.theme.textStyles .postCreatorTitleStyle ?? theme.textTheme.titleMedium, @@ -118,7 +118,7 @@ class _TimelinePostWidgetState extends State { child: Row( children: [ Text( - widget.options.translations.deletePost!, + widget.options.translations.deletePost, style: widget.options.theme.textStyles .deletePostStyle ?? theme.textTheme.bodyMedium, @@ -318,7 +318,7 @@ class _TimelinePostWidgetState extends State { ), const SizedBox(height: 4), Text( - widget.options.translations.viewPost!, + widget.options.translations.viewPost, style: widget.options.theme.textStyles.viewPostStyle ?? theme.textTheme.bodySmall, ), From 5f6bb26404ab825fb4003a3ddad19e13734d1899 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Wed, 22 May 2024 13:51:33 +0200 Subject: [PATCH 13/24] feat: add TimelinePaddingOptions for all the paddings --- CHANGELOG.md | 1 + .../example/lib/config/config.dart | 4 +- .../lib/flutter_timeline_view.dart | 1 + .../lib/src/config/timeline_options.dart | 16 +++--- .../lib/src/config/timeline_paddings.dart | 21 ++++++++ .../timeline_post_creation_screen.dart | 2 +- .../timeline_post_overview_screen.dart | 50 +++++++++++-------- .../lib/src/screens/timeline_post_screen.dart | 2 +- .../lib/src/screens/timeline_screen.dart | 8 +-- .../lib/src/widgets/category_selector.dart | 4 +- 10 files changed, 69 insertions(+), 40 deletions(-) create mode 100644 packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 8494fed..bdfc69c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Use the adaptive variants of the material elements in the timeline - Change the default blue color to the primary color of the Theme.of(context) in the timeline - Change the TimelineTranslations constructor to require all translations or use the TimelineTranslations.empty constructor if you don't want to specify all translations +- Add a TimelinePaddingOptions class to store the padding options for the timeline ## 3.0.1 diff --git a/packages/flutter_timeline/example/lib/config/config.dart b/packages/flutter_timeline/example/lib/config/config.dart index 5eede94..ff46d66 100644 --- a/packages/flutter_timeline/example/lib/config/config.dart +++ b/packages/flutter_timeline/example/lib/config/config.dart @@ -12,7 +12,9 @@ TimelineUserStoryConfiguration getConfig(TimelineService service) { var options = TimelineOptions( textInputBuilder: null, - padding: const EdgeInsets.all(20).copyWith(top: 28), + paddings: TimelinePaddingOptions( + mainPadding: const EdgeInsets.all(20).copyWith(top: 28), + ), allowAllDeletion: true, categoriesOptions: CategoriesOptions( categoriesBuilder: (context) => [ diff --git a/packages/flutter_timeline_view/lib/flutter_timeline_view.dart b/packages/flutter_timeline_view/lib/flutter_timeline_view.dart index 1a63bf4..89264d4 100644 --- a/packages/flutter_timeline_view/lib/flutter_timeline_view.dart +++ b/packages/flutter_timeline_view/lib/flutter_timeline_view.dart @@ -5,6 +5,7 @@ library flutter_timeline_view; export 'src/config/timeline_options.dart'; +export 'src/config/timeline_paddings.dart'; export 'src/config/timeline_styles.dart'; export 'src/config/timeline_theme.dart'; export 'src/config/timeline_translations.dart'; diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart index dc20e32..592bf65 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.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_paddings.dart'; import 'package:flutter_timeline_view/src/config/timeline_theme.dart'; import 'package:flutter_timeline_view/src/config/timeline_translations.dart'; import 'package:intl/intl.dart'; @@ -13,6 +14,7 @@ class TimelineOptions { const TimelineOptions({ this.theme = const TimelineTheme(), this.translations = const TimelineTranslations.empty(), + this.paddings = const TimelinePaddingOptions(), this.imagePickerConfig = const ImagePickerConfig(), this.imagePickerTheme = const ImagePickerTheme(), this.timelinePostHeight, @@ -37,12 +39,8 @@ class TimelineOptions { this.userAvatarBuilder, this.anonymousAvatarBuilder, this.nameBuilder, - this.padding = - const EdgeInsets.only(left: 12.0, top: 24.0, right: 12.0, bottom: 12.0), this.iconSize = 26, this.postWidgetHeight, - this.postPadding = - const EdgeInsets.symmetric(vertical: 12.0, horizontal: 12.0), this.filterOptions = const FilterOptions(), this.categoriesOptions = const CategoriesOptions(), this.requireImageForPost = false, @@ -79,8 +77,12 @@ class TimelineOptions { /// The height of a post in the timeline final double? timelinePostHeight; + /// Class that contains all the translations used in the timeline final TimelineTranslations translations; + /// Class that contains all the paddings used in the timeline + final TimelinePaddingOptions paddings; + final ButtonBuilder? buttonBuilder; final TextInputBuilder? textInputBuilder; @@ -116,18 +118,12 @@ class TimelineOptions { /// The builder for the divider final Widget Function()? dividerBuilder; - /// The padding between posts in the timeline - final EdgeInsets padding; - /// Size of icons like the comment and like icons. Dafualts to 26 final double iconSize; /// Sets a predefined height for the postWidget. final double? postWidgetHeight; - /// Padding of each post - final EdgeInsets postPadding; - /// Options for filtering final FilterOptions filterOptions; diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart b/packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart new file mode 100644 index 0000000..193a0a9 --- /dev/null +++ b/packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +/// This class contains the paddings used in the timeline options +class TimelinePaddingOptions { + const TimelinePaddingOptions({ + this.mainPadding = + const EdgeInsets.only(left: 12.0, top: 24.0, right: 12.0, bottom: 12.0), + this.postPadding = + const EdgeInsets.symmetric(vertical: 12.0, horizontal: 12.0), + this.postOverviewButtonBottomPadding = 30.0, + }); + + /// The padding between posts in the timeline + final EdgeInsets mainPadding; + + /// The padding of each post + final EdgeInsets postPadding; + + /// The bottom padding of the button on the post overview screen + final double postOverviewButtonBottomPadding; +} diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart index ff08b94..cd36ae3 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart @@ -121,7 +121,7 @@ class _TimelinePostCreationScreenState return GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: Padding( - padding: widget.options.padding, + padding: widget.options.paddings.mainPadding, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.max, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart index 8f8d0dc..e810fe1 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart @@ -19,9 +19,11 @@ class TimelinePostOverviewScreen extends StatelessWidget { @override Widget build(BuildContext context) { + var buttonText = '${options.translations.postIn} ${timelinePost.category}'; return Column( + mainAxisSize: MainAxisSize.max, children: [ - Flexible( + Expanded( child: TimelinePostScreen( userId: timelinePost.creatorId, options: options, @@ -36,31 +38,37 @@ class TimelinePostOverviewScreen extends StatelessWidget { () { onPostSubmit(timelinePost); }, - '${options.translations.postIn} ${timelinePost.category}', + buttonText, ) ?? - Padding( - padding: const EdgeInsets.only(bottom: 30.0), - child: ElevatedButton( - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Theme.of(context).primaryColor), - ), - onPressed: () { - onPostSubmit(timelinePost); - }, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Text( - '${options.translations.postIn} ${timelinePost.category}', - style: const TextStyle( - color: Colors.white, - fontSize: 20, - fontWeight: FontWeight.w800, - ), + options.buttonBuilder?.call( + context, + () { + onPostSubmit(timelinePost); + }, + buttonText, + enabled: true, + ) ?? + ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStatePropertyAll(Theme.of(context).primaryColor), + ), + onPressed: () { + onPostSubmit(timelinePost); + }, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Text( + buttonText, + style: const TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w800, ), ), ), ), + SizedBox(height: options.paddings.postOverviewButtonBottomPadding), ], ); } diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 970949e..0cd94a1 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -176,7 +176,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { }, child: SingleChildScrollView( child: Padding( - padding: widget.options.padding, + padding: widget.options.paddings.mainPadding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index bb0855f..1b75303 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -147,14 +147,14 @@ class _TimelineScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( - height: widget.options.padding.top, + height: widget.options.paddings.mainPadding.top, ), if (widget.filterEnabled) ...[ Padding( padding: EdgeInsets.symmetric( // left: widget.options.padding.left, // right: widget.options.padding.right, - horizontal: widget.options.padding.horizontal, + horizontal: widget.options.paddings.mainPadding.horizontal, ), child: Row( crossAxisAlignment: CrossAxisAlignment.end, @@ -240,7 +240,7 @@ class _TimelineScreenState extends State { const SizedBox.shrink(), ...posts.map( (post) => Padding( - padding: widget.options.postPadding, + padding: widget.options.paddings.postPadding, child: widget.postWidgetBuilder?.call(post) ?? TimelinePostWidget( service: service, @@ -303,7 +303,7 @@ class _TimelineScreenState extends State { ), ), SizedBox( - height: widget.options.padding.bottom, + height: widget.options.paddings.mainPadding.bottom, ), ], ); diff --git a/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart b/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart index f62c482..2e69922 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart @@ -37,7 +37,7 @@ class _CategorySelectorState extends State { SizedBox( width: widget.options.categoriesOptions .categorySelectorHorizontalPadding ?? - max(widget.options.padding.left - 20, 0), + max(widget.options.paddings.mainPadding.left - 20, 0), ), for (var category in categories) ...[ widget.options.categoriesOptions.categoryButtonBuilder?.call( @@ -61,7 +61,7 @@ class _CategorySelectorState extends State { SizedBox( width: widget.options.categoriesOptions .categorySelectorHorizontalPadding ?? - max(widget.options.padding.right - 4, 0), + max(widget.options.paddings.mainPadding.right - 4, 0), ), ], ), From 504a7de3433dd980ddd19922194ca264f41362f4 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Wed, 22 May 2024 14:58:27 +0200 Subject: [PATCH 14/24] fix: the avatar size to match the new design --- CHANGELOG.md | 1 + .../lib/src/screens/timeline_post_screen.dart | 8 ++++---- .../lib/src/widgets/timeline_post_widget.dart | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdfc69c..bc8f158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Change the default blue color to the primary color of the Theme.of(context) in the timeline - Change the TimelineTranslations constructor to require all translations or use the TimelineTranslations.empty constructor if you don't want to specify all translations - Add a TimelinePaddingOptions class to store the padding options for the timeline +- fix the avatar size to match the new design ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 0cd94a1..41d8a27 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -196,7 +196,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { 28, ) ?? CircleAvatar( - radius: 20, + radius: 14, backgroundImage: CachedNetworkImageProvider( post.creator!.imageUrl!, @@ -208,7 +208,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { 28, ) ?? const CircleAvatar( - radius: 20, + radius: 14, child: Icon( Icons.person, ), @@ -493,7 +493,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { 28, ) ?? CircleAvatar( - radius: 20, + radius: 14, backgroundImage: CachedNetworkImageProvider( reaction.creator!.imageUrl!, ), @@ -504,7 +504,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { 28, ) ?? const CircleAvatar( - radius: 20, + radius: 14, child: Icon( Icons.person, ), diff --git a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart index 0989380..8e45197 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart @@ -72,7 +72,7 @@ class _TimelinePostWidgetState extends State { 28, ) ?? CircleAvatar( - radius: 20, + radius: 14, backgroundImage: CachedNetworkImageProvider( widget.post.creator!.imageUrl!, ), @@ -80,10 +80,10 @@ class _TimelinePostWidgetState extends State { ] else ...[ widget.options.anonymousAvatarBuilder?.call( widget.post.creator!, - 40, + 28, ) ?? const CircleAvatar( - radius: 20, + radius: 14, child: Icon( Icons.person, ), From 767215a53e0e793a0f8cb1fc1ac5c99b825bd1eb Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Wed, 22 May 2024 16:51:24 +0200 Subject: [PATCH 15/24] fix: add the iconbutton for image uploading back to the ReactionBottom --- CHANGELOG.md | 1 + .../lib/src/widgets/reaction_bottom.dart | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc8f158..81de943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Change the TimelineTranslations constructor to require all translations or use the TimelineTranslations.empty constructor if you don't want to specify all translations - Add a TimelinePaddingOptions class to store the padding options for the timeline - fix the avatar size to match the new design +- Add the iconbutton for image uploading back to the ReactionBottom ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart b/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart index 2118f60..57c9ec1 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/reaction_bottom.dart @@ -49,6 +49,18 @@ class _ReactionBottomState extends State { child: Row( mainAxisSize: MainAxisSize.min, children: [ + if (widget.onPressSelectImage != null) ...[ + IconButton( + onPressed: () async { + _textEditingController.clear(); + widget.onPressSelectImage?.call(); + }, + icon: Icon( + Icons.image, + color: widget.iconColor, + ), + ), + ], IconButton( onPressed: () async { var value = _textEditingController.text; From 25264ba44b54082a99eea054204b683291310eb0 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 23 May 2024 11:12:22 +0200 Subject: [PATCH 16/24] fix: show category title everywhere and use category key for storing posts --- CHANGELOG.md | 1 + .../flutter_timeline_gorouter_userstory.dart | 12 +++-- .../flutter_timeline_navigator_userstory.dart | 53 ++++++++++--------- .../src/config/firebase_timeline_options.dart | 2 - .../timeline_post_overview_screen.dart | 9 +++- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81de943..4a58fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Add a TimelinePaddingOptions class to store the padding options for the timeline - fix the avatar size to match the new design - Add the iconbutton for image uploading back to the ReactionBottom +- Fix category key is correctly used for saving timeline posts and category title is shown everywhere ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 8026163..5a34d81 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -94,12 +94,14 @@ List getTimelineStoryRoutes({ var timelineSelectionScreen = TimelineSelectionScreen( options: config.optionsBuilder(context), categories: config - .optionsBuilder(context) - .categoriesOptions - .categoriesBuilder!(context), + .optionsBuilder(context) + .categoriesOptions + .categoriesBuilder + ?.call(context) ?? + [], onCategorySelected: (category) async { await context.push( - TimelineUserStoryRoutes.timelinepostCreation(category.title), + TimelineUserStoryRoutes.timelinepostCreation(category.key ?? ''), ); }, ); @@ -177,7 +179,7 @@ List getTimelineStoryRoutes({ leading: backButton, backgroundColor: const Color(0xff212121), title: Text( - post.category ?? 'Category', + category?.title ?? post.category ?? 'Category', style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index 48288e6..e25b67c 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -156,7 +156,7 @@ Widget _postDetailScreenRoute({ leading: backButton, backgroundColor: const Color(0xff212121), title: Text( - post.category ?? 'Category', + category?.title ?? post.category ?? 'Category', style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 24, @@ -193,29 +193,28 @@ Widget _postCreationScreenRoute({ service: config.service, onPostCreated: (post) async { var newPost = await config.service.postService.createPost(post); - if (context.mounted) { - if (config.afterPostCreationGoHome) { - await Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => _timelineScreenRoute( - configuration: config, - context: context, - ), + if (!context.mounted) return; + if (config.afterPostCreationGoHome) { + await Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => _timelineScreenRoute( + configuration: config, + context: context, ), - ); - } else { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => _postOverviewScreenRoute( - configuration: config, - context: context, - post: newPost, - ), + ), + ); + } else { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => _postOverviewScreenRoute( + configuration: config, + context: context, + post: newPost, ), - ); - } + ), + ); } }, onPostOverview: (post) async => Navigator.of(context).push( @@ -228,7 +227,7 @@ Widget _postCreationScreenRoute({ ), ), enablePostOverviewScreen: config.enablePostOverviewScreen, - postCategory: category.title, + postCategory: category.key, ); var backButton = IconButton( @@ -341,9 +340,11 @@ Widget _postCategorySelectionScreen({ var timelineSelectionScreen = TimelineSelectionScreen( options: config.optionsBuilder(context), categories: config - .optionsBuilder(context) - .categoriesOptions - .categoriesBuilder!(context), + .optionsBuilder(context) + .categoriesOptions + .categoriesBuilder + ?.call(context) ?? + [], onCategorySelected: (category) async { await Navigator.of(context).push( MaterialPageRoute( diff --git a/packages/flutter_timeline_firebase/lib/src/config/firebase_timeline_options.dart b/packages/flutter_timeline_firebase/lib/src/config/firebase_timeline_options.dart index 6acd32a..0c83cb9 100644 --- a/packages/flutter_timeline_firebase/lib/src/config/firebase_timeline_options.dart +++ b/packages/flutter_timeline_firebase/lib/src/config/firebase_timeline_options.dart @@ -9,10 +9,8 @@ class FirebaseTimelineOptions { const FirebaseTimelineOptions({ this.usersCollectionName = 'users', this.timelineCollectionName = 'timeline', - this.allTimelineCategories = const [], }); final String usersCollectionName; final String timelineCollectionName; - final List allTimelineCategories; } diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart index e810fe1..6293dfc 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_overview_screen.dart @@ -1,5 +1,6 @@ // ignore_for_file: prefer_expression_function_bodies +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_timeline_interface/flutter_timeline_interface.dart'; import 'package:flutter_timeline_view/flutter_timeline_view.dart'; @@ -19,7 +20,13 @@ class TimelinePostOverviewScreen extends StatelessWidget { @override Widget build(BuildContext context) { - var buttonText = '${options.translations.postIn} ${timelinePost.category}'; + // the timelinePost.category is a key so we need to get the category object + var timelineCategoryName = options.categoriesOptions.categoriesBuilder + ?.call(context) + .firstWhereOrNull((element) => element.key == timelinePost.category) + ?.title ?? + timelinePost.category; + var buttonText = '${options.translations.postIn} $timelineCategoryName'; return Column( mainAxisSize: MainAxisSize.max, children: [ From 013e82e61db5278d92666ba535eab7cafe1da284 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 23 May 2024 11:54:43 +0200 Subject: [PATCH 17/24] fix: add post deletion in the timeline userstory --- CHANGELOG.md | 1 + .../flutter_timeline_gorouter_userstory.dart | 8 +++- .../flutter_timeline_navigator_userstory.dart | 7 ++- .../lib/src/screens/timeline_post_screen.dart | 45 ++----------------- 4 files changed, 18 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a58fd7..9f8b315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - fix the avatar size to match the new design - Add the iconbutton for image uploading back to the ReactionBottom - Fix category key is correctly used for saving timeline posts and category title is shown everywhere +- Fix when clicking on post delete in the post screen of the userstory it will now navigate back to the timeline and delete the post ## 3.0.1 diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 5a34d81..3f4935f 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -154,7 +154,13 @@ List getTimelineStoryRoutes({ options: config.optionsBuilder(context), service: service, post: post!, - onPostDelete: () => config.onPostDelete?.call(context, post), + onPostDelete: () async => + config.onPostDelete?.call(context, post) ?? + () async { + await service.postService.deletePost(post); + if (!context.mounted) return; + context.go(TimelineUserStoryRoutes.timelineHome); + }.call(), onUserTap: (user) => config.onUserTap?.call(context, user), ); diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index e25b67c..250fbf7 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -132,7 +132,12 @@ Widget _postDetailScreenRoute({ post: post, onPostDelete: () async => config.onPostDelete?.call(context, post) ?? - await config.service.postService.deletePost(post), + () async { + await config.service.postService.deletePost(post); + if (context.mounted) { + Navigator.of(context).pop(); + } + }.call(), onUserTap: (user) => config.onUserTap?.call(context, user), ); diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 41d8a27..1bc6922 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -15,7 +15,7 @@ import 'package:flutter_timeline_view/src/widgets/reaction_bottom.dart'; import 'package:flutter_timeline_view/src/widgets/tappable_image.dart'; import 'package:intl/intl.dart'; -class TimelinePostScreen extends StatelessWidget { +class TimelinePostScreen extends StatefulWidget { const TimelinePostScreen({ required this.userId, required this.service, @@ -47,47 +47,10 @@ class TimelinePostScreen extends StatelessWidget { final bool? isOverviewScreen; @override - Widget build(BuildContext context) => _TimelinePostScreen( - userId: userId, - service: service, - options: options, - post: post, - onPostDelete: onPostDelete, - onUserTap: onUserTap, - isOverviewScreen: isOverviewScreen, - ); + State createState() => _TimelinePostScreenState(); } -class _TimelinePostScreen extends StatefulWidget { - const _TimelinePostScreen({ - required this.userId, - required this.service, - required this.options, - required this.post, - required this.onPostDelete, - this.onUserTap, - this.isOverviewScreen, - }); - - final String userId; - - final TimelineService service; - - final TimelineOptions options; - - final TimelinePost post; - - final Function(String userId)? onUserTap; - - final VoidCallback onPostDelete; - - final bool? isOverviewScreen; - - @override - State<_TimelinePostScreen> createState() => _TimelinePostScreenState(); -} - -class _TimelinePostScreenState extends State<_TimelinePostScreen> { +class _TimelinePostScreenState extends State { TimelinePost? post; bool isLoading = true; @@ -231,7 +194,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> { if (widget.options.allowAllDeletion || post.creator?.userId == widget.userId) PopupMenuButton( - onSelected: (value) => widget.onPostDelete(), + onSelected: (value) => widget.onPostDelete.call(), itemBuilder: (BuildContext context) => >[ PopupMenuItem( From 6a522f320989d18a2a1396cb490fd81513ca33a6 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 23 May 2024 13:19:08 +0200 Subject: [PATCH 18/24] fix: change liked to the like icon for unliked posts --- CHANGELOG.md | 1 + .../lib/src/widgets/timeline_post_widget.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8b315..0308c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Add the iconbutton for image uploading back to the ReactionBottom - Fix category key is correctly used for saving timeline posts and category title is shown everywhere - Fix when clicking on post delete in the post screen of the userstory it will now navigate back to the timeline and delete the post +- Fix like icon being used for both like and unliked posts ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart index 8e45197..6b53f3e 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart @@ -257,7 +257,7 @@ class _TimelinePostWidgetState extends State { onTap: widget.onTapLike, child: Container( color: Colors.transparent, - child: widget.options.theme.likedIcon ?? + child: widget.options.theme.likeIcon ?? Icon( Icons.favorite_outline, color: widget.options.theme.iconColor, From 35028b9bb9f28de4f47c4c54f564e46fcc2b1bcc Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 23 May 2024 14:40:15 +0200 Subject: [PATCH 19/24] fix: initialize post with empty like count so posts liking works correctly --- CHANGELOG.md | 1 + .../lib/src/service/firebase_post_service.dart | 2 +- .../lib/src/screens/timeline_post_creation_screen.dart | 1 + .../lib/src/screens/timeline_post_screen.dart | 6 ++++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0308c4c..4c6f89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Fix category key is correctly used for saving timeline posts and category title is shown everywhere - Fix when clicking on post delete in the post screen of the userstory it will now navigate back to the timeline and delete the post - Fix like icon being used for both like and unliked posts +- Fix post creator can only like the post once and after it is actually created ## 3.0.1 diff --git a/packages/flutter_timeline_firebase/lib/src/service/firebase_post_service.dart b/packages/flutter_timeline_firebase/lib/src/service/firebase_post_service.dart index 5956ae0..1c5ec58 100644 --- a/packages/flutter_timeline_firebase/lib/src/service/firebase_post_service.dart +++ b/packages/flutter_timeline_firebase/lib/src/service/firebase_post_service.dart @@ -254,7 +254,7 @@ class FirebaseTimelinePostService // update the post with the new like var updatedPost = post.copyWith( likes: post.likes + 1, - likedBy: post.likedBy?..add(userId), + likedBy: [...post.likedBy ?? [], userId], ); posts = posts .map( diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart index cd36ae3..44e4d38 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_creation_screen.dart @@ -104,6 +104,7 @@ class _TimelinePostCreationScreenState category: widget.postCategory, content: contentController.text, likes: 0, + likedBy: const [], reaction: 0, createdAt: DateTime.now(), reactionEnabled: allowComments, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 1bc6922..b1719c4 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -191,8 +191,9 @@ class _TimelinePostScreenState extends State { ), ), const Spacer(), - if (widget.options.allowAllDeletion || - post.creator?.userId == widget.userId) + if (!(widget.isOverviewScreen ?? false) && + (widget.options.allowAllDeletion || + post.creator?.userId == widget.userId)) PopupMenuButton( onSelected: (value) => widget.onPostDelete.call(), itemBuilder: (BuildContext context) => @@ -305,6 +306,7 @@ class _TimelinePostScreenState extends State { ] else ...[ InkWell( onTap: () async { + if (widget.isOverviewScreen ?? false) return; updatePost( await widget.service.postService.likePost( widget.userId, From 23449ec57d8276409b9a3051ec39786651b6440f Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 23 May 2024 15:59:18 +0200 Subject: [PATCH 20/24] feat: change CategorySelectorButton to use the category icon and some extra styling --- CHANGELOG.md | 1 + .../lib/src/config/timeline_options.dart | 3 +- .../lib/src/config/timeline_paddings.dart | 4 + .../lib/src/config/timeline_theme.dart | 4 +- .../screens/timeline_selection_screen.dart | 5 +- .../lib/src/widgets/category_selector.dart | 3 +- .../src/widgets/category_selector_button.dart | 73 ++++++++++++------- 7 files changed, 59 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c6f89a..83d379f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Fix when clicking on post delete in the post screen of the userstory it will now navigate back to the timeline and delete the post - Fix like icon being used for both like and unliked posts - Fix post creator can only like the post once and after it is actually created +- Change the CategorySelectorButton to use more styling options and allow for an icon to be shown ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart index 592bf65..4c627b5 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart @@ -223,8 +223,7 @@ class CategoriesOptions { /// Abilty to override the standard category selector final Widget Function( - String? categoryKey, - String categoryName, + TimelineCategory category, Function() onTap, // ignore: avoid_positional_boolean_parameters bool selected, diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart b/packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart index 193a0a9..33a4ba4 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_paddings.dart @@ -8,6 +8,7 @@ class TimelinePaddingOptions { this.postPadding = const EdgeInsets.symmetric(vertical: 12.0, horizontal: 12.0), this.postOverviewButtonBottomPadding = 30.0, + this.categoryButtonTextPadding, }); /// The padding between posts in the timeline @@ -18,4 +19,7 @@ class TimelinePaddingOptions { /// The bottom padding of the button on the post overview screen final double postOverviewButtonBottomPadding; + + /// The padding between the icon and the text in the category button + final double? categoryButtonTextPadding; } diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart index 3462b2b..49609cd 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart @@ -15,7 +15,7 @@ class TimelineTheme { this.sendIcon, this.moreIcon, this.deleteIcon, - this.categorySelectionBorderColor, + this.categorySelectionButtonBorderColor, this.categorySelectionButtonBackgroundColor, this.postCreationFloatingActionButtonColor, this.textStyles = const TimelineTextStyles(), @@ -45,7 +45,7 @@ class TimelineTheme { final TimelineTextStyles textStyles; /// The color of the border around the category in the selection screen - final Color? categorySelectionBorderColor; + final Color? categorySelectionButtonBorderColor; /// The color of the background of the category selection button in the /// selection screen diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart index 39f3317..8d23418 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_selection_screen.dart @@ -55,8 +55,9 @@ class TimelineSelectionScreen extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), border: Border.all( - color: options.theme.categorySelectionBorderColor ?? - Theme.of(context).primaryColor, + color: + options.theme.categorySelectionButtonBorderColor ?? + Theme.of(context).primaryColor, width: 2, ), color: diff --git a/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart b/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart index 2e69922..c9fe02b 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/category_selector.dart @@ -41,8 +41,7 @@ class _CategorySelectorState extends State { ), for (var category in categories) ...[ widget.options.categoriesOptions.categoryButtonBuilder?.call( - category.key, - category.title, + category, () => widget.onTapCategory(category.key), widget.filter == category.key, widget.isOnTop, diff --git a/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart b/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart index 0d19566..5ef5611 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/category_selector_button.dart @@ -36,7 +36,10 @@ class CategorySelectorButton extends StatelessWidget { ), fixedSize: MaterialStatePropertyAll(Size(140, isOnTop ? 140 : 20)), backgroundColor: MaterialStatePropertyAll( - selected ? theme.colorScheme.primary : Colors.transparent, + selected + ? theme.colorScheme.primary + : options.theme.categorySelectionButtonBackgroundColor ?? + Colors.transparent, ), shape: MaterialStatePropertyAll( RoundedRectangleBorder( @@ -44,7 +47,8 @@ class CategorySelectorButton extends StatelessWidget { Radius.circular(8), ), side: BorderSide( - color: theme.colorScheme.primary, + color: options.theme.categorySelectionButtonBorderColor ?? + theme.colorScheme.primary, width: 2, ), ), @@ -53,38 +57,55 @@ class CategorySelectorButton extends StatelessWidget { child: isOnTop ? SizedBox( width: MediaQuery.of(context).size.width, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, + child: Stack( children: [ - Text( - category.title, - style: (options.theme.textStyles.categoryTitleStyle ?? - theme.textTheme.labelLarge) - ?.copyWith( - color: selected - ? theme.colorScheme.onPrimary - : theme.colorScheme.onSurface, - ), - textAlign: TextAlign.start, + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + category.title, + style: (options.theme.textStyles.categoryTitleStyle ?? + theme.textTheme.labelLarge) + ?.copyWith( + color: selected + ? theme.colorScheme.onPrimary + : theme.colorScheme.onSurface, + ), + textAlign: TextAlign.start, + ), + ], ), + Center(child: category.icon), ], ), ) : Row( children: [ Flexible( - child: Text( - category.title, - style: (options.theme.textStyles.categoryTitleStyle ?? - theme.textTheme.labelLarge) - ?.copyWith( - color: selected - ? theme.colorScheme.onPrimary - : theme.colorScheme.onSurface, - ), - textAlign: TextAlign.start, - overflow: TextOverflow.ellipsis, + child: Row( + children: [ + category.icon, + SizedBox( + width: + options.paddings.categoryButtonTextPadding ?? 8, + ), + Expanded( + child: Text( + category.title, + style: + (options.theme.textStyles.categoryTitleStyle ?? + theme.textTheme.labelLarge) + ?.copyWith( + color: selected + ? theme.colorScheme.onPrimary + : theme.colorScheme.onSurface, + ), + textAlign: TextAlign.start, + overflow: TextOverflow.ellipsis, + ), + ), + ], ), ), ], From 31f93704b67a2a3c9b573d79ca5d032a9d194410 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 24 May 2024 08:39:23 +0200 Subject: [PATCH 21/24] feat: lock CI version to flutter 3.19.6 --- .github/workflows/melos-component-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/melos-component-ci.yml b/.github/workflows/melos-component-ci.yml index 869bed9..98255ce 100644 --- a/.github/workflows/melos-component-ci.yml +++ b/.github/workflows/melos-component-ci.yml @@ -9,4 +9,6 @@ jobs: call-global-iconica-workflow: uses: Iconica-Development/.github/.github/workflows/melos-ci.yml@master secrets: inherit - permissions: write-all \ No newline at end of file + permissions: write-all + with: + flutter_version: 3.19.6 \ No newline at end of file From 1c46fbea4babebd77ce1dc3f0ab9074be7c73efc Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 24 May 2024 10:09:56 +0200 Subject: [PATCH 22/24] fix: incorrect timeline reaction name --- CHANGELOG.md | 1 + .../lib/src/screens/timeline_post_screen.dart | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d379f..4d803fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Fix like icon being used for both like and unliked posts - Fix post creator can only like the post once and after it is actually created - Change the CategorySelectorButton to use more styling options and allow for an icon to be shown +- Fix incorrect timeline reaction name ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index b1719c4..ce34156 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -483,7 +483,7 @@ class _TimelinePostScreenState extends State { children: [ Text( widget.options.nameBuilder - ?.call(post.creator) ?? + ?.call(reaction.creator) ?? reaction.creator?.fullName ?? widget.options.translations .anonymousUser, @@ -504,7 +504,7 @@ class _TimelinePostScreenState extends State { child: Text.rich( TextSpan( text: widget.options.nameBuilder - ?.call(post.creator) ?? + ?.call(reaction.creator) ?? reaction.creator?.fullName ?? widget .options.translations.anonymousUser, From aa22e1305bd68e60a060f14aa48bfc0078f9c5ac Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 24 May 2024 12:35:15 +0200 Subject: [PATCH 23/24] feat: add a post deletion confirmation dialog --- CHANGELOG.md | 1 + .../lib/src/config/timeline_options.dart | 6 +++ .../lib/src/config/timeline_translations.dart | 24 ++++++++++ .../lib/src/screens/timeline_post_screen.dart | 11 ++++- .../lib/src/widgets/timeline_post_widget.dart | 44 ++++++++++++++++++- 5 files changed, 83 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d803fa..d60b008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Fix post creator can only like the post once and after it is actually created - Change the CategorySelectorButton to use more styling options and allow for an icon to be shown - Fix incorrect timeline reaction name +- Add a dialog for post deletion confirmation ## 3.0.1 diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart index 4c627b5..f16c73a 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart @@ -50,6 +50,7 @@ class TimelineOptions { this.maxContentLength, this.categorySelectorButtonBuilder, this.postOverviewButtonBuilder, + this.deletionDialogBuilder, this.listHeaderBuilder, this.titleInputDecoration, this.contentInputDecoration, @@ -166,6 +167,11 @@ class TimelineOptions { String text, )? postOverviewButtonBuilder; + /// Optional builder to override the default alertdialog for post deletion + /// It should pop the navigator with true to delete the post and + /// false to cancel deletion + final WidgetBuilder? deletionDialogBuilder; + /// inputdecoration for the title textfield final InputDecoration? titleInputDecoration; diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart b/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart index dca146d..bdc6985 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart @@ -31,6 +31,10 @@ class TimelineTranslations { required this.postAt, required this.deletePost, required this.deleteReaction, + required this.deleteConfirmationMessage, + required this.deleteConfirmationTitle, + required this.deleteCancelButton, + required this.deleteButton, required this.viewPost, required this.likesTitle, required this.commentsTitle, @@ -65,6 +69,11 @@ class TimelineTranslations { this.commentsTitleOnPost = 'Comments', this.checkPost = 'Check post overview', this.deletePost = 'Delete post', + this.deleteConfirmationTitle = 'Delete Post', + this.deleteConfirmationMessage = + 'Are you sure you want to delete this post?', + this.deleteButton = 'Delete', + this.deleteCancelButton = 'Cancel', this.deleteReaction = 'Delete Reaction', this.viewPost = 'View post', this.likesTitle = 'Likes', @@ -101,6 +110,11 @@ class TimelineTranslations { final String contentHintText; final String deletePost; + final String deleteConfirmationTitle; + final String deleteConfirmationMessage; + final String deleteButton; + final String deleteCancelButton; + final String deleteReaction; final String viewPost; final String likesTitle; @@ -138,6 +152,10 @@ class TimelineTranslations { String? checkPost, String? postAt, String? deletePost, + String? deleteConfirmationTitle, + String? deleteConfirmationMessage, + String? deleteButton, + String? deleteCancelButton, String? deleteReaction, String? viewPost, String? likesTitle, @@ -173,6 +191,12 @@ class TimelineTranslations { checkPost: checkPost ?? this.checkPost, postAt: postAt ?? this.postAt, deletePost: deletePost ?? this.deletePost, + deleteConfirmationTitle: + deleteConfirmationTitle ?? this.deleteConfirmationTitle, + deleteConfirmationMessage: + deleteConfirmationMessage ?? this.deleteConfirmationMessage, + deleteButton: deleteButton ?? this.deleteButton, + deleteCancelButton: deleteCancelButton ?? this.deleteCancelButton, deleteReaction: deleteReaction ?? this.deleteReaction, viewPost: viewPost ?? this.viewPost, likesTitle: likesTitle ?? this.likesTitle, diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index ce34156..8bb2e40 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -13,6 +13,7 @@ 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/widgets/reaction_bottom.dart'; import 'package:flutter_timeline_view/src/widgets/tappable_image.dart'; +import 'package:flutter_timeline_view/src/widgets/timeline_post_widget.dart'; import 'package:intl/intl.dart'; class TimelinePostScreen extends StatefulWidget { @@ -195,7 +196,15 @@ class _TimelinePostScreenState extends State { (widget.options.allowAllDeletion || post.creator?.userId == widget.userId)) PopupMenuButton( - onSelected: (value) => widget.onPostDelete.call(), + onSelected: (value) async { + if (value == 'delete') { + await showPostDeletionConfirmationDialog( + widget.options, + context, + widget.onPostDelete, + ); + } + }, itemBuilder: (BuildContext context) => >[ PopupMenuItem( diff --git a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart index 6b53f3e..20bbf1a 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart @@ -106,9 +106,13 @@ class _TimelinePostWidgetState extends State { if (widget.options.allowAllDeletion || widget.post.creator?.userId == widget.userId) PopupMenuButton( - onSelected: (value) { + onSelected: (value) async { if (value == 'delete') { - widget.onPostDelete(); + await showPostDeletionConfirmationDialog( + widget.options, + context, + widget.onPostDelete, + ); } }, itemBuilder: (BuildContext context) => @@ -331,3 +335,39 @@ class _TimelinePostWidgetState extends State { ); } } + +Future showPostDeletionConfirmationDialog( + TimelineOptions options, + BuildContext context, + Function() onPostDelete, +) async { + var result = await showDialog( + context: context, + builder: (BuildContext context) => + options.deletionDialogBuilder?.call(context) ?? + AlertDialog( + title: Text(options.translations.deleteConfirmationTitle), + content: Text(options.translations.deleteConfirmationMessage), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(options.translations.deleteCancelButton), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text( + options.translations.deleteButton, + ), + ), + ], + ), + ); + + if (result == true) { + onPostDelete(); + } +} From 71645eee3b35e65ecd02c844e0ef01bcb6b7c28b Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Mon, 27 May 2024 17:25:07 +0200 Subject: [PATCH 24/24] fix: remove allowAllDeletion from the options and add it to the userstory --- CHANGELOG.md | 1 + .../flutter_timeline/example/lib/config/config.dart | 2 +- .../src/flutter_timeline_gorouter_userstory.dart | 2 ++ .../src/flutter_timeline_navigator_userstory.dart | 2 ++ .../lib/src/models/timeline_configuration.dart | 5 +++++ .../lib/src/config/timeline_options.dart | 5 ----- .../lib/src/screens/timeline_post_screen.dart | 9 +++++++-- .../lib/src/screens/timeline_screen.dart | 13 +++++++++---- .../lib/src/widgets/timeline_post_widget.dart | 8 +++++++- 9 files changed, 34 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d60b008..2d909e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Change the CategorySelectorButton to use more styling options and allow for an icon to be shown - Fix incorrect timeline reaction name - Add a dialog for post deletion confirmation +- Add a callback method to determine if a user can delete posts that gets called when needed ## 3.0.1 diff --git a/packages/flutter_timeline/example/lib/config/config.dart b/packages/flutter_timeline/example/lib/config/config.dart index ff46d66..017e672 100644 --- a/packages/flutter_timeline/example/lib/config/config.dart +++ b/packages/flutter_timeline/example/lib/config/config.dart @@ -7,6 +7,7 @@ TimelineUserStoryConfiguration getConfig(TimelineService service) { userId: 'test_user', optionsBuilder: (context) => options, enablePostOverviewScreen: false, + canDeleteAllPosts: (_) => true, ); } @@ -15,7 +16,6 @@ var options = TimelineOptions( paddings: TimelinePaddingOptions( mainPadding: const EdgeInsets.all(20).copyWith(top: 28), ), - allowAllDeletion: true, categoriesOptions: CategoriesOptions( categoriesBuilder: (context) => [ const TimelineCategory( diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart index 3f4935f..7587b76 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_gorouter_userstory.dart @@ -33,6 +33,7 @@ List getTimelineStoryRoutes({ var timelineScreen = TimelineScreen( userId: config.getUserId?.call(context) ?? config.userId, onUserTap: (user) => config.onUserTap?.call(context, user), + allowAllDeletion: config.canDeleteAllPosts?.call(context) ?? false, onRefresh: config.onRefresh, service: service, options: config.optionsBuilder(context), @@ -151,6 +152,7 @@ List getTimelineStoryRoutes({ var timelinePostWidget = TimelinePostScreen( userId: config.getUserId?.call(context) ?? config.userId, + allowAllDeletion: config.canDeleteAllPosts?.call(context) ?? false, options: config.optionsBuilder(context), service: service, post: post!, diff --git a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart index 250fbf7..89db27a 100644 --- a/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart +++ b/packages/flutter_timeline/lib/src/flutter_timeline_navigator_userstory.dart @@ -46,6 +46,7 @@ Widget _timelineScreenRoute({ var timelineScreen = TimelineScreen( userId: config.getUserId?.call(context) ?? config.userId, + allowAllDeletion: config.canDeleteAllPosts?.call(context) ?? false, onUserTap: (user) => config.onUserTap?.call(context, user), service: config.service, options: config.optionsBuilder(context), @@ -127,6 +128,7 @@ Widget _postDetailScreenRoute({ var timelinePostScreen = TimelinePostScreen( userId: config.getUserId?.call(context) ?? config.userId, + allowAllDeletion: config.canDeleteAllPosts?.call(context) ?? false, options: config.optionsBuilder(context), service: config.service, post: post, diff --git a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart index 3d35d6c..884bcff 100644 --- a/packages/flutter_timeline/lib/src/models/timeline_configuration.dart +++ b/packages/flutter_timeline/lib/src/models/timeline_configuration.dart @@ -50,6 +50,7 @@ class TimelineUserStoryConfiguration { required this.optionsBuilder, this.getUserId, this.serviceBuilder, + this.canDeleteAllPosts, this.userId = 'test_user', this.homeOpenPageBuilder, this.postCreationOpenPageBuilder, @@ -72,6 +73,10 @@ class TimelineUserStoryConfiguration { /// A function to get the userId only when needed and with a context final String Function(BuildContext context)? getUserId; + /// A function to determine if a user can delete posts that is called + /// when needed + final bool Function(BuildContext context)? canDeleteAllPosts; + /// The TimelineService responsible for fetching user story data. final TimelineService service; diff --git a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart index f16c73a..e22f2b0 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_options.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_options.dart @@ -18,7 +18,6 @@ class TimelineOptions { this.imagePickerConfig = const ImagePickerConfig(), this.imagePickerTheme = const ImagePickerTheme(), this.timelinePostHeight, - this.allowAllDeletion = false, this.sortCommentsAscending = true, this.sortPostsAscending, this.doubleTapTolike = false, @@ -71,10 +70,6 @@ class TimelineOptions { /// Whether to sort posts ascending or descending final bool? sortPostsAscending; - /// Allow all posts to be deleted instead of - /// only the posts of the current user - final bool allowAllDeletion; - /// The height of a post in the timeline final double? timelinePostHeight; diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart index 8bb2e40..6b857d0 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_post_screen.dart @@ -23,6 +23,7 @@ class TimelinePostScreen extends StatefulWidget { required this.options, required this.post, required this.onPostDelete, + this.allowAllDeletion = false, this.isOverviewScreen = false, this.onUserTap, super.key, @@ -31,6 +32,10 @@ class TimelinePostScreen extends StatefulWidget { /// The user id of the current user final String userId; + /// Allow all posts to be deleted instead of + /// only the posts of the current user + final bool allowAllDeletion; + /// The timeline service to fetch the post details final TimelineService service; @@ -193,7 +198,7 @@ class _TimelinePostScreenState extends State { ), const Spacer(), if (!(widget.isOverviewScreen ?? false) && - (widget.options.allowAllDeletion || + (widget.allowAllDeletion || post.creator?.userId == widget.userId)) PopupMenuButton( onSelected: (value) async { @@ -422,7 +427,7 @@ class _TimelinePostScreenState extends State { GestureDetector( onLongPressStart: (details) async { if (reaction.creatorId == widget.userId || - widget.options.allowAllDeletion) { + widget.allowAllDeletion) { var overlay = Overlay.of(context) .context .findRenderObject()! as RenderBox; diff --git a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart index 1b75303..3be9c56 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -21,12 +21,17 @@ class TimelineScreen extends StatefulWidget { this.timelineCategory, this.postWidgetBuilder, this.filterEnabled = false, + this.allowAllDeletion = false, super.key, }); /// The user id of the current user final String userId; + /// Allow all posts to be deleted instead of + /// only the posts of the current user + final bool allowAllDeletion; + /// The service to use for fetching and manipulating posts final TimelineService? service; @@ -151,10 +156,9 @@ class _TimelineScreenState extends State { ), if (widget.filterEnabled) ...[ Padding( - padding: EdgeInsets.symmetric( - // left: widget.options.padding.left, - // right: widget.options.padding.right, - horizontal: widget.options.paddings.mainPadding.horizontal, + padding: EdgeInsets.only( + left: widget.options.paddings.mainPadding.left, + right: widget.options.paddings.mainPadding.right, ), child: Row( crossAxisAlignment: CrossAxisAlignment.end, @@ -246,6 +250,7 @@ class _TimelineScreenState extends State { service: service, userId: widget.userId, options: widget.options, + allowAllDeletion: widget.allowAllDeletion, post: post, onTap: () async { if (widget.onPostTap != null) { diff --git a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart index 20bbf1a..1051d42 100644 --- a/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart +++ b/packages/flutter_timeline_view/lib/src/widgets/timeline_post_widget.dart @@ -18,12 +18,18 @@ class TimelinePostWidget extends StatefulWidget { required this.onTapUnlike, required this.onPostDelete, required this.service, + required this.allowAllDeletion, this.onUserTap, super.key, }); /// The user id of the current user final String userId; + + /// Allow all posts to be deleted instead of + /// only the posts of the current user + final bool allowAllDeletion; + final TimelineOptions options; final TimelinePost post; @@ -103,7 +109,7 @@ class _TimelinePostWidgetState extends State { ), ), const Spacer(), - if (widget.options.allowAllDeletion || + if (widget.allowAllDeletion || widget.post.creator?.userId == widget.userId) PopupMenuButton( onSelected: (value) async {