wip: Big refactor and update of image picker package. Still missing some docs on some parameters

This commit is contained in:
Jacques 2024-02-29 10:34:49 +01:00
parent 898583d1d1
commit fe1dcd6d0d
21 changed files with 385 additions and 278 deletions

View file

@ -1,3 +1,8 @@
## 3.0.0
- Reorder of all parameters of TimelineOptions
- Updated Flutter Image Picker form 1.0.4 to 3.0.0
## 2.1.0
- Fixed multiline textfield not being dismissible.

View file

@ -1,6 +1,6 @@
// import 'package:example/apps/go_router/app.dart';
// import 'package:example/apps/navigator/app.dart';
import 'package:example/apps/widgets/app.dart';
import 'package:example/apps/navigator/app.dart';
// import 'package:example/apps/widgets/app.dart';
import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart';
@ -9,7 +9,7 @@ void main() {
// Uncomment any, but only one, of these lines to run the example with specific navigation.
runApp(const WidgetApp());
// runApp(const NavigatorApp());
// runApp(const WidgetApp());
runApp(const NavigatorApp());
// runApp(const GoRouterApp());
}

View file

@ -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: 2.1.0
version: 3.0.0
publish_to: none

View file

@ -4,7 +4,7 @@
name: flutter_timeline_firebase
description: Implementation of the Flutter Timeline interface for Firebase.
version: 2.1.0
version: 3.0.0
publish_to: none

View file

@ -4,7 +4,7 @@
name: flutter_timeline_interface
description: Interface for the service of the Flutter Timeline component
version: 2.1.0
version: 3.0.0
publish_to: none

View file

@ -4,6 +4,11 @@
///
library flutter_timeline_view;
export 'src/config/icon_timeline_theme.dart';
export 'src/config/post_config.dart';
export 'src/config/post_creation_config.dart';
export 'src/config/post_theme.dart';
export 'src/config/timeline_config.dart';
export 'src/config/timeline_options.dart';
export 'src/config/timeline_styles.dart';
export 'src/config/timeline_theme.dart';

View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
@immutable
class IconTimelineTheme {
const IconTimelineTheme({
this.iconColor,
this.likeIcon,
this.commentIcon,
this.likedIcon,
this.sendIcon,
this.moreIcon,
this.deleteIcon,
this.likeAndDislikeIconsForDoubleTap = const (
Icon(
Icons.favorite_rounded,
color: Color(0xFFC3007A),
),
null,
),
});
final Color? iconColor;
/// The icon to display when the post is not yet liked
final Widget? likeIcon;
/// The icon to display to indicate that a post has comments enabled
final Widget? commentIcon;
/// The icon to display when the post is liked
final Widget? likedIcon;
/// The icon to display to submit a comment
final Widget? sendIcon;
/// The icon for more actions (open delete menu)
final Widget? moreIcon;
/// The icon for delete action (delete post)
final Widget? deleteIcon;
/// The icons to display when double tap to like is enabled
final (Icon?, Icon?) likeAndDislikeIconsForDoubleTap;
}

View file

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2024 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
class TimelinePostConfig {
const TimelinePostConfig({
this.doubleTapTolike = false,
});
/// Whether to allow double tap to like
final bool doubleTapTolike;
}

View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2024 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
class TimelinePostCreationConfig {
const TimelinePostCreationConfig({
this.requireImageForPost = false,
this.minTitleLength,
this.maxTitleLength,
this.minContentLength,
this.maxContentLength,
});
/// Require image for post
final bool requireImageForPost;
/// Minimum length of the title
final int? minTitleLength;
/// Maximum length of the title
final int? maxTitleLength;
/// Minimum length of the post content
final int? minContentLength;
/// Maximum length of the post content
final int? maxContentLength;
}

View file

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2024 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
import 'package:flutter_timeline_view/src/config/timeline_options.dart';
class TimelinePostCreationTheme {
const TimelinePostCreationTheme({
this.buttonBuilder,
this.textInputBuilder,
this.pagePadding = const EdgeInsets.all(20),
});
final ButtonBuilder? buttonBuilder;
final TextInputBuilder? textInputBuilder;
final EdgeInsets pagePadding;
}

