fix: Refactor and fixes

This commit is contained in:
Jacques 2024-01-25 13:36:56 +01:00
parent e99e81c907
commit 73ac508622
8 changed files with 100 additions and 50 deletions

View file

@ -14,6 +14,7 @@ var options = TimelineOptions(
textInputBuilder: null, textInputBuilder: null,
padding: const EdgeInsets.all(20).copyWith(top: 28), padding: const EdgeInsets.all(20).copyWith(top: 28),
allowAllDeletion: true, allowAllDeletion: true,
categoriesOptions: CategoriesOptions(
categoriesBuilder: (context) => [ categoriesBuilder: (context) => [
const TimelineCategory( const TimelineCategory(
key: null, key: null,
@ -31,6 +32,7 @@ var options = TimelineOptions(
icon: SizedBox.shrink(), icon: SizedBox.shrink(),
), ),
], ],
),
); );
void createPost(BuildContext context, TimelineService service, void createPost(BuildContext context, TimelineService service,

View file

@ -30,6 +30,8 @@ Widget _timelineScreenRoute(
onUserTap: (userId) { onUserTap: (userId) {
configuration.onUserTap?.call(context, userId); configuration.onUserTap?.call(context, userId);
}, },
filterEnabled: configuration.filterEnabled,
postWidgetBuilder: configuration.postWidgetBuilder,
); );
Widget _postDetailScreenRoute( Widget _postDetailScreenRoute(

View file

@ -26,6 +26,8 @@ List<GoRoute> getTimelineStoryRoutes(
await context.push( await context.push(
TimelineUserStoryRoutes.timelineViewPath(post.id), TimelineUserStoryRoutes.timelineViewPath(post.id),
), ),
filterEnabled: configuration.filterEnabled,
postWidgetBuilder: configuration.postWidgetBuilder,
); );
return buildScreenWithoutTransition( return buildScreenWithoutTransition(

View file

@ -17,6 +17,8 @@ class TimelineUserStoryConfiguration {
this.onPostTap, this.onPostTap,
this.onUserTap, this.onUserTap,
this.onPostDelete, this.onPostDelete,
this.filterEnabled = false,
this.postWidgetBuilder,
}); });
final String userId; final String userId;
@ -34,4 +36,8 @@ class TimelineUserStoryConfiguration {
final Function(BuildContext context, TimelinePost post)? onPostTap; final Function(BuildContext context, TimelinePost post)? onPostTap;
final Widget Function(BuildContext context, TimelinePost post)? onPostDelete; final Widget Function(BuildContext context, TimelinePost post)? onPostDelete;
final bool filterEnabled;
final Widget Function(TimelinePost post)? postWidgetBuilder;
} }

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2023 Iconica // SPDX-FileCopyrightText: 2023 Iconica
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_image_picker/flutter_image_picker.dart'; import 'package:flutter_image_picker/flutter_image_picker.dart';
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart'; import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
@ -9,7 +10,7 @@ import 'package:flutter_timeline_view/src/config/timeline_translations.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class TimelineOptions { class TimelineOptions {
TimelineOptions({ const TimelineOptions({
this.theme = const TimelineTheme(), this.theme = const TimelineTheme(),
this.translations = const TimelineTranslations.empty(), this.translations = const TimelineTranslations.empty(),
this.imagePickerConfig = const ImagePickerConfig(), this.imagePickerConfig = const ImagePickerConfig(),
@ -40,13 +41,8 @@ class TimelineOptions {
this.iconSize = 26, this.iconSize = 26,
this.postWidgetheight, this.postWidgetheight,
this.postPadding = const EdgeInsets.all(12.0), this.postPadding = const EdgeInsets.all(12.0),
this.categoriesBuilder, this.filterOptions = const FilterOptions(),
this.categoryButtonBuilder, this.categoriesOptions = const CategoriesOptions(),
this.categorySelectorHorizontalPadding,
this.filterEnabled = false,
this.initialFilterWord,
this.searchBarBuilder,
this.postWidget,
}); });
/// Theming options for the timeline /// Theming options for the timeline
@ -120,6 +116,18 @@ class TimelineOptions {
/// Padding of each post /// Padding of each post
final EdgeInsets postPadding; final EdgeInsets postPadding;
final FilterOptions filterOptions;
final CategoriesOptions categoriesOptions;
}
class CategoriesOptions {
const CategoriesOptions({
this.categoriesBuilder,
this.categoryButtonBuilder,
this.categorySelectorHorizontalPadding,
});
/// List of categories that the user can select. /// List of categories that the user can select.
/// If this is null no categories will be shown. /// If this is null no categories will be shown.
final List<TimelineCategory> Function(BuildContext context)? final List<TimelineCategory> Function(BuildContext context)?
@ -136,22 +144,39 @@ class TimelineOptions {
/// Overides the standard horizontal padding of the whole category selector. /// Overides the standard horizontal padding of the whole category selector.
final double? categorySelectorHorizontalPadding; final double? categorySelectorHorizontalPadding;
/// if true the filter textfield is enabled. TimelineCategory? getCategoryByKey(
bool filterEnabled; BuildContext context,
String? key,
) {
if (categoriesBuilder == null) {
return null;
}
return categoriesBuilder!
.call(context)
.firstWhereOrNull((category) => category.key == key);
}
}
class FilterOptions {
const FilterOptions({
this.initialFilterWord,
this.searchBarBuilder,
this.onFilterEnabledChange,
});
/// Set a value to search through posts. When set the searchbar is shown. /// Set a value to search through posts. When set the searchbar is shown.
/// If null no searchbar is shown. /// If null no searchbar is shown.
final String? initialFilterWord; final String? initialFilterWord;
// Possibilty to override the standard search bar.
final Widget Function( final Widget Function(
Future<List<TimelinePost>> Function( Future<List<TimelinePost>> Function(
String filterWord, String filterWord,
Map<String, dynamic> options,
) search, ) search,
)? searchBarBuilder; )? searchBarBuilder;
/// Override the standard postwidget final void Function({required bool filterEnabled})? onFilterEnabledChange;
final Widget Function(TimelinePost post)? postWidget;
} }
typedef ButtonBuilder = Widget Function( typedef ButtonBuilder = Widget Function(

View file

@ -18,6 +18,8 @@ class TimelineScreen extends StatefulWidget {
this.onUserTap, this.onUserTap,
this.posts, this.posts,
this.timelineCategory, this.timelineCategory,
this.postWidgetBuilder,
this.filterEnabled = false,
super.key, super.key,
}); });
@ -46,21 +48,28 @@ class TimelineScreen extends StatefulWidget {
/// If this is not null, the user can tap on the user avatar or name /// If this is not null, the user can tap on the user avatar or name
final Function(String userId)? onUserTap; final Function(String userId)? onUserTap;
/// Override the standard postwidget
final Widget Function(TimelinePost post)? postWidgetBuilder;
/// if true the filter textfield is enabled.
final bool filterEnabled;
@override @override
State<TimelineScreen> createState() => _TimelineScreenState(); State<TimelineScreen> createState() => _TimelineScreenState();
} }
class _TimelineScreenState extends State<TimelineScreen> { class _TimelineScreenState extends State<TimelineScreen> {
late ScrollController controller; late ScrollController controller;
late var textFieldController = late var textFieldController = TextEditingController(
TextEditingController(text: widget.options.initialFilterWord); text: widget.options.filterOptions.initialFilterWord,
);
late var service = widget.service; late var service = widget.service;
bool isLoading = true; bool isLoading = true;
late var category = widget.timelineCategory; late var category = widget.timelineCategory;
late var filterWord = widget.options.initialFilterWord; late var filterWord = widget.options.filterOptions.initialFilterWord;
@override @override
void initState() { void initState() {
@ -81,13 +90,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
builder: (context, _) { builder: (context, _) {
var posts = widget.posts ?? service.getPosts(category); var posts = widget.posts ?? service.getPosts(category);
posts = posts if (widget.filterEnabled && filterWord != null) {
.where(
(p) => category == null || p.category == category,
)
.toList();
if (widget.options.filterEnabled && filterWord != null) {
if (service is TimelineFilterService?) { if (service is TimelineFilterService?) {
posts = posts =
(service as TimelineFilterService).filterPosts(filterWord!, {}); (service as TimelineFilterService).filterPosts(filterWord!, {});
@ -97,6 +100,12 @@ class _TimelineScreenState extends State<TimelineScreen> {
} }
} }
posts = posts
.where(
(p) => category == null || p.category == category,
)
.toList();
// sort posts by date // sort posts by date
if (widget.options.sortPostsAscending != null) { if (widget.options.sortPostsAscending != null) {
posts.sort( posts.sort(
@ -112,7 +121,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
SizedBox( SizedBox(
height: widget.options.padding.top, height: widget.options.padding.top,
), ),
if (widget.options.filterEnabled) ...[ if (widget.filterEnabled) ...[
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: widget.options.padding.horizontal, horizontal: widget.options.padding.horizontal,
@ -151,8 +160,9 @@ class _TimelineScreenState extends State<TimelineScreen> {
onTap: () { onTap: () {
setState(() { setState(() {
textFieldController.clear(); textFieldController.clear();
widget.options.filterEnabled = false;
filterWord = null; filterWord = null;
widget.options.filterOptions.onFilterEnabledChange
?.call(filterEnabled: false);
}); });
}, },
child: const Padding( child: const Padding(
@ -191,7 +201,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
...posts.map( ...posts.map(
(post) => Padding( (post) => Padding(
padding: widget.options.postPadding, padding: widget.options.postPadding,
child: widget.options.postWidget?.call(post) ?? child: widget.postWidgetBuilder?.call(post) ??
TimelinePostWidget( TimelinePostWidget(
service: widget.service, service: widget.service,
userId: widget.userId, userId: widget.userId,

View file

@ -17,22 +17,23 @@ class CategorySelector extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (options.categoriesBuilder == null) { if (options.categoriesOptions.categoriesBuilder == null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
var categories = options.categoriesBuilder!(context); var categories = options.categoriesOptions.categoriesBuilder!(context);
return SingleChildScrollView( return SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(
children: [ children: [
SizedBox( SizedBox(
width: options.categorySelectorHorizontalPadding ?? width:
options.categoriesOptions.categorySelectorHorizontalPadding ??
max(options.padding.horizontal - 4, 0), max(options.padding.horizontal - 4, 0),
), ),
for (var category in categories) ...[ for (var category in categories) ...[
options.categoryButtonBuilder?.call( options.categoriesOptions.categoryButtonBuilder?.call(
categoryKey: category.key, categoryKey: category.key,
categoryName: category.title, categoryName: category.title,
onTap: () => onTapCategory(category.key), onTap: () => onTapCategory(category.key),
@ -48,7 +49,8 @@ class CategorySelector extends StatelessWidget {
), ),
], ],
SizedBox( SizedBox(
width: options.categorySelectorHorizontalPadding ?? width:
options.categoriesOptions.categorySelectorHorizontalPadding ??
max(options.padding.horizontal - 4, 0), max(options.padding.horizontal - 4, 0),
), ),
], ],

View file

@ -29,6 +29,7 @@ dependencies:
git: git:
url: https://github.com/Iconica-Development/flutter_image_picker url: https://github.com/Iconica-Development/flutter_image_picker
ref: 1.0.4 ref: 1.0.4
collection: any
dev_dependencies: dev_dependencies:
flutter_lints: ^2.0.0 flutter_lints: ^2.0.0