mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 10:33:44 +02:00
feat: added go router and navigator user stories
This commit is contained in:
parent
9125c47ac4
commit
e99e81c907
24 changed files with 437 additions and 320 deletions
|
@ -54,7 +54,7 @@ final GoRouter _router = GoRouter(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
...getTimelineStoryRoutes()
|
...getTimelineStoryRoutes(timelineUserStoryConfiguration)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import 'package:example/config/config.dart';
|
||||||
|
import 'package:example/services/timeline_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline/flutter_timeline.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
List<GoRoute> getTimelineRoutes() => getTimelineStoryRoutes(
|
||||||
|
getConfig(
|
||||||
|
TestTimelineService(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final _router = GoRouter(
|
||||||
|
initialLocation: '/timeline',
|
||||||
|
routes: [
|
||||||
|
...getTimelineRoutes(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
class GoRouterApp extends StatelessWidget {
|
||||||
|
const GoRouterApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp.router(
|
||||||
|
routerConfig: _router,
|
||||||
|
title: 'Flutter Timeline',
|
||||||
|
theme: ThemeData(
|
||||||
|
colorScheme:
|
||||||
|
ColorScheme.fromSeed(seedColor: Colors.deepPurple).copyWith(
|
||||||
|
background: const Color(0xFFB8E2E8),
|
||||||
|
),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
import 'package:example/config/config.dart';
|
||||||
|
import 'package:example/services/timeline_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline/flutter_timeline.dart';
|
||||||
|
|
||||||
|
class NavigatorApp extends StatelessWidget {
|
||||||
|
const NavigatorApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'Flutter Timeline',
|
||||||
|
theme: ThemeData(
|
||||||
|
colorScheme:
|
||||||
|
ColorScheme.fromSeed(seedColor: Colors.deepPurple).copyWith(
|
||||||
|
background: const Color(0xFFB8E2E8),
|
||||||
|
),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
home: const MyHomePage(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHomePage extends StatefulWidget {
|
||||||
|
const MyHomePage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyHomePage> createState() => _MyHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
var timelineService = TestTimelineService();
|
||||||
|
var timelineOptions = options;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
floatingActionButton: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
FloatingActionButton(
|
||||||
|
heroTag: 'btn1',
|
||||||
|
onPressed: () =>
|
||||||
|
createPost(context, timelineService, timelineOptions),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.edit,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
FloatingActionButton(
|
||||||
|
heroTag: 'btn2',
|
||||||
|
onPressed: () => generatePost(timelineService),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: timeLineNavigatorUserStory(getConfig(timelineService), context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
95
packages/flutter_timeline/example/lib/apps/widgets/app.dart
Normal file
95
packages/flutter_timeline/example/lib/apps/widgets/app.dart
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import 'package:example/config/config.dart';
|
||||||
|
import 'package:example/services/timeline_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline/flutter_timeline.dart';
|
||||||
|
|
||||||
|
class WidgetApp extends StatelessWidget {
|
||||||
|
const WidgetApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'Flutter Timeline',
|
||||||
|
theme: ThemeData(
|
||||||
|
colorScheme:
|
||||||
|
ColorScheme.fromSeed(seedColor: Colors.deepPurple).copyWith(
|
||||||
|
background: const Color(0xFFB8E2E8),
|
||||||
|
),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
home: const MyHomePage(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHomePage extends StatefulWidget {
|
||||||
|
const MyHomePage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyHomePage> createState() => _MyHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
var timelineService = TestTimelineService();
|
||||||
|
var timelineOptions = options;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
floatingActionButton: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
createPost(context, timelineService, timelineOptions);
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
Icons.edit,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
generatePost(timelineService);
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: TimelineScreen(
|
||||||
|
userId: 'test_user',
|
||||||
|
service: timelineService,
|
||||||
|
options: timelineOptions,
|
||||||
|
onPostTap: (post) async {
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => Scaffold(
|
||||||
|
body: TimelinePostScreen(
|
||||||
|
userId: 'test_user',
|
||||||
|
service: timelineService,
|
||||||
|
options: timelineOptions,
|
||||||
|
post: post,
|
||||||
|
onPostDelete: () {
|
||||||
|
timelineService.deletePost(post);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
75
packages/flutter_timeline/example/lib/config/config.dart
Normal file
75
packages/flutter_timeline/example/lib/config/config.dart
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import 'package:example/apps/widgets/screens/post_screen.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline/flutter_timeline.dart';
|
||||||
|
|
||||||
|
TimelineUserStoryConfiguration getConfig(TimelineService service) {
|
||||||
|
return TimelineUserStoryConfiguration(
|
||||||
|
service: service,
|
||||||
|
userService: TestUserService(),
|
||||||
|
userId: 'test_user',
|
||||||
|
optionsBuilder: (context) => options);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = TimelineOptions(
|
||||||
|
textInputBuilder: null,
|
||||||
|
padding: const EdgeInsets.all(20).copyWith(top: 28),
|
||||||
|
allowAllDeletion: true,
|
||||||
|
categoriesBuilder: (context) => [
|
||||||
|
const TimelineCategory(
|
||||||
|
key: null,
|
||||||
|
title: 'All',
|
||||||
|
icon: SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
const TimelineCategory(
|
||||||
|
key: 'category1',
|
||||||
|
title: 'Category 1',
|
||||||
|
icon: SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
const TimelineCategory(
|
||||||
|
key: 'category2',
|
||||||
|
title: 'Category 2',
|
||||||
|
icon: SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
void createPost(BuildContext context, TimelineService service,
|
||||||
|
TimelineOptions options) async {
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => Scaffold(
|
||||||
|
body: TimelinePostCreationScreen(
|
||||||
|
postCategory: null,
|
||||||
|
userId: 'test_user',
|
||||||
|
service: service,
|
||||||
|
options: options,
|
||||||
|
onPostCreated: (post) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generatePost(TimelineService service) {
|
||||||
|
var amountOfPosts = service.getPosts(null).length;
|
||||||
|
|
||||||
|
service.createPost(
|
||||||
|
TimelinePost(
|
||||||
|
id: 'Post$amountOfPosts',
|
||||||
|
creatorId: 'test_user',
|
||||||
|
title: 'Post $amountOfPosts',
|
||||||
|
category: amountOfPosts % 2 == 0 ? 'category1' : 'category2',
|
||||||
|
content: "Post $amountOfPosts content",
|
||||||
|
likes: 0,
|
||||||
|
reaction: 0,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
reactionEnabled: amountOfPosts % 2 == 0 ? false : true,
|
||||||
|
imageUrl: amountOfPosts % 3 != 0
|
||||||
|
? 'https://s3-eu-west-1.amazonaws.com/sortlist-core-api/6qpvvqjtmniirpkvp8eg83bicnc2'
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,126 +1,15 @@
|
||||||
import 'package:example/timeline_service.dart';
|
// import 'package:example/apps/go_router/app.dart';
|
||||||
|
// import 'package:example/apps/navigator/app.dart';
|
||||||
|
import 'package:example/apps/widgets/app.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_timeline/flutter_timeline.dart';
|
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
initializeDateFormatting();
|
initializeDateFormatting();
|
||||||
|
|
||||||
runApp(const MyApp());
|
// Uncomment any, but only one, of these lines to run the example with specific navigation.
|
||||||
}
|
|
||||||
|
runApp(const WidgetApp());
|
||||||
class MyApp extends StatelessWidget {
|
// runApp(const NavigatorApp());
|
||||||
const MyApp({super.key});
|
// runApp(const GoRouterApp());
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MaterialApp(
|
|
||||||
title: 'Flutter Timeline',
|
|
||||||
theme: ThemeData(
|
|
||||||
colorScheme:
|
|
||||||
ColorScheme.fromSeed(seedColor: Colors.deepPurple).copyWith(
|
|
||||||
background: const Color(0xFFB8E2E8),
|
|
||||||
),
|
|
||||||
useMaterial3: true,
|
|
||||||
),
|
|
||||||
home: const MyHomePage(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
|
||||||
const MyHomePage({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
|
||||||
var timelineService = TestTimelineService();
|
|
||||||
var timelineOptions = TimelineOptions(
|
|
||||||
textInputBuilder: null,
|
|
||||||
padding: const EdgeInsets.all(20).copyWith(top: 28),
|
|
||||||
allowAllDeletion: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
floatingActionButton: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
createPost();
|
|
||||||
},
|
|
||||||
child: const Icon(
|
|
||||||
Icons.edit,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
generatePost();
|
|
||||||
},
|
|
||||||
child: const Icon(
|
|
||||||
Icons.add,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: SafeArea(
|
|
||||||
child: TimelineScreen(
|
|
||||||
userId: 'test_user',
|
|
||||||
service: timelineService,
|
|
||||||
options: timelineOptions,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void createPost() async {
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => Scaffold(
|
|
||||||
body: TimelinePostCreationScreen(
|
|
||||||
postCategory: 'text',
|
|
||||||
userId: 'test_user',
|
|
||||||
service: timelineService,
|
|
||||||
options: timelineOptions,
|
|
||||||
onPostCreated: (post) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void generatePost() {
|
|
||||||
var amountOfPosts = timelineService.getPosts('text').length;
|
|
||||||
|
|
||||||
timelineService.createPost(
|
|
||||||
TimelinePost(
|
|
||||||
id: 'Post$amountOfPosts',
|
|
||||||
creatorId: 'test_user',
|
|
||||||
title: 'Post $amountOfPosts',
|
|
||||||
category: 'text',
|
|
||||||
content: "Post $amountOfPosts content",
|
|
||||||
likes: 0,
|
|
||||||
reaction: 0,
|
|
||||||
createdAt: DateTime.now(),
|
|
||||||
reactionEnabled: amountOfPosts % 2 == 0 ? false : true,
|
|
||||||
imageUrl: amountOfPosts % 3 != 0
|
|
||||||
? 'https://s3-eu-west-1.amazonaws.com/sortlist-core-api/6qpvvqjtmniirpkvp8eg83bicnc2'
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ class TestTimelineService with ChangeNotifier implements TimelineService {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<TimelinePost>> fetchPosts(String? category) async {
|
Future<List<TimelinePost>> fetchPosts(String? category) async {
|
||||||
var posts = getMockedPosts();
|
posts = getMockedPosts();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return posts;
|
return posts;
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ class TestTimelineService with ChangeNotifier implements TimelineService {
|
||||||
id: 'Post0',
|
id: 'Post0',
|
||||||
creatorId: 'test_user',
|
creatorId: 'test_user',
|
||||||
title: 'Post 0',
|
title: 'Post 0',
|
||||||
category: 'text',
|
category: null,
|
||||||
content: "Post 0 content",
|
content: "Post 0 content",
|
||||||
likes: 0,
|
likes: 0,
|
||||||
reaction: 0,
|
reaction: 0,
|
|
@ -38,6 +38,7 @@ dependencies:
|
||||||
flutter_timeline:
|
flutter_timeline:
|
||||||
path: ../
|
path: ../
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
|
go_router: ^13.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
// This is a basic Flutter widget test.
|
|
||||||
//
|
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
import 'package:example/main.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
|
||||||
// Build our app and trigger a frame.
|
|
||||||
await tester.pumpWidget(const MyApp());
|
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
|
||||||
expect(find.text('0'), findsOneWidget);
|
|
||||||
expect(find.text('1'), findsNothing);
|
|
||||||
|
|
||||||
// Tap the '+' icon and trigger a frame.
|
|
||||||
await tester.tap(find.byIcon(Icons.add));
|
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
// Verify that our counter has incremented.
|
|
||||||
expect(find.text('0'), findsNothing);
|
|
||||||
expect(find.text('1'), findsOneWidget);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@
|
||||||
/// Flutter Timeline library
|
/// Flutter Timeline library
|
||||||
library flutter_timeline;
|
library flutter_timeline;
|
||||||
|
|
||||||
|
export 'package:flutter_timeline/src/flutter_timeline_navigator_userstory.dart';
|
||||||
export 'package:flutter_timeline/src/flutter_timeline_userstory.dart';
|
export 'package:flutter_timeline/src/flutter_timeline_userstory.dart';
|
||||||
export 'package:flutter_timeline/src/models/timeline_configuration.dart';
|
export 'package:flutter_timeline/src/models/timeline_configuration.dart';
|
||||||
export 'package:flutter_timeline/src/routes.dart';
|
export 'package:flutter_timeline/src/routes.dart';
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline/flutter_timeline.dart';
|
||||||
|
|
||||||
|
Widget timeLineNavigatorUserStory(
|
||||||
|
TimelineUserStoryConfiguration configuration,
|
||||||
|
BuildContext context,
|
||||||
|
) =>
|
||||||
|
_timelineScreenRoute(configuration, context);
|
||||||
|
|
||||||
|
Widget _timelineScreenRoute(
|
||||||
|
TimelineUserStoryConfiguration configuration,
|
||||||
|
BuildContext context,
|
||||||
|
) =>
|
||||||
|
TimelineScreen(
|
||||||
|
service: configuration.service,
|
||||||
|
options: configuration.optionsBuilder(context),
|
||||||
|
userId: configuration.userId,
|
||||||
|
onPostTap: (post) async =>
|
||||||
|
configuration.onPostTap?.call(context, post) ??
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
_postDetailScreenRoute(configuration, context, post),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onUserTap: (userId) {
|
||||||
|
configuration.onUserTap?.call(context, userId);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _postDetailScreenRoute(
|
||||||
|
TimelineUserStoryConfiguration configuration,
|
||||||
|
BuildContext context,
|
||||||
|
TimelinePost post,
|
||||||
|
) =>
|
||||||
|
TimelinePostScreen(
|
||||||
|
userId: configuration.userId,
|
||||||
|
service: configuration.service,
|
||||||
|
options: configuration.optionsBuilder(context),
|
||||||
|
post: post,
|
||||||
|
onPostDelete: () async {
|
||||||
|
configuration.onPostDelete?.call(context, post) ??
|
||||||
|
await configuration.service.deletePost(post);
|
||||||
|
},
|
||||||
|
);
|
|
@ -22,12 +22,16 @@ List<GoRoute> getTimelineStoryRoutes(
|
||||||
service: configuration.service,
|
service: configuration.service,
|
||||||
options: configuration.optionsBuilder(context),
|
options: configuration.optionsBuilder(context),
|
||||||
onPostTap: (post) async =>
|
onPostTap: (post) async =>
|
||||||
TimelineUserStoryRoutes.timelineViewPath(post.id),
|
configuration.onPostTap?.call(context, post) ??
|
||||||
|
await context.push(
|
||||||
|
TimelineUserStoryRoutes.timelineViewPath(post.id),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
child: configuration.mainPageBuilder?.call(
|
child: configuration.openPageBuilder?.call(
|
||||||
context,
|
context,
|
||||||
timelineScreen,
|
timelineScreen,
|
||||||
) ??
|
) ??
|
||||||
|
@ -37,73 +41,27 @@ List<GoRoute> getTimelineStoryRoutes(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
path: TimelineUserStoryRoutes.timelineSelect,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
var timelineSelectionWidget = TimelineSelectionScreen(
|
|
||||||
options: configuration.optionsBuilder(context),
|
|
||||||
categories: configuration.categoriesBuilder(context),
|
|
||||||
onCategorySelected: (category) async => context.push(
|
|
||||||
TimelineUserStoryRoutes.timelineCreatePath(category.name),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return buildScreenWithoutTransition(
|
|
||||||
context: context,
|
|
||||||
state: state,
|
|
||||||
child: configuration.postSelectionScreenBuilder?.call(
|
|
||||||
context,
|
|
||||||
timelineSelectionWidget,
|
|
||||||
) ??
|
|
||||||
Scaffold(
|
|
||||||
body: timelineSelectionWidget,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: TimelineUserStoryRoutes.timelineCreate,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
var timelineCreateWidget = TimelinePostCreationScreen(
|
|
||||||
userId: configuration.userId,
|
|
||||||
options: configuration.optionsBuilder(context),
|
|
||||||
postCategory: state.pathParameters['category'] ?? '',
|
|
||||||
service: configuration.service,
|
|
||||||
onPostCreated: (post) => context.go(
|
|
||||||
TimelineUserStoryRoutes.timelineViewPath(post.id),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return buildScreenWithoutTransition(
|
|
||||||
context: context,
|
|
||||||
state: state,
|
|
||||||
child: configuration.postCreationScreenBuilder?.call(
|
|
||||||
context,
|
|
||||||
timelineCreateWidget,
|
|
||||||
) ??
|
|
||||||
Scaffold(
|
|
||||||
body: timelineCreateWidget,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: TimelineUserStoryRoutes.timelineView,
|
path: TimelineUserStoryRoutes.timelineView,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
|
var post =
|
||||||
|
configuration.service.getPost(state.pathParameters['post']!)!;
|
||||||
|
|
||||||
var timelinePostWidget = TimelinePostScreen(
|
var timelinePostWidget = TimelinePostScreen(
|
||||||
userId: configuration.userId,
|
userId: configuration.userId,
|
||||||
options: configuration.optionsBuilder(context),
|
options: configuration.optionsBuilder(context),
|
||||||
service: configuration.service,
|
service: configuration.service,
|
||||||
post: configuration.service.getPost(state.pathParameters['post']!)!,
|
post: post,
|
||||||
onPostDelete: () => context.pop(),
|
onPostDelete: () => configuration.onPostDelete?.call(context, post),
|
||||||
onUserTap: (user) => configuration.onUserTap?.call(context, user),
|
onUserTap: (user) => configuration.onUserTap?.call(context, user),
|
||||||
);
|
);
|
||||||
var category = configuration.categoriesBuilder(context).first;
|
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
child: configuration.postScreenBuilder?.call(
|
child: configuration.openPageBuilder?.call(
|
||||||
context,
|
context,
|
||||||
timelinePostWidget,
|
timelinePostWidget,
|
||||||
category,
|
|
||||||
) ??
|
) ??
|
||||||
Scaffold(
|
Scaffold(
|
||||||
body: timelinePostWidget,
|
body: timelinePostWidget,
|
||||||
|
|
|
@ -9,42 +9,29 @@ import 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
||||||
@immutable
|
@immutable
|
||||||
class TimelineUserStoryConfiguration {
|
class TimelineUserStoryConfiguration {
|
||||||
const TimelineUserStoryConfiguration({
|
const TimelineUserStoryConfiguration({
|
||||||
required this.categoriesBuilder,
|
|
||||||
required this.optionsBuilder,
|
|
||||||
required this.userId,
|
required this.userId,
|
||||||
required this.service,
|
required this.service,
|
||||||
required this.userService,
|
required this.userService,
|
||||||
this.mainPageBuilder,
|
required this.optionsBuilder,
|
||||||
this.postScreenBuilder,
|
this.openPageBuilder,
|
||||||
this.postCreationScreenBuilder,
|
this.onPostTap,
|
||||||
this.postSelectionScreenBuilder,
|
|
||||||
this.onUserTap,
|
this.onUserTap,
|
||||||
|
this.onPostDelete,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String userId;
|
final String userId;
|
||||||
|
|
||||||
final Function(BuildContext context, String userId)? onUserTap;
|
|
||||||
|
|
||||||
final Widget Function(BuildContext context, Widget child)?
|
|
||||||
mainPageBuilder;
|
|
||||||
|
|
||||||
final Widget Function(
|
|
||||||
BuildContext context,
|
|
||||||
Widget child,
|
|
||||||
TimelineCategory category,
|
|
||||||
)? postScreenBuilder;
|
|
||||||
|
|
||||||
final Widget Function(BuildContext context, Widget child)?
|
|
||||||
postCreationScreenBuilder;
|
|
||||||
|
|
||||||
final Widget Function(BuildContext context, Widget child)?
|
|
||||||
postSelectionScreenBuilder;
|
|
||||||
|
|
||||||
final TimelineService service;
|
final TimelineService service;
|
||||||
|
|
||||||
final TimelineUserService userService;
|
final TimelineUserService userService;
|
||||||
|
|
||||||
final TimelineOptions Function(BuildContext context) optionsBuilder;
|
final TimelineOptions Function(BuildContext context) optionsBuilder;
|
||||||
|
|
||||||
final List<TimelineCategory> Function(BuildContext context) categoriesBuilder;
|
final Function(BuildContext context, String userId)? onUserTap;
|
||||||
|
|
||||||
|
final Function(BuildContext context, Widget child)? openPageBuilder;
|
||||||
|
|
||||||
|
final Function(BuildContext context, TimelinePost post)? onPostTap;
|
||||||
|
|
||||||
|
final Widget Function(BuildContext context, TimelinePost post)? onPostDelete;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
|
|
||||||
mixin TimelineUserStoryRoutes {
|
mixin TimelineUserStoryRoutes {
|
||||||
static const String timelineHome = '/timeline';
|
static const String timelineHome = '/timeline';
|
||||||
static const String timelineCreate = '/timeline-create/:category';
|
|
||||||
static String timelineCreatePath(String category) =>
|
|
||||||
'/timeline-create/$category';
|
|
||||||
static const String timelineSelect = '/timeline-select';
|
|
||||||
static const String timelineView = '/timeline-view/:post';
|
static const String timelineView = '/timeline-view/:post';
|
||||||
static String timelineViewPath(String postId) => '/timeline-view/$postId';
|
static String timelineViewPath(String postId) => '/timeline-view/$postId';
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ import 'package:flutter/material.dart';
|
||||||
@immutable
|
@immutable
|
||||||
class TimelineCategory {
|
class TimelineCategory {
|
||||||
const TimelineCategory({
|
const TimelineCategory({
|
||||||
required this.name,
|
required this.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
this.canCreate = true,
|
this.canCreate = true,
|
||||||
this.canView = true,
|
this.canView = true,
|
||||||
});
|
});
|
||||||
final String name;
|
final String? key;
|
||||||
final String title;
|
final String title;
|
||||||
final Widget icon;
|
final Widget icon;
|
||||||
final bool canCreate;
|
final bool canCreate;
|
||||||
|
|
|
@ -15,12 +15,12 @@ class TimelinePost {
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.creatorId,
|
required this.creatorId,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.category,
|
|
||||||
required this.content,
|
required this.content,
|
||||||
required this.likes,
|
required this.likes,
|
||||||
required this.reaction,
|
required this.reaction,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.reactionEnabled,
|
required this.reactionEnabled,
|
||||||
|
this.category,
|
||||||
this.creator,
|
this.creator,
|
||||||
this.likedBy,
|
this.likedBy,
|
||||||
this.reactions,
|
this.reactions,
|
||||||
|
@ -67,7 +67,7 @@ class TimelinePost {
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
/// The category of the post on which can be filtered.
|
/// The category of the post on which can be filtered.
|
||||||
final String category;
|
final String? category;
|
||||||
|
|
||||||
/// The url of the image of the post.
|
/// The url of the image of the post.
|
||||||
final String? imageUrl;
|
final String? imageUrl;
|
||||||
|
|
|
@ -12,4 +12,6 @@ export 'src/screens/timeline_post_creation_screen.dart';
|
||||||
export 'src/screens/timeline_post_screen.dart';
|
export 'src/screens/timeline_post_screen.dart';
|
||||||
export 'src/screens/timeline_screen.dart';
|
export 'src/screens/timeline_screen.dart';
|
||||||
export 'src/screens/timeline_selection_screen.dart';
|
export 'src/screens/timeline_selection_screen.dart';
|
||||||
|
export 'src/widgets/category_selector.dart';
|
||||||
|
export 'src/widgets/category_selector_button.dart';
|
||||||
export 'src/widgets/timeline_post_widget.dart';
|
export 'src/widgets/timeline_post_widget.dart';
|
||||||
|
|
|
@ -40,13 +40,13 @@ 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.categories,
|
this.categoriesBuilder,
|
||||||
this.categoryButtonBuilder,
|
this.categoryButtonBuilder,
|
||||||
this.catergoryLabelBuilder,
|
|
||||||
this.categorySelectorHorizontalPadding,
|
this.categorySelectorHorizontalPadding,
|
||||||
this.filterEnabled = false,
|
this.filterEnabled = false,
|
||||||
this.initialFilterWord,
|
this.initialFilterWord,
|
||||||
this.searchBarBuilder,
|
this.searchBarBuilder,
|
||||||
|
this.postWidget,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Theming options for the timeline
|
/// Theming options for the timeline
|
||||||
|
@ -122,7 +122,8 @@ class TimelineOptions {
|
||||||
|
|
||||||
/// 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<String>? categories;
|
final List<TimelineCategory> Function(BuildContext context)?
|
||||||
|
categoriesBuilder;
|
||||||
|
|
||||||
/// Abilty to override the standard category selector
|
/// Abilty to override the standard category selector
|
||||||
final Widget Function({
|
final Widget Function({
|
||||||
|
@ -132,10 +133,6 @@ class TimelineOptions {
|
||||||
required bool selected,
|
required bool selected,
|
||||||
})? categoryButtonBuilder;
|
})? categoryButtonBuilder;
|
||||||
|
|
||||||
/// Ability to set an proper label for the category selectors.
|
|
||||||
/// Default to category key.
|
|
||||||
final String Function(String? categoryKey)? catergoryLabelBuilder;
|
|
||||||
|
|
||||||
/// 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;
|
||||||
|
|
||||||
|
@ -152,6 +149,9 @@ class TimelineOptions {
|
||||||
Map<String, dynamic> options,
|
Map<String, dynamic> options,
|
||||||
) search,
|
) search,
|
||||||
)? searchBarBuilder;
|
)? searchBarBuilder;
|
||||||
|
|
||||||
|
/// Override the standard postwidget
|
||||||
|
final Widget Function(TimelinePost post)? postWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ButtonBuilder = Widget Function(
|
typedef ButtonBuilder = Widget Function(
|
||||||
|
|
|
@ -13,16 +13,16 @@ import 'package:flutter_timeline_view/src/config/timeline_options.dart';
|
||||||
class TimelinePostCreationScreen extends StatefulWidget {
|
class TimelinePostCreationScreen extends StatefulWidget {
|
||||||
const TimelinePostCreationScreen({
|
const TimelinePostCreationScreen({
|
||||||
required this.userId,
|
required this.userId,
|
||||||
required this.postCategory,
|
|
||||||
required this.onPostCreated,
|
required this.onPostCreated,
|
||||||
required this.service,
|
required this.service,
|
||||||
required this.options,
|
required this.options,
|
||||||
|
this.postCategory,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String userId;
|
final String userId;
|
||||||
|
|
||||||
final String postCategory;
|
final String? postCategory;
|
||||||
|
|
||||||
/// called when the post is created
|
/// called when the post is created
|
||||||
final Function(TimelinePost) onPostCreated;
|
final Function(TimelinePost) onPostCreated;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import 'package:flutter_timeline_view/src/widgets/reaction_bottom.dart';
|
||||||
import 'package:flutter_timeline_view/src/widgets/tappable_image.dart';
|
import 'package:flutter_timeline_view/src/widgets/tappable_image.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class TimelinePostScreen extends StatefulWidget {
|
class TimelinePostScreen extends StatelessWidget {
|
||||||
const TimelinePostScreen({
|
const TimelinePostScreen({
|
||||||
required this.userId,
|
required this.userId,
|
||||||
required this.service,
|
required this.service,
|
||||||
|
@ -44,10 +44,45 @@ class TimelinePostScreen extends StatefulWidget {
|
||||||
final VoidCallback onPostDelete;
|
final VoidCallback onPostDelete;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TimelinePostScreen> createState() => _TimelinePostScreenState();
|
Widget build(BuildContext context) => Scaffold(
|
||||||
|
body: _TimelinePostScreen(
|
||||||
|
userId: userId,
|
||||||
|
service: service,
|
||||||
|
options: options,
|
||||||
|
post: post,
|
||||||
|
onPostDelete: onPostDelete,
|
||||||
|
onUserTap: onUserTap,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
class _TimelinePostScreen extends StatefulWidget {
|
||||||
|
const _TimelinePostScreen({
|
||||||
|
required this.userId,
|
||||||
|
required this.service,
|
||||||
|
required this.options,
|
||||||
|
required this.post,
|
||||||
|
required this.onPostDelete,
|
||||||
|
this.onUserTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String userId;
|
||||||
|
|
||||||
|
final TimelineService service;
|
||||||
|
|
||||||
|
final TimelineOptions options;
|
||||||
|
|
||||||
|
final TimelinePost post;
|
||||||
|
|
||||||
|
final Function(String userId)? onUserTap;
|
||||||
|
|
||||||
|
final VoidCallback onPostDelete;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_TimelinePostScreen> createState() => _TimelinePostScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
TimelinePost? post;
|
TimelinePost? post;
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
|
|
||||||
|
@ -96,8 +131,9 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
var dateFormat = widget.options.dateFormat ??
|
var dateFormat = widget.options.dateFormat ??
|
||||||
DateFormat('dd/MM/yyyy', Localizations.localeOf(context).languageCode);
|
DateFormat('dd/MM/yyyy', Localizations.localeOf(context).languageCode);
|
||||||
var timeFormat = widget.options.timeFormat ?? DateFormat('HH:mm');
|
var timeFormat = widget.options.timeFormat ?? DateFormat('HH:mm');
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return const Center(
|
const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -185,12 +221,7 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
if (widget.options.allowAllDeletion ||
|
if (widget.options.allowAllDeletion ||
|
||||||
post.creator?.userId == widget.userId)
|
post.creator?.userId == widget.userId)
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
onSelected: (value) async {
|
onSelected: (value) => widget.onPostDelete(),
|
||||||
if (value == 'delete') {
|
|
||||||
await widget.service.deletePost(post);
|
|
||||||
widget.onPostDelete();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) =>
|
||||||
<PopupMenuEntry<String>>[
|
<PopupMenuEntry<String>>[
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
|
|
|
@ -7,19 +7,17 @@ import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
import 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
import 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
||||||
import 'package:flutter_timeline_view/src/widgets/category_selector.dart';
|
|
||||||
|
|
||||||
class TimelineScreen extends StatefulWidget {
|
class TimelineScreen extends StatefulWidget {
|
||||||
const TimelineScreen({
|
const TimelineScreen({
|
||||||
required this.userId,
|
required this.userId,
|
||||||
required this.service,
|
required this.service,
|
||||||
required this.options,
|
required this.options,
|
||||||
|
required this.onPostTap,
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
this.onPostTap,
|
|
||||||
this.onUserTap,
|
this.onUserTap,
|
||||||
this.posts,
|
this.posts,
|
||||||
this.timelineCategory,
|
this.timelineCategory,
|
||||||
this.postWidget,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,14 +41,11 @@ class TimelineScreen extends StatefulWidget {
|
||||||
final List<TimelinePost>? posts;
|
final List<TimelinePost>? posts;
|
||||||
|
|
||||||
/// Called when a post is tapped
|
/// Called when a post is tapped
|
||||||
final Function(TimelinePost)? onPostTap;
|
final Function(TimelinePost) onPostTap;
|
||||||
|
|
||||||
/// 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)? postWidget;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TimelineScreen> createState() => _TimelineScreenState();
|
State<TimelineScreen> createState() => _TimelineScreenState();
|
||||||
}
|
}
|
||||||
|
@ -196,35 +191,13 @@ class _TimelineScreenState extends State<TimelineScreen> {
|
||||||
...posts.map(
|
...posts.map(
|
||||||
(post) => Padding(
|
(post) => Padding(
|
||||||
padding: widget.options.postPadding,
|
padding: widget.options.postPadding,
|
||||||
child: widget.postWidget?.call(post) ??
|
child: widget.options.postWidget?.call(post) ??
|
||||||
TimelinePostWidget(
|
TimelinePostWidget(
|
||||||
service: widget.service,
|
service: widget.service,
|
||||||
userId: widget.userId,
|
userId: widget.userId,
|
||||||
options: widget.options,
|
options: widget.options,
|
||||||
post: post,
|
post: post,
|
||||||
onTap: () async {
|
onTap: () => widget.onPostTap(post),
|
||||||
if (widget.onPostTap != null) {
|
|
||||||
widget.onPostTap!.call(post);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => Scaffold(
|
|
||||||
body: TimelinePostScreen(
|
|
||||||
userId: widget.userId,
|
|
||||||
service: widget.service,
|
|
||||||
options: widget.options,
|
|
||||||
post: post,
|
|
||||||
onPostDelete: () {
|
|
||||||
widget.service.deletePost(post);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onTapLike: () async =>
|
onTapLike: () async =>
|
||||||
service.likePost(widget.userId, post),
|
service.likePost(widget.userId, post),
|
||||||
onTapUnlike: () async =>
|
onTapUnlike: () async =>
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
import 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
||||||
import 'package:flutter_timeline_view/src/widgets/category_selector_button.dart';
|
|
||||||
|
|
||||||
class CategorySelector extends StatelessWidget {
|
class CategorySelector extends StatelessWidget {
|
||||||
const CategorySelector({
|
const CategorySelector({
|
||||||
|
@ -18,10 +17,12 @@ class CategorySelector extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (options.categories == null) {
|
if (options.categoriesBuilder == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var categories = options.categoriesBuilder!(context);
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -30,37 +31,19 @@ class CategorySelector extends StatelessWidget {
|
||||||
width: options.categorySelectorHorizontalPadding ??
|
width: options.categorySelectorHorizontalPadding ??
|
||||||
max(options.padding.horizontal - 4, 0),
|
max(options.padding.horizontal - 4, 0),
|
||||||
),
|
),
|
||||||
options.categoryButtonBuilder?.call(
|
for (var category in categories) ...[
|
||||||
categoryKey: null,
|
|
||||||
categoryName:
|
|
||||||
options.catergoryLabelBuilder?.call(null) ?? 'All',
|
|
||||||
onTap: () => onTapCategory(null),
|
|
||||||
selected: filter == null,
|
|
||||||
) ??
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
||||||
child: CategorySelectorButton(
|
|
||||||
category: null,
|
|
||||||
selected: filter == null,
|
|
||||||
onTap: () => onTapCategory(null),
|
|
||||||
labelBuilder: options.catergoryLabelBuilder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
for (var category in options.categories!) ...[
|
|
||||||
options.categoryButtonBuilder?.call(
|
options.categoryButtonBuilder?.call(
|
||||||
categoryKey: category,
|
categoryKey: category.key,
|
||||||
categoryName:
|
categoryName: category.title,
|
||||||
options.catergoryLabelBuilder?.call(category) ?? category,
|
onTap: () => onTapCategory(category.key),
|
||||||
onTap: () => onTapCategory(category),
|
selected: filter == category.key,
|
||||||
selected: filter == category,
|
|
||||||
) ??
|
) ??
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
child: CategorySelectorButton(
|
child: CategorySelectorButton(
|
||||||
category: category,
|
category: category,
|
||||||
selected: filter == category,
|
selected: filter == category.key,
|
||||||
onTap: () => onTapCategory(category),
|
onTap: () => onTapCategory(category.key),
|
||||||
labelBuilder: options.catergoryLabelBuilder,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
|
|
||||||
class CategorySelectorButton extends StatelessWidget {
|
class CategorySelectorButton extends StatelessWidget {
|
||||||
const CategorySelectorButton({
|
const CategorySelectorButton({
|
||||||
required this.category,
|
required this.category,
|
||||||
required this.selected,
|
required this.selected,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
this.labelBuilder,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? category;
|
final TimelineCategory category;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final String Function(String? category)? labelBuilder;
|
|
||||||
final void Function() onTap;
|
final void Function() onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -41,7 +40,7 @@ class CategorySelectorButton extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
labelBuilder?.call(category) ?? category ?? 'All',
|
category.title,
|
||||||
style: theme.textTheme.labelMedium?.copyWith(
|
style: theme.textTheme.labelMedium?.copyWith(
|
||||||
color: selected
|
color: selected
|
||||||
? theme.colorScheme.onPrimary
|
? theme.colorScheme.onPrimary
|
||||||
|
|
Loading…
Reference in a new issue