mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 18:43:45 +02:00
feat: add textstyling options
This commit is contained in:
parent
7e89ba9c85
commit
cf129c05c0
9 changed files with 221 additions and 39 deletions
|
@ -5,6 +5,7 @@
|
||||||
library flutter_timeline_view;
|
library flutter_timeline_view;
|
||||||
|
|
||||||
export 'src/config/timeline_options.dart';
|
export 'src/config/timeline_options.dart';
|
||||||
|
export 'src/config/timeline_styles.dart';
|
||||||
export 'src/config/timeline_theme.dart';
|
export 'src/config/timeline_theme.dart';
|
||||||
export 'src/config/timeline_translations.dart';
|
export 'src/config/timeline_translations.dart';
|
||||||
export 'src/screens/timeline_post_creation_screen.dart';
|
export 'src/screens/timeline_post_creation_screen.dart';
|
||||||
|
|
|
@ -12,7 +12,7 @@ import 'package:intl/intl.dart';
|
||||||
class TimelineOptions {
|
class TimelineOptions {
|
||||||
const TimelineOptions({
|
const TimelineOptions({
|
||||||
this.theme = const TimelineTheme(),
|
this.theme = const TimelineTheme(),
|
||||||
this.translations = const TimelineTranslations(),
|
this.translations = const TimelineTranslations.empty(),
|
||||||
this.imagePickerConfig = const ImagePickerConfig(),
|
this.imagePickerConfig = const ImagePickerConfig(),
|
||||||
this.imagePickerTheme = const ImagePickerTheme(),
|
this.imagePickerTheme = const ImagePickerTheme(),
|
||||||
this.timelinePostHeight,
|
this.timelinePostHeight,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline_view/src/config/timeline_styles.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class TimelineTheme {
|
class TimelineTheme {
|
||||||
|
@ -14,6 +15,7 @@ class TimelineTheme {
|
||||||
this.sendIcon,
|
this.sendIcon,
|
||||||
this.moreIcon,
|
this.moreIcon,
|
||||||
this.deleteIcon,
|
this.deleteIcon,
|
||||||
|
this.textStyles = const TimelineTextStyles(),
|
||||||
});
|
});
|
||||||
|
|
||||||
final Color? iconColor;
|
final Color? iconColor;
|
||||||
|
@ -35,4 +37,6 @@ class TimelineTheme {
|
||||||
|
|
||||||
/// The icon for delete action (delete post)
|
/// The icon for delete action (delete post)
|
||||||
final Widget? deleteIcon;
|
final Widget? deleteIcon;
|
||||||
|
|
||||||
|
final TimelineTextStyles textStyles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,30 +7,53 @@ import 'package:flutter/material.dart';
|
||||||
@immutable
|
@immutable
|
||||||
class TimelineTranslations {
|
class TimelineTranslations {
|
||||||
const TimelineTranslations({
|
const TimelineTranslations({
|
||||||
this.anonymousUser = 'Anonymous user',
|
required this.anonymousUser,
|
||||||
this.noPosts = 'No posts yet',
|
required this.noPosts,
|
||||||
this.noPostsWithFilter = 'No posts with this filter',
|
required this.noPostsWithFilter,
|
||||||
this.title = 'Title',
|
required this.title,
|
||||||
this.content = 'Content',
|
required this.content,
|
||||||
this.contentDescription = 'What do you want to share?',
|
required this.contentDescription,
|
||||||
this.uploadImage = 'Upload image',
|
required this.uploadImage,
|
||||||
this.uploadImageDescription = 'Upload an image to your message (optional)',
|
required this.uploadImageDescription,
|
||||||
this.allowComments = 'Are people allowed to comment?',
|
required this.allowComments,
|
||||||
this.allowCommentsDescription =
|
required this.allowCommentsDescription,
|
||||||
'Indicate whether people are allowed to respond',
|
required this.checkPost,
|
||||||
this.checkPost = 'Check post overview',
|
required this.deletePost,
|
||||||
this.deletePost = 'Delete post',
|
required this.deleteReaction,
|
||||||
this.deleteReaction = 'Delete Reaction',
|
required this.viewPost,
|
||||||
this.viewPost = 'View post',
|
required this.likesTitle,
|
||||||
this.likesTitle = 'Likes',
|
required this.commentsTitle,
|
||||||
this.commentsTitle = 'Comments',
|
required this.firstComment,
|
||||||
this.firstComment = 'Be the first to comment',
|
required this.writeComment,
|
||||||
this.writeComment = 'Write your comment here...',
|
required this.postAt,
|
||||||
this.postAt = 'at',
|
required this.postLoadingError,
|
||||||
this.postLoadingError = 'Something went wrong while loading the post',
|
required this.timelineSelectionDescription,
|
||||||
this.timelineSelectionDescription = 'Choose a category',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 noPosts;
|
||||||
final String noPostsWithFilter;
|
final String noPostsWithFilter;
|
||||||
final String anonymousUser;
|
final String anonymousUser;
|
||||||
|
@ -55,4 +78,54 @@ class TimelineTranslations {
|
||||||
final String postLoadingError;
|
final String postLoadingError;
|
||||||
|
|
||||||
final String timelineSelectionDescription;
|
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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,10 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
}
|
}
|
||||||
if (this.post == null) {
|
if (this.post == null) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(widget.options.translations.postLoadingError),
|
child: Text(
|
||||||
|
widget.options.translations.postLoadingError,
|
||||||
|
style: widget.options.theme.textStyles.errorTextStyle,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
var post = this.post!;
|
var post = this.post!;
|
||||||
|
@ -155,7 +158,9 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
if (post.creator!.fullName != null) ...[
|
if (post.creator!.fullName != null) ...[
|
||||||
Text(
|
Text(
|
||||||
post.creator!.fullName!,
|
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',
|
value: 'delete',
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(widget.options.translations.deletePost),
|
Text(
|
||||||
|
widget.options.translations.deletePost,
|
||||||
|
style: widget.options.theme.textStyles
|
||||||
|
.deletePostStyle ??
|
||||||
|
theme.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
widget.options.theme.deleteIcon ??
|
widget.options.theme.deleteIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -255,19 +265,25 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${post.likes} ${widget.options.translations.likesTitle}',
|
'${post.likes} ${widget.options.translations.likesTitle}',
|
||||||
style: theme.textTheme.titleSmall,
|
style: widget
|
||||||
|
.options.theme.textStyles.postLikeTitleAndAmount ??
|
||||||
|
theme.textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: post.creator?.fullName ??
|
text: post.creator?.fullName ??
|
||||||
widget.options.translations.anonymousUser,
|
widget.options.translations.anonymousUser,
|
||||||
style: theme.textTheme.titleSmall,
|
style: widget
|
||||||
|
.options.theme.textStyles.postCreatorNameStyle ??
|
||||||
|
theme.textTheme.titleSmall,
|
||||||
children: [
|
children: [
|
||||||
const TextSpan(text: ' '),
|
const TextSpan(text: ' '),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: post.title,
|
text: post.title,
|
||||||
style: theme.textTheme.bodyMedium,
|
style:
|
||||||
|
widget.options.theme.textStyles.postTitleStyle ??
|
||||||
|
theme.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -35,6 +35,7 @@ class TimelineScreen extends StatefulWidget {
|
||||||
/// The controller for the scroll view
|
/// The controller for the scroll view
|
||||||
final ScrollController? controller;
|
final ScrollController? controller;
|
||||||
|
|
||||||
|
/// The string to filter the timeline by category
|
||||||
final String? timelineCategoryFilter;
|
final String? timelineCategoryFilter;
|
||||||
|
|
||||||
/// This is used if you want to pass in a list of posts instead
|
/// 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.timelineCategoryFilter == null
|
||||||
? widget.options.translations.noPosts
|
? widget.options.translations.noPosts
|
||||||
: widget.options.translations.noPostsWithFilter,
|
: widget.options.translations.noPostsWithFilter,
|
||||||
|
style: widget.options.theme.textStyles.noPostsStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -31,7 +31,9 @@ class TimelineSelectionScreen extends StatelessWidget {
|
||||||
padding: EdgeInsets.only(top: size.height * 0.05, bottom: 8),
|
padding: EdgeInsets.only(top: size.height * 0.05, bottom: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
options.translations.timelineSelectionDescription,
|
options.translations.timelineSelectionDescription,
|
||||||
style: theme.textTheme.displayMedium,
|
style:
|
||||||
|
options.theme.textStyles.categorySelectionDescriptionStyle ??
|
||||||
|
theme.textTheme.displayMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
@ -53,7 +55,8 @@ class TimelineSelectionScreen extends StatelessWidget {
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
category.title,
|
category.title,
|
||||||
style: theme.textTheme.displaySmall,
|
style: options.theme.textStyles.categorySelectionTitleStyle ??
|
||||||
|
theme.textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -43,7 +43,6 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
// TODO(anyone): should posts with text have a max height?
|
|
||||||
height: post.imageUrl != null ? height : null,
|
height: post.imageUrl != null ? height : null,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -74,7 +73,9 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
if (post.creator!.fullName != null) ...[
|
if (post.creator!.fullName != null) ...[
|
||||||
Text(
|
Text(
|
||||||
post.creator!.fullName!,
|
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',
|
value: 'delete',
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(options.translations.deletePost),
|
Text(
|
||||||
|
options.translations.deletePost,
|
||||||
|
style: options.theme.textStyles.deletePostStyle ??
|
||||||
|
theme.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
options.theme.deleteIcon ??
|
options.theme.deleteIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -152,27 +157,31 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
if (post.reactionEnabled)
|
if (post.reactionEnabled)
|
||||||
options.theme.commentIcon ??
|
options.theme.commentIcon ??
|
||||||
const Icon(
|
Icon(
|
||||||
Icons.chat_bubble_outline_rounded,
|
Icons.chat_bubble_outline_rounded,
|
||||||
|
color: options.theme.iconColor,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${post.likes} ${options.translations.likesTitle}',
|
'${post.likes} ${options.translations.likesTitle}',
|
||||||
style: theme.textTheme.titleSmall,
|
style: options.theme.textStyles.listPostLikeTitleAndAmount ??
|
||||||
|
theme.textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: post.creator?.fullName ??
|
text: post.creator?.fullName ??
|
||||||
options.translations.anonymousUser,
|
options.translations.anonymousUser,
|
||||||
style: theme.textTheme.titleSmall,
|
style: options.theme.textStyles.listCreatorNameStyle ??
|
||||||
|
theme.textTheme.titleSmall,
|
||||||
children: [
|
children: [
|
||||||
const TextSpan(text: ' '),
|
const TextSpan(text: ' '),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: post.title,
|
text: post.title,
|
||||||
style: theme.textTheme.bodyMedium,
|
style: options.theme.textStyles.listPostTitleStyle ??
|
||||||
|
theme.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -180,7 +189,8 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
options.translations.viewPost,
|
options.translations.viewPost,
|
||||||
style: theme.textTheme.bodySmall,
|
style: options.theme.textStyles.viewPostStyle ??
|
||||||
|
theme.textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue