mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 10:33:44 +02:00
fix: Refactor and fixes
This commit is contained in:
parent
e99e81c907
commit
73ac508622
8 changed files with 100 additions and 50 deletions
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue