diff --git a/packages/flutter_timeline_view/lib/flutter_timeline_view.dart b/packages/flutter_timeline_view/lib/flutter_timeline_view.dart index 99c5da1..d0b2f63 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_styles.dart'; export 'src/config/timeline_theme.dart'; export 'src/config/timeline_translations.dart'; export 'src/screens/timeline_post_creation_screen.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 a5d34d5..21792be 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_styles.dart b/packages/flutter_timeline_view/lib/src/config/timeline_styles.dart new file mode 100644 index 0000000..e8209c3 --- /dev/null +++ b/packages/flutter_timeline_view/lib/src/config/timeline_styles.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; + +@immutable +class TimelineTextStyles { + /// Options to update all the texts in the timeline view + /// with different textstyles + const TimelineTextStyles({ + this.viewPostStyle, + this.listPostTitleStyle, + this.listPostCreatorTitleStyle, + this.listCreatorNameStyle, + this.listPostLikeTitleAndAmount, + this.deletePostStyle, + this.categorySelectionDescriptionStyle, + this.categorySelectionTitleStyle, + this.noPostsStyle, + this.errorTextStyle, + this.postCreatorTitleStyle, + this.postCreatorNameStyle, + this.postTitleStyle, + this.postLikeTitleAndAmount, + this.postCreatedAtStyle, + }); + + /// The TextStyle for the text indicating that you can view a post + final TextStyle? viewPostStyle; + + /// The TextStyle for the creatorname at the top of the card + /// when it is in the list + final TextStyle? listPostCreatorTitleStyle; + + /// The TextStyle for the post title when it is in the list + final TextStyle? listPostTitleStyle; + + /// The TextStyle for the creatorname at the bottom of the card + /// when it is in the list + final TextStyle? listCreatorNameStyle; + + /// The TextStyle for the amount of like and name of the likes at + /// the bottom of the card when it is in the list + final TextStyle? listPostLikeTitleAndAmount; + + /// The TextStyle for the deletion text that shows in the popupmenu + final TextStyle? deletePostStyle; + + /// The TextStyle for the category explainer on the selection page + final TextStyle? categorySelectionDescriptionStyle; + + /// The TextStyle for the category items in the list on the selection page + final TextStyle? categorySelectionTitleStyle; + + /// The TextStyle for the text when there are no posts + final TextStyle? noPostsStyle; + + /// The TextStyle for all error texts + final TextStyle? errorTextStyle; + + /// The TextStyle for the creatorname at the top of the post page + final TextStyle? postCreatorTitleStyle; + + /// The TextStyle for the creatorname at the bottom of the post page + final TextStyle? postCreatorNameStyle; + + /// The TextStyle for the title of the post on the post page + final TextStyle? postTitleStyle; + + /// The TextStyle for the amount of likes and name of the likes + /// on the post page + final TextStyle? postLikeTitleAndAmount; + + /// The TextStyle for the creation time of the post + final TextStyle? postCreatedAtStyle; +} 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 d880031..a2bb2c0 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_theme.dart @@ -3,6 +3,7 @@ // SPDX-License-Identifier: BSD-3-Clause import 'package:flutter/material.dart'; +import 'package:flutter_timeline_view/src/config/timeline_styles.dart'; @immutable class TimelineTheme { @@ -14,6 +15,7 @@ class TimelineTheme { this.sendIcon, this.moreIcon, this.deleteIcon, + this.textStyles = const TimelineTextStyles(), }); final Color? iconColor; @@ -35,4 +37,6 @@ class TimelineTheme { /// The icon for delete action (delete post) final Widget? deleteIcon; + + final TimelineTextStyles textStyles; } 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 19ec269..f01b1bc 100644 --- a/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart +++ b/packages/flutter_timeline_view/lib/src/config/timeline_translations.dart @@ -7,30 +7,53 @@ import 'package:flutter/material.dart'; @immutable class TimelineTranslations { const TimelineTranslations({ - this.anonymousUser = 'Anonymous user', - this.noPosts = 'No posts yet', - this.noPostsWithFilter = 'No posts with this filter', - this.title = 'Title', - this.content = 'Content', - this.contentDescription = 'What do you want to share?', - this.uploadImage = 'Upload image', - this.uploadImageDescription = 'Upload an image to your message (optional)', - this.allowComments = 'Are people allowed to comment?', - this.allowCommentsDescription = - 'Indicate whether people are allowed to respond', - this.checkPost = 'Check post overview', - this.deletePost = 'Delete post', - this.deleteReaction = 'Delete Reaction', - this.viewPost = 'View post', - this.likesTitle = 'Likes', - this.commentsTitle = 'Comments', - this.firstComment = 'Be the first to comment', - this.writeComment = 'Write your comment here...', - this.postAt = 'at', - this.postLoadingError = 'Something went wrong while loading the post', - this.timelineSelectionDescription = 'Choose a category', + required this.anonymousUser, + required this.noPosts, + required this.noPostsWithFilter, + required this.title, + required this.content, + required this.contentDescription, + required this.uploadImage, + required this.uploadImageDescription, + required this.allowComments, + required this.allowCommentsDescription, + required this.checkPost, + required this.deletePost, + required this.deleteReaction, + required this.viewPost, + required this.likesTitle, + required this.commentsTitle, + required this.firstComment, + required this.writeComment, + required this.postAt, + required this.postLoadingError, + required this.timelineSelectionDescription, }); + const TimelineTranslations.empty() + : anonymousUser = 'Anonymous user', + noPosts = 'No posts yet', + noPostsWithFilter = 'No posts with this filter', + title = 'Title', + content = 'Content', + contentDescription = 'What do you want to share?', + uploadImage = 'Upload image', + uploadImageDescription = 'Upload an image to your message (optional)', + allowComments = 'Are people allowed to comment?', + allowCommentsDescription = + 'Indicate whether people are allowed to respond', + checkPost = 'Check post overview', + deletePost = 'Delete post', + deleteReaction = 'Delete Reaction', + viewPost = 'View post', + likesTitle = 'Likes', + commentsTitle = 'Comments', + firstComment = 'Be the first to comment', + writeComment = 'Write your comment here...', + postAt = 'at', + postLoadingError = 'Something went wrong while loading the post', + timelineSelectionDescription = 'Choose a category'; + final String noPosts; final String noPostsWithFilter; final String anonymousUser; @@ -55,4 +78,54 @@ class TimelineTranslations { final String postLoadingError; final String timelineSelectionDescription; + + TimelineTranslations copyWith({ + String? noPosts, + String? noPostsWithFilter, + String? anonymousUser, + String? title, + String? content, + String? contentDescription, + String? uploadImage, + String? uploadImageDescription, + String? allowComments, + String? allowCommentsDescription, + String? checkPost, + String? postAt, + String? deletePost, + String? deleteReaction, + String? viewPost, + String? likesTitle, + String? commentsTitle, + String? writeComment, + String? firstComment, + String? postLoadingError, + String? timelineSelectionDescription, + }) => + TimelineTranslations( + noPosts: noPosts ?? this.noPosts, + noPostsWithFilter: noPostsWithFilter ?? this.noPostsWithFilter, + anonymousUser: anonymousUser ?? this.anonymousUser, + title: title ?? this.title, + content: content ?? this.content, + contentDescription: contentDescription ?? this.contentDescription, + uploadImage: uploadImage ?? this.uploadImage, + uploadImageDescription: + uploadImageDescription ?? this.uploadImageDescription, + allowComments: allowComments ?? this.allowComments, + allowCommentsDescription: + allowCommentsDescription ?? this.allowCommentsDescription, + checkPost: checkPost ?? this.checkPost, + postAt: postAt ?? this.postAt, + deletePost: deletePost ?? this.deletePost, + deleteReaction: deleteReaction ?? this.deleteReaction, + viewPost: viewPost ?? this.viewPost, + likesTitle: likesTitle ?? this.likesTitle, + commentsTitle: commentsTitle ?? this.commentsTitle, + writeComment: writeComment ?? this.writeComment, + firstComment: firstComment ?? this.firstComment, + postLoadingError: postLoadingError ?? this.postLoadingError, + timelineSelectionDescription: + timelineSelectionDescription ?? this.timelineSelectionDescription, + ); } 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 47f4b8e..f6d779d 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 @@ -100,7 +100,10 @@ class _TimelinePostScreenState extends State { } if (this.post == null) { return Center( - child: Text(widget.options.translations.postLoadingError), + child: Text( + widget.options.translations.postLoadingError, + style: widget.options.theme.textStyles.errorTextStyle, + ), ); } var post = this.post!; @@ -155,7 +158,9 @@ class _TimelinePostScreenState extends State { if (post.creator!.fullName != null) ...[ Text( post.creator!.fullName!, - style: theme.textTheme.titleMedium, + style: widget.options.theme.textStyles + .postCreatorTitleStyle ?? + theme.textTheme.titleMedium, ), ], ], @@ -177,7 +182,12 @@ class _TimelinePostScreenState extends State { value: 'delete', child: Row( children: [ - Text(widget.options.translations.deletePost), + Text( + widget.options.translations.deletePost, + style: widget.options.theme.textStyles + .deletePostStyle ?? + theme.textTheme.bodyMedium, + ), const SizedBox(width: 8), widget.options.theme.deleteIcon ?? Icon( @@ -255,19 +265,25 @@ class _TimelinePostScreenState extends State { ), Text( '${post.likes} ${widget.options.translations.likesTitle}', - style: theme.textTheme.titleSmall, + style: widget + .options.theme.textStyles.postLikeTitleAndAmount ?? + theme.textTheme.titleSmall, ), const SizedBox(height: 4), Text.rich( TextSpan( text: post.creator?.fullName ?? widget.options.translations.anonymousUser, - style: theme.textTheme.titleSmall, + style: widget + .options.theme.textStyles.postCreatorNameStyle ?? + theme.textTheme.titleSmall, children: [ const TextSpan(text: ' '), TextSpan( text: post.title, - style: theme.textTheme.bodyMedium, + style: + widget.options.theme.textStyles.postTitleStyle ?? + theme.textTheme.bodyMedium, ), ], ), 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 523593f..cf0f214 100644 --- a/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart +++ b/packages/flutter_timeline_view/lib/src/screens/timeline_screen.dart @@ -35,6 +35,7 @@ class TimelineScreen extends StatefulWidget { /// The controller for the scroll view final ScrollController? controller; + /// The string to filter the timeline by category final String? timelineCategoryFilter; /// This is used if you want to pass in a list of posts instead @@ -121,6 +122,7 @@ class _TimelineScreenState extends State { widget.timelineCategoryFilter == null ? widget.options.translations.noPosts : widget.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 7e7a61a..789923e 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 @@ -31,7 +31,9 @@ class TimelineSelectionScreen extends StatelessWidget { padding: EdgeInsets.only(top: size.height * 0.05, bottom: 8), child: Text( options.translations.timelineSelectionDescription, - style: theme.textTheme.displayMedium, + style: + options.theme.textStyles.categorySelectionDescriptionStyle ?? + theme.textTheme.displayMedium, ), ), const SizedBox(height: 4), @@ -53,7 +55,8 @@ class TimelineSelectionScreen extends StatelessWidget { margin: const EdgeInsets.symmetric(vertical: 8), child: Text( category.title, - style: theme.textTheme.displaySmall, + style: options.theme.textStyles.categorySelectionTitleStyle ?? + theme.textTheme.displaySmall, ), ), ), 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 8bb570f..ffdcd58 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 @@ -43,7 +43,6 @@ class TimelinePostWidget extends StatelessWidget { return InkWell( onTap: onTap, child: SizedBox( - // TODO(anyone): should posts with text have a max height? height: post.imageUrl != null ? height : null, width: double.infinity, child: Column( @@ -74,7 +73,9 @@ class TimelinePostWidget extends StatelessWidget { if (post.creator!.fullName != null) ...[ Text( post.creator!.fullName!, - style: theme.textTheme.titleMedium, + style: options.theme.textStyles + .listPostCreatorTitleStyle ?? + theme.textTheme.titleMedium, ), ], ], @@ -94,7 +95,11 @@ class TimelinePostWidget extends StatelessWidget { value: 'delete', child: Row( children: [ - Text(options.translations.deletePost), + Text( + options.translations.deletePost, + style: options.theme.textStyles.deletePostStyle ?? + theme.textTheme.bodyMedium, + ), const SizedBox(width: 8), options.theme.deleteIcon ?? Icon( @@ -152,27 +157,31 @@ class TimelinePostWidget extends StatelessWidget { const SizedBox(width: 8), if (post.reactionEnabled) options.theme.commentIcon ?? - const Icon( + Icon( Icons.chat_bubble_outline_rounded, + color: options.theme.iconColor, ), ], ), ), Text( '${post.likes} ${options.translations.likesTitle}', - style: theme.textTheme.titleSmall, + style: options.theme.textStyles.listPostLikeTitleAndAmount ?? + theme.textTheme.titleSmall, ), const SizedBox(height: 4), Text.rich( TextSpan( text: post.creator?.fullName ?? options.translations.anonymousUser, - style: theme.textTheme.titleSmall, + style: options.theme.textStyles.listCreatorNameStyle ?? + theme.textTheme.titleSmall, children: [ const TextSpan(text: ' '), TextSpan( text: post.title, - style: theme.textTheme.bodyMedium, + style: options.theme.textStyles.listPostTitleStyle ?? + theme.textTheme.bodyMedium, ), ], ), @@ -180,7 +189,8 @@ class TimelinePostWidget extends StatelessWidget { ), Text( options.translations.viewPost, - style: theme.textTheme.bodySmall, + style: options.theme.textStyles.viewPostStyle ?? + theme.textTheme.bodySmall, ), ], ),