View file

@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: 2024 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
import 'package:flutter_timeline_view/flutter_timeline_view.dart';
import 'package:intl/intl.dart';
class TimelinePostTheme {
const TimelinePostTheme({
this.timelinePostHeight,
this.iconsWithValues = false,
this.itemInfoBuilder,
this.dateFormat,
this.timeFormat,
this.userAvatarBuilder,
this.anonymousAvatarBuilder,
this.nameBuilder,
this.iconSize = 26,
this.postWidgetHeight,
this.postPadding = const EdgeInsets.all(12.0),
this.pagePadding = const EdgeInsets.all(20),
this.iconTheme = const IconTimelineTheme(),
});
/// The height of a post in the timeline
final double? timelinePostHeight;
/// Whether to display the icons with values
final bool iconsWithValues;
/// The builder for the item info, all below the like and comment buttons
final Widget Function({required TimelinePost post})? itemInfoBuilder;
/// The format to display the post date in
final DateFormat? dateFormat;
/// The format to display the post time in
final DateFormat? timeFormat;
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;
/// 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;
final EdgeInsets pagePadding;
/// Theming options for the icons within timeline
final IconTimelineTheme iconTheme;
}

View file

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2024 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
class TimelineConfig {
const TimelineConfig({
this.allowAllDeletion = false,
this.sortCommentsAscending = true,
this.sortPostsAscending,
});
/// Allow all posts to be deleted instead of
/// only the posts of the current user
final bool allowAllDeletion;
/// Whether to sort comments ascending or descending
final bool sortCommentsAscending;
/// Whether to sort posts ascending or descending
final bool? sortPostsAscending;
}

View file

