mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 02:23:46 +02:00
feat: add timeline selection screen
This commit is contained in:
parent
0035e1f4fb
commit
e523f52118
14 changed files with 187 additions and 63 deletions
|
@ -1,9 +1,12 @@
|
||||||
// SPDX-FileCopyrightText: 2023 Iconica
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
/// Flutter Timeline library
|
/// Flutter Timeline library
|
||||||
library flutter_timeline;
|
library flutter_timeline;
|
||||||
|
|
||||||
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/routes.dart';
|
||||||
export 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
export 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
export 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
export 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
||||||
|
|
|
@ -1,50 +1,14 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_timeline/flutter_timeline.dart';
|
|
||||||
import 'package:flutter_timeline/src/go_router.dart';
|
import 'package:flutter_timeline/src/go_router.dart';
|
||||||
|
import 'package:flutter_timeline/src/models/timeline_configuration.dart';
|
||||||
|
import 'package:flutter_timeline/src/routes.dart';
|
||||||
|
import 'package:flutter_timeline_view/flutter_timeline_view.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
mixin TimelineUserStoryRoutes {
|
|
||||||
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 String timelineViewPath(String postId) => '/timeline-view/$postId';
|
|
||||||
}
|
|
||||||
|
|
||||||
class TimelineUserStoryConfiguration {
|
|
||||||
const TimelineUserStoryConfiguration({
|
|
||||||
required this.optionsBuilder,
|
|
||||||
required this.userId,
|
|
||||||
required this.service,
|
|
||||||
required this.userService,
|
|
||||||
this.mainPageBuilder,
|
|
||||||
this.postScreenBuilder,
|
|
||||||
this.postCreationScreenBuilder,
|
|
||||||
this.postSelectionScreenBuilder,
|
|
||||||
this.onUserTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String userId;
|
|
||||||
|
|
||||||
final Function(String userId)? onUserTap;
|
|
||||||
|
|
||||||
final Widget Function(Widget filterBar, Widget child)? mainPageBuilder;
|
|
||||||
|
|
||||||
final Widget Function(Widget child)? postScreenBuilder;
|
|
||||||
|
|
||||||
final Widget Function(Widget child)? postCreationScreenBuilder;
|
|
||||||
|
|
||||||
final Widget Function(Widget child)? postSelectionScreenBuilder;
|
|
||||||
|
|
||||||
final TimelineService service;
|
|
||||||
|
|
||||||
final TimelineUserService userService;
|
|
||||||
|
|
||||||
final TimelineOptions Function(BuildContext context) optionsBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<GoRoute> getTimelineStoryRoutes(
|
List<GoRoute> getTimelineStoryRoutes(
|
||||||
TimelineUserStoryConfiguration configuration,
|
TimelineUserStoryConfiguration configuration,
|
||||||
) =>
|
) =>
|
||||||
|
@ -52,20 +16,23 @@ List<GoRoute> getTimelineStoryRoutes(
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: TimelineUserStoryRoutes.timelineHome,
|
path: TimelineUserStoryRoutes.timelineHome,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
|
var timelineFilter =
|
||||||
|
Container(); // TODO(anyone): create a filter widget
|
||||||
var timelineScreen = TimelineScreen(
|
var timelineScreen = TimelineScreen(
|
||||||
userId: configuration.userId,
|
userId: configuration.userId,
|
||||||
onUserTap: configuration.onUserTap,
|
onUserTap: (user) => configuration.onUserTap?.call(context, user),
|
||||||
service: configuration.service,
|
service: configuration.service,
|
||||||
options: configuration.optionsBuilder(context),
|
options: configuration.optionsBuilder(context),
|
||||||
onPostTap: (post) async =>
|
onPostTap: (post) async =>
|
||||||
TimelineUserStoryRoutes.timelineViewPath(post.id),
|
TimelineUserStoryRoutes.timelineViewPath(post.id),
|
||||||
timelineCategoryFilter: 'news',
|
timelineCategoryFilter: null,
|
||||||
);
|
);
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
child: configuration.mainPageBuilder?.call(
|
child: configuration.mainPageBuilder?.call(
|
||||||
Container(), // TODO(anyone): create a selection widget
|
context,
|
||||||
|
timelineFilter,
|
||||||
timelineScreen,
|
timelineScreen,
|
||||||
) ??
|
) ??
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
@ -77,12 +44,18 @@ List<GoRoute> getTimelineStoryRoutes(
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: TimelineUserStoryRoutes.timelineSelect,
|
path: TimelineUserStoryRoutes.timelineSelect,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
var timelineSelectionWidget =
|
var timelineSelectionWidget = TimelineSelectionScreen(
|
||||||
Container(); // TODO(anyone): create timeline selection screen
|
options: configuration.optionsBuilder(context),
|
||||||
|
categories: configuration.categoriesBuilder(context),
|
||||||
|
onCategorySelected: (category) async => context.push(
|
||||||
|
TimelineUserStoryRoutes.timelineCreatePath(category.name),
|
||||||
|
),
|
||||||
|
);
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
child: configuration.postSelectionScreenBuilder?.call(
|
child: configuration.postSelectionScreenBuilder?.call(
|
||||||
|
context,
|
||||||
timelineSelectionWidget,
|
timelineSelectionWidget,
|
||||||
) ??
|
) ??
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
@ -106,8 +79,10 @@ List<GoRoute> getTimelineStoryRoutes(
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
child: configuration.postCreationScreenBuilder
|
child: configuration.postCreationScreenBuilder?.call(
|
||||||
?.call(timelineCreateWidget) ??
|
context,
|
||||||
|
timelineCreateWidget,
|
||||||
|
) ??
|
||||||
Scaffold(
|
Scaffold(
|
||||||
body: timelineCreateWidget,
|
body: timelineCreateWidget,
|
||||||
),
|
),
|
||||||
|
@ -123,16 +98,17 @@ List<GoRoute> getTimelineStoryRoutes(
|
||||||
service: configuration.service,
|
service: configuration.service,
|
||||||
userService: configuration.userService,
|
userService: configuration.userService,
|
||||||
post: configuration.service.getPost(state.pathParameters['post']!)!,
|
post: configuration.service.getPost(state.pathParameters['post']!)!,
|
||||||
onPostDelete: () => context.go(
|
onPostDelete: () => context.pop(),
|
||||||
TimelineUserStoryRoutes.timelineHome,
|
onUserTap: (user) => configuration.onUserTap?.call(context, user),
|
||||||
),
|
|
||||||
onUserTap: configuration.onUserTap,
|
|
||||||
);
|
);
|
||||||
|
var category = configuration.categoriesBuilder(context).first;
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
child: configuration.postScreenBuilder?.call(
|
child: configuration.postScreenBuilder?.call(
|
||||||
|
context,
|
||||||
timelinePostWidget,
|
timelinePostWidget,
|
||||||
|
category,
|
||||||
) ??
|
) ??
|
||||||
Scaffold(
|
Scaffold(
|
||||||
body: timelinePostWidget,
|
body: timelinePostWidget,
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 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';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class TimelineUserStoryConfiguration {
|
||||||
|
const TimelineUserStoryConfiguration({
|
||||||
|
required this.categoriesBuilder,
|
||||||
|
required this.optionsBuilder,
|
||||||
|
required this.userId,
|
||||||
|
required this.service,
|
||||||
|
required this.userService,
|
||||||
|
this.mainPageBuilder,
|
||||||
|
this.postScreenBuilder,
|
||||||
|
this.postCreationScreenBuilder,
|
||||||
|
this.postSelectionScreenBuilder,
|
||||||
|
this.onUserTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String userId;
|
||||||
|
|
||||||
|
final Function(BuildContext context, String userId)? onUserTap;
|
||||||
|
|
||||||
|
final Widget Function(BuildContext context, Widget filterBar, 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 TimelineUserService userService;
|
||||||
|
|
||||||
|
final TimelineOptions Function(BuildContext context) optionsBuilder;
|
||||||
|
|
||||||
|
final List<TimelineCategory> Function(BuildContext context) categoriesBuilder;
|
||||||
|
}
|
13
packages/flutter_timeline/lib/src/routes.dart
Normal file
13
packages/flutter_timeline/lib/src/routes.dart
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
mixin TimelineUserStoryRoutes {
|
||||||
|
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 String timelineViewPath(String postId) => '/timeline-view/$postId';
|
||||||
|
}
|
|
@ -16,12 +16,12 @@ dependencies:
|
||||||
go_router: ^12.1.1
|
go_router: ^12.1.1
|
||||||
flutter_timeline_view:
|
flutter_timeline_view:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_timeline.git
|
url: https://github.com/Iconica-Development/flutter_timeline
|
||||||
path: packages/flutter_timeline_view
|
path: packages/flutter_timeline_view
|
||||||
ref: 1.0.0
|
ref: 1.0.0
|
||||||
flutter_timeline_interface:
|
flutter_timeline_interface:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_timeline.git
|
url: https://github.com/Iconica-Development/flutter_timeline
|
||||||
path: packages/flutter_timeline_interface
|
path: packages/flutter_timeline_interface
|
||||||
ref: 1.0.0
|
ref: 1.0.0
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ dependencies:
|
||||||
|
|
||||||
flutter_timeline_interface:
|
flutter_timeline_interface:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_timeline.git
|
url: https://github.com/Iconica-Development/flutter_timeline
|
||||||
path: packages/flutter_timeline_interface
|
path: packages/flutter_timeline_interface
|
||||||
ref: 1.0.0
|
ref: 1.0.0
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
///
|
///
|
||||||
library flutter_timeline_interface;
|
library flutter_timeline_interface;
|
||||||
|
|
||||||
|
export 'src/model/timeline_category.dart';
|
||||||
export 'src/model/timeline_post.dart';
|
export 'src/model/timeline_post.dart';
|
||||||
export 'src/model/timeline_poster.dart';
|
export 'src/model/timeline_poster.dart';
|
||||||
export 'src/model/timeline_reaction.dart';
|
export 'src/model/timeline_reaction.dart';
|
||||||
|
|
||||||
export 'src/services/timeline_service.dart';
|
export 'src/services/timeline_service.dart';
|
||||||
export 'src/services/user_service.dart';
|
export 'src/services/user_service.dart';
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class TimelineCategory {
|
||||||
|
const TimelineCategory({
|
||||||
|
required this.name,
|
||||||
|
required this.title,
|
||||||
|
required this.icon,
|
||||||
|
this.canCreate = true,
|
||||||
|
this.canView = true,
|
||||||
|
});
|
||||||
|
final String name;
|
||||||
|
final String title;
|
||||||
|
final Widget icon;
|
||||||
|
final bool canCreate;
|
||||||
|
final bool canView;
|
||||||
|
}
|
|
@ -14,10 +14,6 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_data_interface:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_data_interface.git
|
|
||||||
ref: 1.0.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
|
|
@ -10,4 +10,5 @@ export 'src/config/timeline_translations.dart';
|
||||||
export 'src/screens/timeline_post_creation_screen.dart';
|
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/widgets/timeline_post_widget.dart';
|
export 'src/widgets/timeline_post_widget.dart';
|
||||||
|
|
|
@ -27,6 +27,7 @@ class TimelineTranslations {
|
||||||
this.writeComment = 'Write your comment here...',
|
this.writeComment = 'Write your comment here...',
|
||||||
this.postAt = 'at',
|
this.postAt = 'at',
|
||||||
this.postLoadingError = 'Something went wrong while loading the post',
|
this.postLoadingError = 'Something went wrong while loading the post',
|
||||||
|
this.timelineSelectionDescription = 'Choose a category',
|
||||||
});
|
});
|
||||||
|
|
||||||
final String noPosts;
|
final String noPosts;
|
||||||
|
@ -50,4 +51,6 @@ class TimelineTranslations {
|
||||||
final String writeComment;
|
final String writeComment;
|
||||||
final String firstComment;
|
final String firstComment;
|
||||||
final String postLoadingError;
|
final String postLoadingError;
|
||||||
|
|
||||||
|
final String timelineSelectionDescription;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
|
import 'package:flutter_timeline_view/src/config/timeline_options.dart';
|
||||||
|
|
||||||
|
class TimelineSelectionScreen extends StatelessWidget {
|
||||||
|
const TimelineSelectionScreen({
|
||||||
|
required this.options,
|
||||||
|
required this.categories,
|
||||||
|
required this.onCategorySelected,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<TimelineCategory> categories;
|
||||||
|
|
||||||
|
final TimelineOptions options;
|
||||||
|
|
||||||
|
final Function(TimelineCategory) onCategorySelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var size = MediaQuery.of(context).size;
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: size.width * 0.05,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: size.height * 0.05, bottom: 8),
|
||||||
|
child: Text(
|
||||||
|
options.translations.timelineSelectionDescription,
|
||||||
|
style: theme.textTheme.displayMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
for (var category in categories.where(
|
||||||
|
(element) => element.canCreate,
|
||||||
|
)) ...[
|
||||||
|
InkWell(
|
||||||
|
onTap: () => onCategorySelected.call(category),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 26,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Text(
|
||||||
|
category.title,
|
||||||
|
style: theme.textTheme.displaySmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ dependencies:
|
||||||
|
|
||||||
flutter_timeline_interface:
|
flutter_timeline_interface:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_timeline.git
|
url: https://github.com/Iconica-Development/flutter_timeline
|
||||||
path: packages/flutter_timeline_interface
|
path: packages/flutter_timeline_interface
|
||||||
ref: 1.0.0
|
ref: 1.0.0
|
||||||
flutter_image_picker:
|
flutter_image_picker:
|
||||||
|
|
Loading…
Reference in a new issue