mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 10:33:44 +02:00
feat:add namebuilder
This commit is contained in:
parent
03b14924a2
commit
d60244fa3e
5 changed files with 180 additions and 108 deletions
|
@ -26,6 +26,7 @@ class TimelinePost {
|
||||||
this.reactions,
|
this.reactions,
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
this.image,
|
this.image,
|
||||||
|
this.data = const {},
|
||||||
});
|
});
|
||||||
|
|
||||||
factory TimelinePost.fromJson(String id, Map<String, dynamic> json) =>
|
factory TimelinePost.fromJson(String id, Map<String, dynamic> json) =>
|
||||||
|
@ -50,6 +51,7 @@ class TimelinePost {
|
||||||
.toList(),
|
.toList(),
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
reactionEnabled: json['reaction_enabled'] as bool,
|
reactionEnabled: json['reaction_enabled'] as bool,
|
||||||
|
data: json['data'] ?? {},
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The unique identifier of the post.
|
/// The unique identifier of the post.
|
||||||
|
@ -94,6 +96,9 @@ class TimelinePost {
|
||||||
/// If reacting is enabled on the post.
|
/// If reacting is enabled on the post.
|
||||||
final bool reactionEnabled;
|
final bool reactionEnabled;
|
||||||
|
|
||||||
|
/// Option to add extra data to a timelinepost that won't be shown anywhere
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
|
||||||
TimelinePost copyWith({
|
TimelinePost copyWith({
|
||||||
String? id,
|
String? id,
|
||||||
String? creatorId,
|
String? creatorId,
|
||||||
|
@ -109,6 +114,7 @@ class TimelinePost {
|
||||||
List<TimelinePostReaction>? reactions,
|
List<TimelinePostReaction>? reactions,
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
bool? reactionEnabled,
|
bool? reactionEnabled,
|
||||||
|
Map<String, dynamic>? data,
|
||||||
}) =>
|
}) =>
|
||||||
TimelinePost(
|
TimelinePost(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
@ -125,6 +131,7 @@ class TimelinePost {
|
||||||
reactions: reactions ?? this.reactions,
|
reactions: reactions ?? this.reactions,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
reactionEnabled: reactionEnabled ?? this.reactionEnabled,
|
reactionEnabled: reactionEnabled ?? this.reactionEnabled,
|
||||||
|
data: data ?? this.data,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
|
@ -140,5 +147,6 @@ class TimelinePost {
|
||||||
'reactions': reactions?.map((e) => e.toJson()).toList() ?? [],
|
'reactions': reactions?.map((e) => e.toJson()).toList() ?? [],
|
||||||
'created_at': createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
'reaction_enabled': reactionEnabled,
|
'reaction_enabled': reactionEnabled,
|
||||||
|
'data': data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ class TimelineOptions {
|
||||||
this.buttonBuilder,
|
this.buttonBuilder,
|
||||||
this.textInputBuilder,
|
this.textInputBuilder,
|
||||||
this.userAvatarBuilder,
|
this.userAvatarBuilder,
|
||||||
|
this.anonymousAvatarBuilder,
|
||||||
|
this.nameBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Theming options for the timeline
|
/// Theming options for the timeline
|
||||||
|
@ -56,6 +58,12 @@ class TimelineOptions {
|
||||||
|
|
||||||
final UserAvatarBuilder? userAvatarBuilder;
|
final UserAvatarBuilder? userAvatarBuilder;
|
||||||
|
|
||||||
|
/// When the imageUrl is null this anonymousAvatarBuilder will be used
|
||||||
|
/// You can use it to display a default avatarW
|
||||||
|
final UserAvatarBuilder? anonymousAvatarBuilder;
|
||||||
|
|
||||||
|
final String Function(TimelinePosterUserModel?)? nameBuilder;
|
||||||
|
|
||||||
/// ImagePickerTheme can be used to change the UI of the
|
/// ImagePickerTheme can be used to change the UI of the
|
||||||
/// Image Picker Widget to change the text/icons to your liking.
|
/// Image Picker Widget to change the text/icons to your liking.
|
||||||
final ImagePickerTheme imagePickerTheme;
|
final ImagePickerTheme imagePickerTheme;
|
||||||
|
@ -78,7 +86,7 @@ typedef TextInputBuilder = Widget Function(
|
||||||
String hintText,
|
String hintText,
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef UserAvatarBuilder = Widget Function(
|
typedef UserAvatarBuilder = Widget? Function(
|
||||||
TimelinePosterUserModel user,
|
TimelinePosterUserModel user,
|
||||||
double size,
|
double size,
|
||||||
);
|
);
|
||||||
|
|
|
@ -123,6 +123,7 @@ class _TimelinePostCreationScreenState
|
||||||
height: 100,
|
height: 100,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: contentController,
|
controller: contentController,
|
||||||
|
textCapitalization: TextCapitalization.sentences,
|
||||||
expands: true,
|
expands: true,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
minLines: null,
|
minLines: null,
|
||||||
|
|
|
@ -153,16 +153,28 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
post.creator!.imageUrl!,
|
post.creator!.imageUrl!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
] else ...[
|
||||||
|
widget.options.anonymousAvatarBuilder?.call(
|
||||||
|
post.creator!,
|
||||||
|
40,
|
||||||
|
) ??
|
||||||
|
const CircleAvatar(
|
||||||
|
radius: 20,
|
||||||
|
child: Icon(
|
||||||
|
Icons.person,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
if (post.creator!.fullName != null) ...[
|
Text(
|
||||||
Text(
|
widget.options.nameBuilder
|
||||||
post.creator!.fullName!,
|
?.call(post.creator) ??
|
||||||
style: widget.options.theme.textStyles
|
post.creator?.fullName ??
|
||||||
.postCreatorTitleStyle ??
|
widget.options.translations.anonymousUser,
|
||||||
theme.textTheme.titleMedium,
|
style: widget.options.theme.textStyles
|
||||||
),
|
.postCreatorTitleStyle ??
|
||||||
],
|
theme.textTheme.titleMedium,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -206,63 +218,67 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
|
||||||
// image of the post
|
// image of the post
|
||||||
if (post.imageUrl != null) ...[
|
if (post.imageUrl != null) ...[
|
||||||
CachedNetworkImage(
|
const SizedBox(height: 8),
|
||||||
imageUrl: post.imageUrl!,
|
ClipRRect(
|
||||||
width: double.infinity,
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
fit: BoxFit.fitHeight,
|
child: CachedNetworkImage(
|
||||||
|
width: double.infinity,
|
||||||
|
imageUrl: post.imageUrl!,
|
||||||
|
fit: BoxFit.fitHeight,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
// post information
|
// post information
|
||||||
Padding(
|
Row(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
children: [
|
||||||
child: Row(
|
if (post.likedBy?.contains(widget.userId) ?? false) ...[
|
||||||
children: [
|
InkWell(
|
||||||
if (post.likedBy?.contains(widget.userId) ?? false) ...[
|
onTap: () async {
|
||||||
InkWell(
|
updatePost(
|
||||||
onTap: () async {
|
await widget.service.unlikePost(
|
||||||
updatePost(
|
widget.userId,
|
||||||
await widget.service.unlikePost(
|
post,
|
||||||
widget.userId,
|
),
|
||||||
post,
|
);
|
||||||
),
|
},
|
||||||
);
|
child: widget.options.theme.likedIcon ??
|
||||||
},
|
|
||||||
child: widget.options.theme.likedIcon ??
|
|
||||||
Icon(
|
|
||||||
Icons.thumb_up_rounded,
|
|
||||||
color: widget.options.theme.iconColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
] else ...[
|
|
||||||
InkWell(
|
|
||||||
onTap: () async {
|
|
||||||
updatePost(
|
|
||||||
await widget.service.likePost(
|
|
||||||
widget.userId,
|
|
||||||
post,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: widget.options.theme.likeIcon ??
|
|
||||||
Icon(
|
|
||||||
Icons.thumb_up_alt_outlined,
|
|
||||||
color: widget.options.theme.iconColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
if (post.reactionEnabled)
|
|
||||||
widget.options.theme.commentIcon ??
|
|
||||||
Icon(
|
Icon(
|
||||||
Icons.chat_bubble_outline_rounded,
|
Icons.thumb_up_rounded,
|
||||||
color: widget.options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
updatePost(
|
||||||
|
await widget.service.likePost(
|
||||||
|
widget.userId,
|
||||||
|
post,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: widget.options.theme.likeIcon ??
|
||||||
|
Icon(
|
||||||
|
Icons.thumb_up_alt_outlined,
|
||||||
|
color: widget.options.theme.iconColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
|
if (post.reactionEnabled)
|
||||||
|
widget.options.theme.commentIcon ??
|
||||||
|
Icon(
|
||||||
|
Icons.chat_bubble_outline_rounded,
|
||||||
|
color: widget.options.theme.iconColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'${post.likes} ${widget.options.translations.likesTitle}',
|
'${post.likes} ${widget.options.translations.likesTitle}',
|
||||||
style: widget
|
style: widget
|
||||||
|
@ -272,7 +288,8 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: post.creator?.fullName ??
|
text: widget.options.nameBuilder?.call(post.creator) ??
|
||||||
|
post.creator?.fullName ??
|
||||||
widget.options.translations.anonymousUser,
|
widget.options.translations.anonymousUser,
|
||||||
style: widget
|
style: widget
|
||||||
.options.theme.textStyles.postCreatorNameStyle ??
|
.options.theme.textStyles.postCreatorNameStyle ??
|
||||||
|
@ -287,9 +304,8 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 20),
|
||||||
Html(
|
Html(
|
||||||
data: post.content,
|
data: post.content,
|
||||||
style: {
|
style: {
|
||||||
|
@ -300,15 +316,20 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
'#': Style(
|
'#': Style(
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
textOverflow: TextOverflow.ellipsis,
|
textOverflow: TextOverflow.ellipsis,
|
||||||
|
padding: HtmlPaddings.zero,
|
||||||
|
margin: Margins.zero,
|
||||||
),
|
),
|
||||||
'H1': Style(
|
'H1': Style(
|
||||||
margin: Margins.all(0),
|
padding: HtmlPaddings.zero,
|
||||||
|
margin: Margins.zero,
|
||||||
),
|
),
|
||||||
'H2': Style(
|
'H2': Style(
|
||||||
margin: Margins.all(0),
|
padding: HtmlPaddings.zero,
|
||||||
|
margin: Margins.zero,
|
||||||
),
|
),
|
||||||
'H3': Style(
|
'H3': Style(
|
||||||
margin: Margins.all(0),
|
padding: HtmlPaddings.zero,
|
||||||
|
margin: Margins.zero,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -319,7 +340,7 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
'${timeFormat.format(post.createdAt)}',
|
'${timeFormat.format(post.createdAt)}',
|
||||||
style: theme.textTheme.bodySmall,
|
style: theme.textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 20),
|
||||||
if (post.reactionEnabled) ...[
|
if (post.reactionEnabled) ...[
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.commentsTitle,
|
widget.options.translations.commentsTitle,
|
||||||
|
@ -381,6 +402,17 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
reaction.creator!.imageUrl!,
|
reaction.creator!.imageUrl!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
] else ...[
|
||||||
|
widget.options.anonymousAvatarBuilder?.call(
|
||||||
|
reaction.creator!,
|
||||||
|
25,
|
||||||
|
) ??
|
||||||
|
const CircleAvatar(
|
||||||
|
radius: 20,
|
||||||
|
child: Icon(
|
||||||
|
Icons.person,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
if (reaction.imageUrl != null) ...[
|
if (reaction.imageUrl != null) ...[
|
||||||
|
@ -389,7 +421,9 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
reaction.creator?.fullName ??
|
widget.options.nameBuilder
|
||||||
|
?.call(post.creator) ??
|
||||||
|
reaction.creator?.fullName ??
|
||||||
widget.options.translations
|
widget.options.translations
|
||||||
.anonymousUser,
|
.anonymousUser,
|
||||||
style: theme.textTheme.titleSmall,
|
style: theme.textTheme.titleSmall,
|
||||||
|
@ -408,7 +442,9 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: reaction.creator?.fullName ??
|
text: widget.options.nameBuilder
|
||||||
|
?.call(post.creator) ??
|
||||||
|
reaction.creator?.fullName ??
|
||||||
widget
|
widget
|
||||||
.options.translations.anonymousUser,
|
.options.translations.anonymousUser,
|
||||||
style: theme.textTheme.titleSmall,
|
style: theme.textTheme.titleSmall,
|
||||||
|
|
|
@ -68,16 +68,27 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
post.creator!.imageUrl!,
|
post.creator!.imageUrl!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
] else ...[
|
||||||
|
options.anonymousAvatarBuilder?.call(
|
||||||
|
post.creator!,
|
||||||
|
40,
|
||||||
|
) ??
|
||||||
|
const CircleAvatar(
|
||||||
|
radius: 20,
|
||||||
|
child: Icon(
|
||||||
|
Icons.person,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
if (post.creator!.fullName != null) ...[
|
Text(
|
||||||
Text(
|
options.nameBuilder?.call(post.creator) ??
|
||||||
post.creator!.fullName!,
|
post.creator?.fullName ??
|
||||||
style: options.theme.textStyles
|
options.translations.anonymousUser,
|
||||||
.listPostCreatorTitleStyle ??
|
style:
|
||||||
theme.textTheme.titleMedium,
|
options.theme.textStyles.postCreatorTitleStyle ??
|
||||||
),
|
theme.textTheme.titleMedium,
|
||||||
],
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -118,52 +129,59 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
|
||||||
// image of the post
|
// image of the post
|
||||||
if (post.imageUrl != null) ...[
|
if (post.imageUrl != null) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: height != null ? 1 : 0,
|
flex: height != null ? 1 : 0,
|
||||||
child: CachedNetworkImage(
|
child: ClipRRect(
|
||||||
imageUrl: post.imageUrl!,
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
width: double.infinity,
|
child: CachedNetworkImage(
|
||||||
fit: BoxFit.fitWidth,
|
width: double.infinity,
|
||||||
|
imageUrl: post.imageUrl!,
|
||||||
|
fit: BoxFit.fitWidth,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
// post information
|
// post information
|
||||||
Padding(
|
Row(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
children: [
|
||||||
child: Row(
|
if (post.likedBy?.contains(userId) ?? false) ...[
|
||||||
children: [
|
InkWell(
|
||||||
if (post.likedBy?.contains(userId) ?? false) ...[
|
onTap: onTapUnlike,
|
||||||
InkWell(
|
child: options.theme.likedIcon ??
|
||||||
onTap: onTapUnlike,
|
|
||||||
child: options.theme.likedIcon ??
|
|
||||||
Icon(
|
|
||||||
Icons.thumb_up_rounded,
|
|
||||||
color: options.theme.iconColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
] else ...[
|
|
||||||
InkWell(
|
|
||||||
onTap: onTapLike,
|
|
||||||
child: options.theme.likeIcon ??
|
|
||||||
Icon(
|
|
||||||
Icons.thumb_up_alt_outlined,
|
|
||||||
color: options.theme.iconColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
if (post.reactionEnabled)
|
|
||||||
options.theme.commentIcon ??
|
|
||||||
Icon(
|
Icon(
|
||||||
Icons.chat_bubble_outline_rounded,
|
Icons.thumb_up_rounded,
|
||||||
color: options.theme.iconColor,
|
color: options.theme.iconColor,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
InkWell(
|
||||||
|
onTap: onTapLike,
|
||||||
|
child: options.theme.likeIcon ??
|
||||||
|
Icon(
|
||||||
|
Icons.thumb_up_alt_outlined,
|
||||||
|
color: options.theme.iconColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
|
if (post.reactionEnabled)
|
||||||
|
options.theme.commentIcon ??
|
||||||
|
Icon(
|
||||||
|
Icons.chat_bubble_outline_rounded,
|
||||||
|
color: options.theme.iconColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
'${post.likes} ${options.translations.likesTitle}',
|
'${post.likes} ${options.translations.likesTitle}',
|
||||||
style: options.theme.textStyles.listPostLikeTitleAndAmount ??
|
style: options.theme.textStyles.listPostLikeTitleAndAmount ??
|
||||||
|
@ -172,7 +190,8 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: post.creator?.fullName ??
|
text: options.nameBuilder?.call(post.creator) ??
|
||||||
|
post.creator?.fullName ??
|
||||||
options.translations.anonymousUser,
|
options.translations.anonymousUser,
|
||||||
style: options.theme.textStyles.listCreatorNameStyle ??
|
style: options.theme.textStyles.listCreatorNameStyle ??
|
||||||
theme.textTheme.titleSmall,
|
theme.textTheme.titleSmall,
|
||||||
|
@ -185,8 +204,8 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
options.translations.viewPost,
|
options.translations.viewPost,
|
||||||
style: options.theme.textStyles.viewPostStyle ??
|
style: options.theme.textStyles.viewPostStyle ??
|
||||||
|
|
Loading…
Reference in a new issue