feat: add textstyling options

This commit is contained in:
Freek van de Ven 2023-12-06 16:17:49 +01:00
parent 7e89ba9c85
commit cf129c05c0
9 changed files with 221 additions and 39 deletions

View file

@ -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';

View file

@ -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,

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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,
);
}

View file

@ -100,7 +100,10 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
}
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<TimelinePostScreen> {
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<TimelinePostScreen> {
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<TimelinePostScreen> {
),
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,
),
],
),

View file

@ -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<TimelineScreen> {
widget.timelineCategoryFilter == null
? widget.options.translations.noPosts
: widget.options.translations.noPostsWithFilter,
style: widget.options.theme.textStyles.noPostsStyle,
),
),
),

View file

@ -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,
),
),
),

View file

@ -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,
),
],
),