@ -5,87 +5,35 @@ 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/flutter_timeline_view.dart';
import 'package:flutter_timeline_view/src/config/post_config.dart';
import 'package:flutter_timeline_view/src/config/post_creation_config.dart';
import 'package:flutter_timeline_view/src/config/post_creation_theme.dart';
import 'package:flutter_timeline_view/src/config/post_theme.dart';
import 'package:flutter_timeline_view/src/config/timeline_config.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';
class TimelineOptions {
const TimelineOptions({
this.theme = const TimelineTheme(),
this.textStyles = const TimelineTextStyles(),
this.translations = const TimelineTranslations.empty(),
this.imagePickerConfig = const ImagePickerConfig(),
this.imagePickerTheme = const ImagePickerTheme(),
this.timelinePostHeight,
this.allowAllDeletion = false,
this.sortCommentsAscending = true,
this.sortPostsAscending,
this.doubleTapTolike = false,
this.iconsWithValues = false,
this.likeAndDislikeIconsForDoubleTap = const (
Icon(
Icons.favorite_rounded,
color: Color(0xFFC3007A),
),
null,
),
this.itemInfoBuilder,
this.dateFormat,
this.timeFormat,
this.buttonBuilder,
this.textInputBuilder,
this.dividerBuilder,
this.userAvatarBuilder,
this.anonymousAvatarBuilder,
this.nameBuilder,
this.padding = const EdgeInsets.symmetric(vertical: 12.0),
this.iconSize = 26,
this.postWidgetHeight,
this.postPadding = const EdgeInsets.all(12.0),
this.filterOptions = const FilterOptions(),
this.categoriesOptions = const CategoriesOptions(),
this.requireImageForPost = false,
this.minTitleLength,
this.maxTitleLength,
this.minContentLength,
this.maxContentLength,
this.postTheme = const TimelinePostTheme(),
this.config = const TimelineConfig(),
this.postConfig = const TimelinePostConfig(),
this.theme = const TimelineTheme(),
this.postCreationTheme = const TimelinePostCreationTheme(),
this.postCreationConfig = const TimelinePostCreationConfig(),
});
/// Theming options for the timeline
final TimelineTheme theme;
/// The format to display the post date in
final DateFormat? dateFormat;
/// The format to display the post time in
final DateFormat? timeFormat;
/// Whether to sort comments ascending or descending
final bool sortCommentsAscending;
/// 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;
/// Parameter to set all textstyles within timeline
final TimelineTextStyles textStyles;
final TimelineTranslations translations;
final ButtonBuilder? buttonBuilder;
final TextInputBuilder? textInputBuilder;
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
/// Image Picker Widget to change the text/icons to your liking.
final ImagePickerTheme imagePickerTheme;
@ -94,53 +42,20 @@ class TimelineOptions {
/// size and quality for the uploaded image.
final ImagePickerConfig imagePickerConfig;
/// Whether to allow double tap to like
final bool doubleTapTolike;
/// The icons to display when double tap to like is enabled
final (Icon?, Icon?) likeAndDislikeIconsForDoubleTap;
/// Whether to display the icons with values
final bool iconsWithValues;
/// The builder for the item info, all below the like and comment buttons
final Widget Function({required TimelinePost post})? itemInfoBuilder;
/// 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;
/// Options for using the category selector.
final CategoriesOptions categoriesOptions;
/// Require image for post
final bool requireImageForPost;
final TimelineConfig config;
final TimelineTheme theme;
/// Minimum length of the title
final int? minTitleLength;
final TimelinePostConfig postConfig;
final TimelinePostTheme postTheme;
/// Maximum length of the title
final int? maxTitleLength;
/// Minimum length of the post content
final int? minContentLength;
/// Maximum length of the post content
final int? maxContentLength;
final TimelinePostCreationTheme postCreationTheme;
final TimelinePostCreationConfig postCreationConfig;
}
class CategoriesOptions {

View file

@ -1,42 +1,17 @@
// SPDX-FileCopyrightText: 2023 Iconica
// SPDX-FileCopyrightText: 2024 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
import 'package:flutter_timeline_view/src/config/timeline_styles.dart';
@immutable
class TimelineTheme {
const TimelineTheme({
this.iconColor,
this.likeIcon,
this.commentIcon,
this.likedIcon,
this.sendIcon,
this.moreIcon,
this.deleteIcon,
this.textStyles = const TimelineTextStyles(),
this.dividerBuilder,
this.pagePadding = const EdgeInsets.all(20),
});
final Color? iconColor;
/// The builder for the divider
final Widget Function()? dividerBuilder;
/// The icon to display when the post is not yet liked
final Widget? likeIcon;
/// The icon to display to indicate that a post has comments enabled
final Widget? commentIcon;
/// The icon to display when the post is liked
final Widget? likedIcon;
/// The icon to display to submit a comment
final Widget? sendIcon;
/// The icon for more actions (open delete menu)
final Widget? moreIcon;
/// The icon for delete action (delete post)
final Widget? deleteIcon;
final TimelineTextStyles textStyles;
final EdgeInsets pagePadding;
}

View file

@ -69,24 +69,28 @@ class _TimelinePostCreationScreenState
setState(() {
editingDone =
titleController.text.isNotEmpty && contentController.text.isNotEmpty;
if (widget.options.requireImageForPost) {
if (widget.options.postCreationConfig.requireImageForPost) {
editingDone = editingDone && image != null;
}
if (widget.options.minTitleLength != null) {
if (widget.options.postCreationConfig.minTitleLength != null) {
editingDone = editingDone &&
titleController.text.length >= widget.options.minTitleLength!;
titleController.text.length >=
widget.options.postCreationConfig.minTitleLength!;
}
if (widget.options.maxTitleLength != null) {
if (widget.options.postCreationConfig.maxTitleLength != null) {
editingDone = editingDone &&
titleController.text.length <= widget.options.maxTitleLength!;
titleController.text.length <=
widget.options.postCreationConfig.maxTitleLength!;
}
if (widget.options.minContentLength != null) {
if (widget.options.postCreationConfig.minContentLength != null) {
editingDone = editingDone &&
contentController.text.length >= widget.options.minContentLength!;
contentController.text.length >=
widget.options.postCreationConfig.minContentLength!;
}
if (widget.options.maxContentLength != null) {
if (widget.options.postCreationConfig.maxContentLength != null) {
editingDone = editingDone &&
contentController.text.length <= widget.options.maxContentLength!;
contentController.text.length <=
widget.options.postCreationConfig.maxContentLength!;
}
});
}
@ -119,7 +123,7 @@ class _TimelinePostCreationScreenState
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Padding(
padding: widget.options.padding,
padding: widget.options.postCreationTheme.pagePadding,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
@ -129,7 +133,7 @@ class _TimelinePostCreationScreenState
widget.options.translations.title,
style: theme.textTheme.displaySmall,
),
widget.options.textInputBuilder?.call(
widget.options.postCreationTheme.textInputBuilder?.call(
titleController,
null,
'',
@ -185,8 +189,8 @@ class _TimelinePostCreationScreenState
padding: const EdgeInsets.all(8.0),
color: theme.colorScheme.background,
child: ImagePicker(
imagePickerConfig: widget.options.imagePickerConfig,
imagePickerTheme: widget.options.imagePickerTheme,
config: widget.options.imagePickerConfig,
theme: widget.options.imagePickerTheme,
),
),
);
@ -271,8 +275,8 @@ class _TimelinePostCreationScreenState
),
Align(
alignment: Alignment.bottomCenter,
child: (widget.options.buttonBuilder != null)
? widget.options.buttonBuilder!(
child: (widget.options.postCreationTheme.buttonBuilder != null)
? widget.options.postCreationTheme.buttonBuilder!(
context,
onPostCreated,
widget.options.translations.checkPost,

View file

@ -86,15 +86,16 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
TimelinePost? post;
bool isLoading = true;
late var textInputBuilder = widget.options.textInputBuilder ??
(controller, suffixIcon, hintText) => TextField(
textCapitalization: TextCapitalization.sentences,
controller: controller,
decoration: InputDecoration(
hintText: hintText,
suffixIcon: suffixIcon,
),
);
late var textInputBuilder =
widget.options.postCreationTheme.textInputBuilder ??
(controller, suffixIcon, hintText) => TextField(
textCapitalization: TextCapitalization.sentences,
controller: controller,
decoration: InputDecoration(
hintText: hintText,
suffixIcon: suffixIcon,
),
);
@override
void initState() {
@ -129,9 +130,9 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var dateFormat = widget.options.dateFormat ??
var dateFormat = widget.options.postTheme.dateFormat ??
DateFormat('dd/MM/yyyy', Localizations.localeOf(context).languageCode);
var timeFormat = widget.options.timeFormat ?? DateFormat('HH:mm');
var timeFormat = widget.options.postTheme.timeFormat ?? DateFormat('HH:mm');
if (isLoading) {
const Center(
@ -142,13 +143,13 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
return Center(
child: Text(
widget.options.translations.postLoadingError,
style: widget.options.theme.textStyles.errorTextStyle,
style: widget.options.textStyles.errorTextStyle,
),
);
}
var post = this.post!;
post.reactions?.sort(
(a, b) => widget.options.sortCommentsAscending
(a, b) => widget.options.config.sortCommentsAscending
? a.createdAt.compareTo(b.createdAt)
: b.createdAt.compareTo(a.createdAt),
);
@ -167,7 +168,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
},
child: SingleChildScrollView(
child: Padding(
padding: widget.options.padding,
padding: widget.options.postTheme.pagePadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -182,7 +183,8 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
child: Row(
children: [
if (post.creator!.imageUrl != null) ...[
widget.options.userAvatarBuilder?.call(
widget.options.postTheme.userAvatarBuilder
?.call(
post.creator!,
40,
) ??
@ -194,7 +196,8 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
),
),
] else ...[
widget.options.anonymousAvatarBuilder?.call(
widget.options.postTheme.anonymousAvatarBuilder
?.call(
post.creator!,
40,
) ??
@ -207,11 +210,11 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
],
const SizedBox(width: 10),
Text(
widget.options.nameBuilder
widget.options.postTheme.nameBuilder
?.call(post.creator) ??
post.creator?.fullName ??
widget.options.translations.anonymousUser,
style: widget.options.theme.textStyles
style: widget.options.textStyles
.postCreatorTitleStyle ??
theme.textTheme.titleMedium,
),
@ -219,7 +222,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
),
),
const Spacer(),
if (widget.options.allowAllDeletion ||
if (widget.options.config.allowAllDeletion ||
post.creator?.userId == widget.userId)
PopupMenuButton(
onSelected: (value) => widget.onPostDelete(),
@ -231,24 +234,27 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
children: [
Text(
widget.options.translations.deletePost,
style: widget.options.theme.textStyles
style: widget.options.textStyles
.deletePostStyle ??
theme.textTheme.bodyMedium,
),
const SizedBox(width: 8),
widget.options.theme.deleteIcon ??
widget.options.postTheme.iconTheme
.deleteIcon ??
Icon(
Icons.delete,
color: widget.options.theme.iconColor,
color: widget.options.postTheme
.iconTheme.iconColor,
),
],
),
),
],
child: widget.options.theme.moreIcon ??
child: widget.options.postTheme.iconTheme.moreIcon ??
Icon(
Icons.more_horiz_rounded,
color: widget.options.theme.iconColor,
color: widget
.options.postTheme.iconTheme.iconColor,
),
),
],
@ -258,10 +264,10 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
const SizedBox(height: 8),
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: widget.options.doubleTapTolike
child: widget.options.postConfig.doubleTapTolike
? TappableImage(
likeAndDislikeIcon: widget
.options.likeAndDislikeIconsForDoubleTap,
likeAndDislikeIcon: widget.options.postTheme
.iconTheme.likeAndDislikeIconsForDoubleTap,
post: post,
userId: widget.userId,
onLike: ({required bool liked}) async {
@ -314,11 +320,13 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
},
child: Container(
color: Colors.transparent,
child: widget.options.theme.likedIcon ??
Icon(
Icons.thumb_up_rounded,
color: widget.options.theme.iconColor,
),
child:
widget.options.postTheme.iconTheme.likedIcon ??
Icon(
Icons.thumb_up_rounded,
color: widget.options.postTheme.iconTheme
.iconColor,
),
),
),
] else ...[
@ -333,49 +341,50 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
},
child: Container(
color: Colors.transparent,
child: widget.options.theme.likeIcon ??
Icon(
Icons.thumb_up_alt_outlined,
color: widget.options.theme.iconColor,
size: widget.options.iconSize,
),
child:
widget.options.postTheme.iconTheme.likeIcon ??
Icon(
Icons.thumb_up_alt_outlined,
color: widget.options.postTheme.iconTheme
.iconColor,
size: widget.options.postTheme.iconSize,
),
),
),
],
const SizedBox(width: 8),
if (post.reactionEnabled)
widget.options.theme.commentIcon ??
widget.options.postTheme.iconTheme.commentIcon ??
Icon(
Icons.chat_bubble_outline_rounded,
color: widget.options.theme.iconColor,
size: widget.options.iconSize,
color:
widget.options.postTheme.iconTheme.iconColor,
size: widget.options.postTheme.iconSize,
),
],
),
const SizedBox(height: 8),
Text(
'${post.likes} ${widget.options.translations.likesTitle}',
style: widget
.options.theme.textStyles.postLikeTitleAndAmount ??
style: widget.options.textStyles.postLikeTitleAndAmount ??
theme.textTheme.titleSmall
?.copyWith(color: Colors.black),
),
const SizedBox(height: 4),
Text.rich(
TextSpan(
text: widget.options.nameBuilder?.call(post.creator) ??
text: widget.options.postTheme.nameBuilder
?.call(post.creator) ??
post.creator?.fullName ??
widget.options.translations.anonymousUser,
style: widget
.options.theme.textStyles.postCreatorNameStyle ??
style: widget.options.textStyles.postCreatorNameStyle ??
theme.textTheme.titleSmall,
children: [
const TextSpan(text: ' '),
TextSpan(
text: post.title,
style:
widget.options.theme.textStyles.postTitleStyle ??
theme.textTheme.bodyMedium,
style: widget.options.textStyles.postTitleStyle ??
theme.textTheme.bodyMedium,
),
],
),
@ -427,7 +436,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
GestureDetector(
onLongPressStart: (details) async {
if (reaction.creatorId == widget.userId ||
widget.options.allowAllDeletion) {
widget.options.config.allowAllDeletion) {
var overlay = Overlay.of(context)
.context
.findRenderObject()! as RenderBox;
@ -467,7 +476,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
children: [
if (reaction.creator?.imageUrl != null &&
reaction.creator!.imageUrl!.isNotEmpty) ...[
widget.options.userAvatarBuilder?.call(
widget.options.postTheme.userAvatarBuilder?.call(
reaction.creator!,
25,
) ??
@ -478,7 +487,8 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
),
),
] else ...[
widget.options.anonymousAvatarBuilder?.call(
widget.options.postTheme.anonymousAvatarBuilder
?.call(
reaction.creator!,
25,
) ??
@ -496,7 +506,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.options.nameBuilder
widget.options.postTheme.nameBuilder
?.call(post.creator) ??
reaction.creator?.fullName ??
widget.options.translations
@ -517,7 +527,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
Expanded(
child: Text.rich(
TextSpan(
text: widget.options.nameBuilder
text: widget.options.postTheme.nameBuilder
?.call(post.creator) ??
reaction.creator?.fullName ??
widget
@ -565,8 +575,8 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
padding: const EdgeInsets.all(8.0),
color: theme.colorScheme.background,
child: ImagePicker(
imagePickerConfig: widget.options.imagePickerConfig,
imagePickerTheme: widget.options.imagePickerTheme,
config: widget.options.imagePickerConfig,
theme: widget.options.imagePickerTheme,
),
),
);
@ -598,7 +608,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
),
),
translations: widget.options.translations,
iconColor: widget.options.theme.iconColor,
iconColor: widget.options.postTheme.iconTheme.iconColor,
),
),
],

View file

@ -110,9 +110,9 @@ class _TimelineScreenState extends State<TimelineScreen> {
.toList();
// sort posts by date
if (widget.options.sortPostsAscending != null) {
if (widget.options.config.sortPostsAscending != null) {
posts.sort(
(a, b) => widget.options.sortPostsAscending!
(a, b) => widget.options.config.sortPostsAscending!
? a.createdAt.compareTo(b.createdAt)
: b.createdAt.compareTo(a.createdAt),
);
@ -122,12 +122,12 @@ class _TimelineScreenState extends State<TimelineScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: widget.options.padding.top,
height: widget.options.postTheme.postPadding.top,
),
if (widget.filterEnabled) ...[
Padding(
padding: EdgeInsets.symmetric(
horizontal: widget.options.padding.horizontal,
horizontal: widget.options.postTheme.postPadding.horizontal,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
@ -203,7 +203,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
children: [
...posts.map(
(post) => Padding(
padding: widget.options.postPadding,
padding: widget.options.postTheme.postPadding,
child: widget.postWidgetBuilder?.call(post) ??
TimelinePostWidget(
service: service,
@ -253,7 +253,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
category == null
? widget.options.translations.noPosts
: widget.options.translations.noPostsWithFilter,
style: widget.options.theme.textStyles.noPostsStyle,
style: widget.options.textStyles.noPostsStyle,
),
),
),
@ -262,7 +262,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
),
),
SizedBox(
height: widget.options.padding.bottom,
height: widget.options.postTheme.postPadding.bottom,
),
],
);

View file

@ -31,9 +31,8 @@ class TimelineSelectionScreen extends StatelessWidget {
padding: EdgeInsets.only(top: size.height * 0.05, bottom: 8),
child: Text(
options.translations.timelineSelectionDescription,
style:
options.theme.textStyles.categorySelectionDescriptionStyle ??
theme.textTheme.displayMedium,
style: options.textStyles.categorySelectionDescriptionStyle ??
theme.textTheme.displayMedium,
),
),
const SizedBox(height: 4),
@ -55,7 +54,7 @@ class TimelineSelectionScreen extends StatelessWidget {
margin: const EdgeInsets.symmetric(vertical: 8),
child: Text(
category.title,
style: options.theme.textStyles.categorySelectionTitleStyle ??
style: options.textStyles.categorySelectionTitleStyle ??
theme.textTheme.displaySmall,
),
),

View file

@ -28,9 +28,9 @@ class CategorySelector extends StatelessWidget {
child: Row(
children: [
SizedBox(
width:
options.categoriesOptions.categorySelectorHorizontalPadding ??
max(options.padding.horizontal - 4, 0),
width: options
.categoriesOptions.categorySelectorHorizontalPadding ??
max(options.postCreationTheme.pagePadding.horizontal - 4, 0),
),
for (var category in categories) ...[
options.categoriesOptions.categoryButtonBuilder?.call(
@ -49,9 +49,9 @@ class CategorySelector extends StatelessWidget {
),
],
SizedBox(
width:
options.categoriesOptions.categorySelectorHorizontalPadding ??
max(options.padding.horizontal - 4, 0),
width: options
.categoriesOptions.categorySelectorHorizontalPadding ??
max(options.postCreationTheme.pagePadding.horizontal - 4, 0),
),
],
),

View file

@ -50,7 +50,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
onTap: widget.onTap,
child: SizedBox(
height: widget.post.imageUrl != null
? widget.options.postWidgetHeight
? widget.options.postTheme.postWidgetHeight
: null,
width: double.infinity,
child: Column(
@ -67,7 +67,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
child: Row(
children: [
if (widget.post.creator!.imageUrl != null) ...[
widget.options.userAvatarBuilder?.call(
widget.options.postTheme.userAvatarBuilder?.call(
widget.post.creator!,
40,
) ??
@ -78,7 +78,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
),
),
] else ...[
widget.options.anonymousAvatarBuilder?.call(
widget.options.postTheme.anonymousAvatarBuilder?.call(
widget.post.creator!,
40,
) ??
@ -91,19 +91,19 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
],
const SizedBox(width: 10),
Text(
widget.options.nameBuilder
widget.options.postTheme.nameBuilder
?.call(widget.post.creator) ??
widget.post.creator?.fullName ??
widget.options.translations.anonymousUser,
style: widget.options.theme.textStyles
.postCreatorTitleStyle ??
theme.textTheme.titleMedium,
style:
widget.options.textStyles.postCreatorTitleStyle ??
theme.textTheme.titleMedium,
),
],
),
),
const Spacer(),
if (widget.options.allowAllDeletion ||
if (widget.options.config.allowAllDeletion ||
widget.post.creator?.userId == widget.userId)
PopupMenuButton(
onSelected: (value) {
@ -119,24 +119,25 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
children: [
Text(
widget.options.translations.deletePost,
style: widget.options.theme.textStyles
.deletePostStyle ??
theme.textTheme.bodyMedium,
style:
widget.options.textStyles.deletePostStyle ??
theme.textTheme.bodyMedium,
),
const SizedBox(width: 8),
widget.options.theme.deleteIcon ??
widget.options.postTheme.iconTheme.deleteIcon ??
Icon(
Icons.delete,
color: widget.options.theme.iconColor,
color: widget
.options.postTheme.iconTheme.iconColor,
),
],
),
),
],
child: widget.options.theme.moreIcon ??
child: widget.options.postTheme.iconTheme.moreIcon ??
Icon(
Icons.more_horiz_rounded,
color: widget.options.theme.iconColor,
color: widget.options.postTheme.iconTheme.iconColor,
),
),
],
@ -145,13 +146,13 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
if (widget.post.imageUrl != null) ...[
const SizedBox(height: 8),
Flexible(
flex: widget.options.postWidgetHeight != null ? 1 : 0,
flex: widget.options.postTheme.postWidgetHeight != null ? 1 : 0,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: widget.options.doubleTapTolike
child: widget.options.postConfig.doubleTapTolike
? TappableImage(
likeAndDislikeIcon:
widget.options.likeAndDislikeIconsForDoubleTap,
likeAndDislikeIcon: widget.options.postTheme.iconTheme
.likeAndDislikeIconsForDoubleTap,
post: widget.post,
userId: widget.userId,
onLike: ({required bool liked}) async {
@ -188,7 +189,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
height: 8,
),
// post information
if (widget.options.iconsWithValues)
if (widget.options.postTheme.iconsWithValues)
Row(
children: [
TextButton.icon(
@ -210,7 +211,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
);
}
},
icon: widget.options.theme.likeIcon ??
icon: widget.options.postTheme.iconTheme.likeIcon ??
Icon(
widget.post.likedBy?.contains(widget.userId) ?? false
? Icons.favorite
@ -221,7 +222,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
if (widget.post.reactionEnabled)
TextButton.icon(
onPressed: widget.onTap,
icon: widget.options.theme.commentIcon ??
icon: widget.options.postTheme.iconTheme.commentIcon ??
const Icon(
Icons.chat_bubble_outline_outlined,
),
@ -238,11 +239,12 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
onTap: widget.onTapUnlike,
child: Container(
color: Colors.transparent,
child: widget.options.theme.likedIcon ??
child: widget.options.postTheme.iconTheme.likedIcon ??
Icon(
Icons.thumb_up_rounded,
color: widget.options.theme.iconColor,
size: widget.options.iconSize,
color:
widget.options.postTheme.iconTheme.iconColor,
size: widget.options.postTheme.iconSize,
),
),
),
@ -251,11 +253,12 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
onTap: widget.onTapLike,
child: Container(
color: Colors.transparent,
child: widget.options.theme.likedIcon ??
child: widget.options.postTheme.iconTheme.likedIcon ??
Icon(
Icons.thumb_up_rounded,
color: widget.options.theme.iconColor,
size: widget.options.iconSize,
color:
widget.options.postTheme.iconTheme.iconColor,
size: widget.options.postTheme.iconSize,
),
),
),
@ -264,11 +267,11 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
if (widget.post.reactionEnabled) ...[
Container(
color: Colors.transparent,
child: widget.options.theme.commentIcon ??
child: widget.options.postTheme.iconTheme.commentIcon ??
Icon(
Icons.chat_bubble_outline_rounded,
color: widget.options.theme.iconColor,
size: widget.options.iconSize,
color: widget.options.postTheme.iconTheme.iconColor,
size: widget.options.postTheme.iconSize,
),
),
],
@ -279,33 +282,32 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
height: 8,
),
if (widget.options.itemInfoBuilder != null) ...[
widget.options.itemInfoBuilder!(
if (widget.options.postTheme.itemInfoBuilder != null) ...[
widget.options.postTheme.itemInfoBuilder!(
post: widget.post,
),
] else ...[
Text(
'${widget.post.likes} '
'${widget.options.translations.likesTitle}',
style: widget
.options.theme.textStyles.listPostLikeTitleAndAmount ??
style: widget.options.textStyles.listPostLikeTitleAndAmount ??
theme.textTheme.titleSmall,
),
const SizedBox(height: 4),
Text.rich(
TextSpan(
text: widget.options.nameBuilder?.call(widget.post.creator) ??
text: widget.options.postTheme.nameBuilder
?.call(widget.post.creator) ??
widget.post.creator?.fullName ??
widget.options.translations.anonymousUser,
style: widget.options.theme.textStyles.listCreatorNameStyle ??
style: widget.options.textStyles.listCreatorNameStyle ??
theme.textTheme.titleSmall,
children: [
const TextSpan(text: ' '),
TextSpan(
text: widget.post.title,
style:
widget.options.theme.textStyles.listPostTitleStyle ??
theme.textTheme.bodyMedium,
style: widget.options.textStyles.listPostTitleStyle ??
theme.textTheme.bodyMedium,
),
],
),
@ -313,12 +315,12 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
const SizedBox(height: 4),
Text(
widget.options.translations.viewPost,
style: widget.options.theme.textStyles.viewPostStyle ??
style: widget.options.textStyles.viewPostStyle ??
theme.textTheme.bodySmall,
),
],
if (widget.options.dividerBuilder != null)
widget.options.dividerBuilder!(),
if (widget.options.theme.dividerBuilder != null)
widget.options.theme.dividerBuilder!(),
],
),
),

View file

@ -4,7 +4,7 @@
name: flutter_timeline_view
description: Visual elements of the Flutter Timeline Component
version: 2.1.0
version: 3.0.0
publish_to: none
@ -27,7 +27,7 @@ dependencies:
flutter_image_picker:
git:
url: https://github.com/Iconica-Development/flutter_image_picker
ref: 1.0.4
ref: 3.0.0
collection: any
dev_dependencies: