mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 02:23:46 +02:00
Merge pull request #39 from Iconica-Development/feat/implement-figma-design
feat: implement figma designs
This commit is contained in:
commit
7b76a8d956
20 changed files with 365 additions and 144 deletions
|
@ -25,9 +25,14 @@ class GoRouterApp extends StatelessWidget {
|
||||||
routerConfig: _router,
|
routerConfig: _router,
|
||||||
title: 'Flutter Timeline',
|
title: 'Flutter Timeline',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme:
|
textTheme: const TextTheme(
|
||||||
ColorScheme.fromSeed(seedColor: Colors.deepPurple).copyWith(
|
titleLarge: TextStyle(
|
||||||
background: const Color(0xFFB8E2E8),
|
color: Color(0xffb71c6d), fontFamily: 'Playfair Display')),
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
seedColor: const Color(0xFFB8E2E8),
|
||||||
|
primary: const Color(0xffb71c6d),
|
||||||
|
).copyWith(
|
||||||
|
background: const Color(0XFFFAF9F6),
|
||||||
),
|
),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
|
|
|
@ -34,7 +34,13 @@ class _PostScreenState extends State<PostScreen> {
|
||||||
|
|
||||||
class TestUserService implements TimelineUserService {
|
class TestUserService implements TimelineUserService {
|
||||||
final Map<String, TimelinePosterUserModel> _users = {
|
final Map<String, TimelinePosterUserModel> _users = {
|
||||||
'test_user': const TimelinePosterUserModel(userId: 'test_user')
|
'test_user': const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -101,6 +101,13 @@ void generatePost(TimelineService service) {
|
||||||
content: "Post $amountOfPosts content",
|
content: "Post $amountOfPosts content",
|
||||||
likes: 0,
|
likes: 0,
|
||||||
reaction: 0,
|
reaction: 0,
|
||||||
|
creator: const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
),
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
reactionEnabled: amountOfPosts % 2 == 0 ? false : true,
|
reactionEnabled: amountOfPosts % 2 == 0 ? false : true,
|
||||||
imageUrl: amountOfPosts % 3 != 0
|
imageUrl: amountOfPosts % 3 != 0
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// import 'package:example/apps/go_router/app.dart';
|
// import 'package:example/apps/go_router/app.dart';
|
||||||
// import 'package:example/apps/navigator/app.dart';
|
// import 'package:example/apps/navigator/app.dart';
|
||||||
import 'package:example/apps/widgets/app.dart';
|
import 'package:example/apps/go_router/app.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/date_symbol_data_local.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.
|
// Uncomment any, but only one, of these lines to run the example with specific navigation.
|
||||||
|
|
||||||
runApp(const WidgetApp());
|
// runApp(const WidgetApp());
|
||||||
// runApp(const NavigatorApp());
|
// runApp(const NavigatorApp());
|
||||||
// runApp(const GoRouterApp());
|
runApp(const GoRouterApp());
|
||||||
}
|
}
|
||||||
|
|
29
packages/flutter_timeline/example/test/widget_test.dart
Normal file
29
packages/flutter_timeline/example/test/widget_test.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// 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:example/apps/widgets/app.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
|
// Build our app and trigger a frame.
|
||||||
|
await tester.pumpWidget(const WidgetApp());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
}
|
|
@ -43,10 +43,16 @@ List<GoRoute> getTimelineStoryRoutes({
|
||||||
);
|
);
|
||||||
|
|
||||||
var button = FloatingActionButton(
|
var button = FloatingActionButton(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
onPressed: () async => context.go(
|
onPressed: () async => context.go(
|
||||||
TimelineUserStoryRoutes.timelinePostCreation,
|
TimelineUserStoryRoutes.timelinePostCreation,
|
||||||
),
|
),
|
||||||
child: const Icon(Icons.add),
|
shape: const CircleBorder(),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 30,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return buildScreenWithoutTransition(
|
return buildScreenWithoutTransition(
|
||||||
|
@ -55,7 +61,13 @@ List<GoRoute> getTimelineStoryRoutes({
|
||||||
child: config.homeOpenPageBuilder
|
child: config.homeOpenPageBuilder
|
||||||
?.call(context, timelineScreen, button) ??
|
?.call(context, timelineScreen, button) ??
|
||||||
Scaffold(
|
Scaffold(
|
||||||
appBar: AppBar(),
|
appBar: AppBar(
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
title: Text(
|
||||||
|
'Iconinstagram',
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
body: timelineScreen,
|
body: timelineScreen,
|
||||||
floatingActionButton: button,
|
floatingActionButton: button,
|
||||||
),
|
),
|
||||||
|
@ -66,18 +78,19 @@ List<GoRoute> getTimelineStoryRoutes({
|
||||||
path: TimelineUserStoryRoutes.timelineView,
|
path: TimelineUserStoryRoutes.timelineView,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
var post =
|
var post =
|
||||||
config.service.postService.getPost(state.pathParameters['post']!)!;
|
config.service.postService.getPost(state.pathParameters['post']!);
|
||||||
|
|
||||||
var timelinePostWidget = TimelinePostScreen(
|
var timelinePostWidget = TimelinePostScreen(
|
||||||
userId: config.userId,
|
userId: config.userId,
|
||||||
options: config.optionsBuilder(context),
|
options: config.optionsBuilder(context),
|
||||||
service: config.service,
|
service: config.service,
|
||||||
post: post,
|
post: post!,
|
||||||
onPostDelete: () => config.onPostDelete?.call(context, post),
|
onPostDelete: () => config.onPostDelete?.call(context, post),
|
||||||
onUserTap: (user) => config.onUserTap?.call(context, user),
|
onUserTap: (user) => config.onUserTap?.call(context, user),
|
||||||
);
|
);
|
||||||
|
|
||||||
var backButton = IconButton(
|
var backButton = IconButton(
|
||||||
|
color: Colors.white,
|
||||||
icon: const Icon(Icons.arrow_back_ios),
|
icon: const Icon(Icons.arrow_back_ios),
|
||||||
onPressed: () => context.go(TimelineUserStoryRoutes.timelineHome),
|
onPressed: () => context.go(TimelineUserStoryRoutes.timelineHome),
|
||||||
);
|
);
|
||||||
|
@ -90,6 +103,11 @@ List<GoRoute> getTimelineStoryRoutes({
|
||||||
Scaffold(
|
Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: backButton,
|
leading: backButton,
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
title: Text(
|
||||||
|
'Category',
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: timelinePostWidget,
|
body: timelinePostWidget,
|
||||||
),
|
),
|
||||||
|
@ -133,8 +151,10 @@ List<GoRoute> getTimelineStoryRoutes({
|
||||||
?.call(context, timelinePostCreationWidget, backButton) ??
|
?.call(context, timelinePostCreationWidget, backButton) ??
|
||||||
Scaffold(
|
Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
backgroundColor: Colors.black,
|
||||||
title: Text(
|
title: Text(
|
||||||
config.optionsBuilder(context).translations.postCreation,
|
config.optionsBuilder(context).translations.postCreation,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
leading: backButton,
|
leading: backButton,
|
||||||
),
|
),
|
||||||
|
|
|
@ -134,6 +134,7 @@ Widget _postCreationScreenRoute({
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
config.optionsBuilder(context).translations.postCreation,
|
config.optionsBuilder(context).translations.postCreation,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -8,13 +8,13 @@ version: 2.3.0
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.1.3 <4.0.0'
|
sdk: ">=3.1.3 <4.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
go_router: any
|
go_router: any
|
||||||
|
|
||||||
flutter_timeline_view:
|
flutter_timeline_view:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_timeline
|
url: https://github.com/Iconica-Development/flutter_timeline
|
||||||
|
@ -22,10 +22,10 @@ dependencies:
|
||||||
ref: 2.3.0
|
ref: 2.3.0
|
||||||
|
|
||||||
flutter_timeline_interface:
|
flutter_timeline_interface:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_timeline
|
url: https://github.com/Iconica-Development/flutter_timeline
|
||||||
path: packages/flutter_timeline_interface
|
path: packages/flutter_timeline_interface
|
||||||
ref: 2.3.0
|
ref: 2.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
@ -35,4 +35,3 @@ dev_dependencies:
|
||||||
ref: 6.0.0
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ class TimelineOptions {
|
||||||
this.padding = const EdgeInsets.symmetric(vertical: 12.0),
|
this.padding = const EdgeInsets.symmetric(vertical: 12.0),
|
||||||
this.iconSize = 26,
|
this.iconSize = 26,
|
||||||
this.postWidgetHeight,
|
this.postWidgetHeight,
|
||||||
this.postPadding = const EdgeInsets.all(12.0),
|
this.postPadding =
|
||||||
|
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 12.0),
|
||||||
this.filterOptions = const FilterOptions(),
|
this.filterOptions = const FilterOptions(),
|
||||||
this.categoriesOptions = const CategoriesOptions(),
|
this.categoriesOptions = const CategoriesOptions(),
|
||||||
this.requireImageForPost = false,
|
this.requireImageForPost = false,
|
||||||
|
|
|
@ -20,6 +20,7 @@ class TimelineTextStyles {
|
||||||
this.postTitleStyle,
|
this.postTitleStyle,
|
||||||
this.postLikeTitleAndAmount,
|
this.postLikeTitleAndAmount,
|
||||||
this.postCreatedAtStyle,
|
this.postCreatedAtStyle,
|
||||||
|
this.categoryTitleStyle,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The TextStyle for the text indicating that you can view a post
|
/// The TextStyle for the text indicating that you can view a post
|
||||||
|
@ -70,4 +71,6 @@ class TimelineTextStyles {
|
||||||
|
|
||||||
/// The TextStyle for the creation time of the post
|
/// The TextStyle for the creation time of the post
|
||||||
final TextStyle? postCreatedAtStyle;
|
final TextStyle? postCreatedAtStyle;
|
||||||
|
|
||||||
|
final TextStyle? categoryTitleStyle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,15 @@ class TimelineTranslations {
|
||||||
required this.noPosts,
|
required this.noPosts,
|
||||||
required this.noPostsWithFilter,
|
required this.noPostsWithFilter,
|
||||||
required this.title,
|
required this.title,
|
||||||
|
required this.titleHintText,
|
||||||
required this.content,
|
required this.content,
|
||||||
|
required this.contentHintText,
|
||||||
required this.contentDescription,
|
required this.contentDescription,
|
||||||
required this.uploadImage,
|
required this.uploadImage,
|
||||||
required this.uploadImageDescription,
|
required this.uploadImageDescription,
|
||||||
required this.allowComments,
|
required this.allowComments,
|
||||||
required this.allowCommentsDescription,
|
required this.allowCommentsDescription,
|
||||||
|
required this.commentsTitleOnPost,
|
||||||
required this.checkPost,
|
required this.checkPost,
|
||||||
required this.deletePost,
|
required this.deletePost,
|
||||||
required this.deleteReaction,
|
required this.deleteReaction,
|
||||||
|
@ -32,6 +35,8 @@ class TimelineTranslations {
|
||||||
required this.postOverview,
|
required this.postOverview,
|
||||||
required this.postIn,
|
required this.postIn,
|
||||||
required this.postCreation,
|
required this.postCreation,
|
||||||
|
required this.yes,
|
||||||
|
required this.no,
|
||||||
});
|
});
|
||||||
|
|
||||||
const TimelineTranslations.empty()
|
const TimelineTranslations.empty()
|
||||||
|
@ -46,12 +51,13 @@ class TimelineTranslations {
|
||||||
allowComments = 'Are people allowed to comment?',
|
allowComments = 'Are people allowed to comment?',
|
||||||
allowCommentsDescription =
|
allowCommentsDescription =
|
||||||
'Indicate whether people are allowed to respond',
|
'Indicate whether people are allowed to respond',
|
||||||
|
commentsTitleOnPost = 'Comments',
|
||||||
checkPost = 'Check post overview',
|
checkPost = 'Check post overview',
|
||||||
deletePost = 'Delete post',
|
deletePost = 'Delete post',
|
||||||
deleteReaction = 'Delete Reaction',
|
deleteReaction = 'Delete Reaction',
|
||||||
viewPost = 'View post',
|
viewPost = 'View post',
|
||||||
likesTitle = 'Likes',
|
likesTitle = 'Likes',
|
||||||
commentsTitle = 'Comments',
|
commentsTitle = 'Are people allowed to comment?',
|
||||||
firstComment = 'Be the first to comment',
|
firstComment = 'Be the first to comment',
|
||||||
writeComment = 'Write your comment here...',
|
writeComment = 'Write your comment here...',
|
||||||
postAt = 'at',
|
postAt = 'at',
|
||||||
|
@ -60,7 +66,11 @@ class TimelineTranslations {
|
||||||
searchHint = 'Search...',
|
searchHint = 'Search...',
|
||||||
postOverview = 'Post Overview',
|
postOverview = 'Post Overview',
|
||||||
postIn = 'Post in',
|
postIn = 'Post in',
|
||||||
postCreation = 'Create Post';
|
postCreation = 'Create Post',
|
||||||
|
titleHintText = 'Title...',
|
||||||
|
contentHintText = 'Context...',
|
||||||
|
yes = 'Yes',
|
||||||
|
no = 'No';
|
||||||
|
|
||||||
final String noPosts;
|
final String noPosts;
|
||||||
final String noPostsWithFilter;
|
final String noPostsWithFilter;
|
||||||
|
@ -76,11 +86,15 @@ class TimelineTranslations {
|
||||||
final String checkPost;
|
final String checkPost;
|
||||||
final String postAt;
|
final String postAt;
|
||||||
|
|
||||||
|
final String titleHintText;
|
||||||
|
final String contentHintText;
|
||||||
|
|
||||||
final String deletePost;
|
final String deletePost;
|
||||||
final String deleteReaction;
|
final String deleteReaction;
|
||||||
final String viewPost;
|
final String viewPost;
|
||||||
final String likesTitle;
|
final String likesTitle;
|
||||||
final String commentsTitle;
|
final String commentsTitle;
|
||||||
|
final String commentsTitleOnPost;
|
||||||
final String writeComment;
|
final String writeComment;
|
||||||
final String firstComment;
|
final String firstComment;
|
||||||
final String postLoadingError;
|
final String postLoadingError;
|
||||||
|
@ -93,6 +107,9 @@ class TimelineTranslations {
|
||||||
final String postIn;
|
final String postIn;
|
||||||
final String postCreation;
|
final String postCreation;
|
||||||
|
|
||||||
|
final String yes;
|
||||||
|
final String no;
|
||||||
|
|
||||||
TimelineTranslations copyWith({
|
TimelineTranslations copyWith({
|
||||||
String? noPosts,
|
String? noPosts,
|
||||||
String? noPostsWithFilter,
|
String? noPostsWithFilter,
|
||||||
|
@ -104,6 +121,7 @@ class TimelineTranslations {
|
||||||
String? uploadImageDescription,
|
String? uploadImageDescription,
|
||||||
String? allowComments,
|
String? allowComments,
|
||||||
String? allowCommentsDescription,
|
String? allowCommentsDescription,
|
||||||
|
String? commentsTitleOnPost,
|
||||||
String? checkPost,
|
String? checkPost,
|
||||||
String? postAt,
|
String? postAt,
|
||||||
String? deletePost,
|
String? deletePost,
|
||||||
|
@ -119,6 +137,10 @@ class TimelineTranslations {
|
||||||
String? postOverview,
|
String? postOverview,
|
||||||
String? postIn,
|
String? postIn,
|
||||||
String? postCreation,
|
String? postCreation,
|
||||||
|
String? titleHintText,
|
||||||
|
String? contentHintText,
|
||||||
|
String? yes,
|
||||||
|
String? no,
|
||||||
}) =>
|
}) =>
|
||||||
TimelineTranslations(
|
TimelineTranslations(
|
||||||
noPosts: noPosts ?? this.noPosts,
|
noPosts: noPosts ?? this.noPosts,
|
||||||
|
@ -133,6 +155,7 @@ class TimelineTranslations {
|
||||||
allowComments: allowComments ?? this.allowComments,
|
allowComments: allowComments ?? this.allowComments,
|
||||||
allowCommentsDescription:
|
allowCommentsDescription:
|
||||||
allowCommentsDescription ?? this.allowCommentsDescription,
|
allowCommentsDescription ?? this.allowCommentsDescription,
|
||||||
|
commentsTitleOnPost: commentsTitleOnPost ?? this.commentsTitleOnPost,
|
||||||
checkPost: checkPost ?? this.checkPost,
|
checkPost: checkPost ?? this.checkPost,
|
||||||
postAt: postAt ?? this.postAt,
|
postAt: postAt ?? this.postAt,
|
||||||
deletePost: deletePost ?? this.deletePost,
|
deletePost: deletePost ?? this.deletePost,
|
||||||
|
@ -149,5 +172,9 @@ class TimelineTranslations {
|
||||||
postOverview: postOverview ?? this.postOverview,
|
postOverview: postOverview ?? this.postOverview,
|
||||||
postIn: postIn ?? this.postIn,
|
postIn: postIn ?? this.postIn,
|
||||||
postCreation: postCreation ?? this.postCreation,
|
postCreation: postCreation ?? this.postCreation,
|
||||||
|
titleHintText: titleHintText ?? this.titleHintText,
|
||||||
|
contentHintText: contentHintText ?? this.contentHintText,
|
||||||
|
yes: yes ?? this.yes,
|
||||||
|
no: no ?? this.no,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:dotted_border/dotted_border.dart';
|
import 'package:dotted_border/dotted_border.dart';
|
||||||
|
@ -97,7 +98,7 @@ class _TimelinePostCreationScreenState
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Future<void> onPostCreated() async {
|
Future<void> onPostCreated() async {
|
||||||
var post = TimelinePost(
|
var post = TimelinePost(
|
||||||
id: '',
|
id: 'Post${Random().nextInt(1000)}',
|
||||||
creatorId: widget.userId,
|
creatorId: widget.userId,
|
||||||
title: titleController.text,
|
title: titleController.text,
|
||||||
category: widget.postCategory,
|
category: widget.postCategory,
|
||||||
|
@ -128,7 +129,7 @@ class _TimelinePostCreationScreenState
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.title,
|
widget.options.translations.title,
|
||||||
style: theme.textTheme.displaySmall,
|
style: theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
widget.options.textInputBuilder?.call(
|
widget.options.textInputBuilder?.call(
|
||||||
titleController,
|
titleController,
|
||||||
|
@ -137,11 +138,14 @@ class _TimelinePostCreationScreenState
|
||||||
) ??
|
) ??
|
||||||
TextField(
|
TextField(
|
||||||
controller: titleController,
|
controller: titleController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: widget.options.translations.titleHintText,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.content,
|
widget.options.translations.content,
|
||||||
style: theme.textTheme.displaySmall,
|
style: theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
|
@ -157,6 +161,9 @@ class _TimelinePostCreationScreenState
|
||||||
expands: true,
|
expands: true,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
minLines: null,
|
minLines: null,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: widget.options.translations.contentHintText,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -165,7 +172,7 @@ class _TimelinePostCreationScreenState
|
||||||
// input field for the content
|
// input field for the content
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.uploadImage,
|
widget.options.translations.uploadImage,
|
||||||
style: theme.textTheme.displaySmall,
|
style: theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.uploadImageDescription,
|
widget.options.translations.uploadImageDescription,
|
||||||
|
@ -198,30 +205,31 @@ class _TimelinePostCreationScreenState
|
||||||
}
|
}
|
||||||
checkIfEditingDone();
|
checkIfEditingDone();
|
||||||
},
|
},
|
||||||
child: image != null
|
child: ClipRRect(
|
||||||
? ClipRRect(
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
child: image != null
|
||||||
child: Image.memory(
|
? Image.memory(
|
||||||
image!,
|
image!,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 150.0,
|
height: 150.0,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
// give it a rounded border
|
// give it a rounded border
|
||||||
),
|
)
|
||||||
)
|
: DottedBorder(
|
||||||
: DottedBorder(
|
dashPattern: const [4, 4],
|
||||||
radius: const Radius.circular(8.0),
|
radius: const Radius.circular(8.0),
|
||||||
color: theme.textTheme.displayMedium?.color ??
|
color: theme.textTheme.displayMedium?.color ??
|
||||||
Colors.white,
|
Colors.white,
|
||||||
child: const SizedBox(
|
child: const SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 150.0,
|
height: 150.0,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.image,
|
Icons.image,
|
||||||
size: 32,
|
size: 50,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// if an image is selected, show a delete button
|
// if an image is selected, show a delete button
|
||||||
if (image != null) ...[
|
if (image != null) ...[
|
||||||
|
@ -255,21 +263,36 @@ class _TimelinePostCreationScreenState
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.commentsTitle,
|
widget.options.translations.commentsTitle,
|
||||||
style: theme.textTheme.displaySmall,
|
style: theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.allowCommentsDescription,
|
widget.options.translations.allowCommentsDescription,
|
||||||
style: theme.textTheme.bodyMedium,
|
style: theme.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
// radio buttons for yes or no
|
Row(
|
||||||
Switch(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
value: allowComments,
|
children: <Widget>[
|
||||||
onChanged: (newValue) {
|
Checkbox(
|
||||||
setState(() {
|
value: allowComments,
|
||||||
allowComments = newValue;
|
onChanged: (value) {
|
||||||
});
|
setState(() {
|
||||||
},
|
allowComments = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Text(widget.options.translations.yes),
|
||||||
|
Checkbox(
|
||||||
|
value: !allowComments,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
allowComments = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Text(widget.options.translations.no),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: (widget.options.buttonBuilder != null)
|
child: (widget.options.buttonBuilder != null)
|
||||||
|
|
|
@ -21,7 +21,11 @@ class TimelinePostOverviewScreen extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(options.translations.postOverview),
|
backgroundColor: Colors.black,
|
||||||
|
title: Text(
|
||||||
|
options.translations.postOverview,
|
||||||
|
style: TextStyle(color: Theme.of(context).primaryColor),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -93,6 +93,10 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: hintText,
|
hintText: hintText,
|
||||||
suffixIcon: suffixIcon,
|
suffixIcon: suffixIcon,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(20.0), // Adjust the value as needed
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -184,7 +188,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
if (post.creator!.imageUrl != null) ...[
|
if (post.creator!.imageUrl != null) ...[
|
||||||
widget.options.userAvatarBuilder?.call(
|
widget.options.userAvatarBuilder?.call(
|
||||||
post.creator!,
|
post.creator!,
|
||||||
40,
|
28,
|
||||||
) ??
|
) ??
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 20,
|
radius: 20,
|
||||||
|
@ -196,7 +200,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
] else ...[
|
] else ...[
|
||||||
widget.options.anonymousAvatarBuilder?.call(
|
widget.options.anonymousAvatarBuilder?.call(
|
||||||
post.creator!,
|
post.creator!,
|
||||||
40,
|
28,
|
||||||
) ??
|
) ??
|
||||||
const CircleAvatar(
|
const CircleAvatar(
|
||||||
radius: 20,
|
radius: 20,
|
||||||
|
@ -318,6 +322,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.thumb_up_rounded,
|
Icons.thumb_up_rounded,
|
||||||
color: widget.options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
|
size: widget.options.iconSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -418,8 +423,8 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (post.reactionEnabled) ...[
|
if (post.reactionEnabled) ...[
|
||||||
Text(
|
Text(
|
||||||
widget.options.translations.commentsTitle,
|
widget.options.translations.commentsTitleOnPost,
|
||||||
style: theme.textTheme.displaySmall,
|
style: theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
for (var reaction
|
for (var reaction
|
||||||
in post.reactions ?? <TimelinePostReaction>[]) ...[
|
in post.reactions ?? <TimelinePostReaction>[]) ...[
|
||||||
|
@ -469,7 +474,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
reaction.creator!.imageUrl!.isNotEmpty) ...[
|
reaction.creator!.imageUrl!.isNotEmpty) ...[
|
||||||
widget.options.userAvatarBuilder?.call(
|
widget.options.userAvatarBuilder?.call(
|
||||||
reaction.creator!,
|
reaction.creator!,
|
||||||
25,
|
28,
|
||||||
) ??
|
) ??
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 20,
|
radius: 20,
|
||||||
|
@ -480,7 +485,7 @@ class _TimelinePostScreenState extends State<_TimelinePostScreen> {
|
||||||
] else ...[
|
] else ...[
|
||||||
widget.options.anonymousAvatarBuilder?.call(
|
widget.options.anonymousAvatarBuilder?.call(
|
||||||
reaction.creator!,
|
reaction.creator!,
|
||||||
25,
|
28,
|
||||||
) ??
|
) ??
|
||||||
const CircleAvatar(
|
const CircleAvatar(
|
||||||
radius: 20,
|
radius: 20,
|
||||||
|
|
|
@ -74,10 +74,27 @@ class _TimelineScreenState extends State<TimelineScreen> {
|
||||||
|
|
||||||
late var filterWord = widget.options.filterOptions.initialFilterWord;
|
late var filterWord = widget.options.filterOptions.initialFilterWord;
|
||||||
|
|
||||||
|
bool _isOnTop = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.removeListener(_updateIsOnTop);
|
||||||
|
controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateIsOnTop() {
|
||||||
|
setState(() {
|
||||||
|
_isOnTop = controller.position.pixels < 40;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
controller = widget.scrollController ?? ScrollController();
|
controller = widget.scrollController ?? ScrollController();
|
||||||
|
controller.addListener(_updateIsOnTop);
|
||||||
|
|
||||||
// only load the posts after the first frame
|
// only load the posts after the first frame
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
unawaited(loadPosts());
|
unawaited(loadPosts());
|
||||||
|
@ -188,6 +205,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
CategorySelector(
|
CategorySelector(
|
||||||
|
isOnTop: _isOnTop,
|
||||||
filter: category,
|
filter: category,
|
||||||
options: widget.options,
|
options: widget.options,
|
||||||
onTapCategory: (categoryKey) {
|
onTapCategory: (categoryKey) {
|
||||||
|
|
|
@ -17,7 +17,13 @@ class LocalTimelinePostService
|
||||||
Future<TimelinePost> createPost(TimelinePost post) async {
|
Future<TimelinePost> createPost(TimelinePost post) async {
|
||||||
posts.add(
|
posts.add(
|
||||||
post.copyWith(
|
post.copyWith(
|
||||||
creator: const TimelinePosterUserModel(userId: 'test_user'),
|
creator: const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -62,7 +68,13 @@ class LocalTimelinePostService
|
||||||
for (var reaction in reactions) {
|
for (var reaction in reactions) {
|
||||||
updatedReactions.add(
|
updatedReactions.add(
|
||||||
reaction.copyWith(
|
reaction.copyWith(
|
||||||
creator: const TimelinePosterUserModel(userId: 'test_user'),
|
creator: const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -156,7 +168,13 @@ class LocalTimelinePostService
|
||||||
|
|
||||||
var updatedReaction = reaction.copyWith(
|
var updatedReaction = reaction.copyWith(
|
||||||
id: reactionId,
|
id: reactionId,
|
||||||
creator: const TimelinePosterUserModel(userId: 'test_user'),
|
creator: const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var updatedPost = post.copyWith(
|
var updatedPost = post.copyWith(
|
||||||
|
@ -179,11 +197,20 @@ class LocalTimelinePostService
|
||||||
creatorId: 'test_user',
|
creatorId: 'test_user',
|
||||||
title: 'Post 0',
|
title: 'Post 0',
|
||||||
category: null,
|
category: null,
|
||||||
|
imageUrl:
|
||||||
|
'https://t4.ftcdn.net/jpg/02/77/71/45/240_F_277714513_fQ0akmI3TQxa0wkPCLeO12Rx3cL2AuIf.jpg',
|
||||||
content: 'Standard post without image made by the current user',
|
content: 'Standard post without image made by the current user',
|
||||||
likes: 0,
|
likes: 0,
|
||||||
reaction: 0,
|
reaction: 0,
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
reactionEnabled: false,
|
reactionEnabled: false,
|
||||||
|
creator: const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TimelinePost(
|
TimelinePost(
|
||||||
id: 'Post1',
|
id: 'Post1',
|
||||||
|
@ -197,7 +224,14 @@ class LocalTimelinePostService
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
reactionEnabled: false,
|
reactionEnabled: false,
|
||||||
imageUrl:
|
imageUrl:
|
||||||
'https://s3-eu-west-1.amazonaws.com/sortlist-core-api/6qpvvqjtmniirpkvp8eg83bicnc2',
|
'https://t4.ftcdn.net/jpg/02/77/71/45/240_F_277714513_fQ0akmI3TQxa0wkPCLeO12Rx3cL2AuIf.jpg',
|
||||||
|
creator: const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TimelinePost(
|
TimelinePost(
|
||||||
id: 'Post2',
|
id: 'Post2',
|
||||||
|
@ -211,7 +245,14 @@ class LocalTimelinePostService
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
reactionEnabled: true,
|
reactionEnabled: true,
|
||||||
imageUrl:
|
imageUrl:
|
||||||
'https://s3-eu-west-1.amazonaws.com/sortlist-core-api/6qpvvqjtmniirpkvp8eg83bicnc2',
|
'https://t4.ftcdn.net/jpg/02/77/71/45/240_F_277714513_fQ0akmI3TQxa0wkPCLeO12Rx3cL2AuIf.jpg',
|
||||||
|
creator: const TimelinePosterUserModel(
|
||||||
|
userId: 'test_user',
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.britannica.com/68/143568-050-5246474F/Donkey.jpg?w=400&h=300&c=crop',
|
||||||
|
firstName: 'Dirk',
|
||||||
|
lastName: 'lukassen',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,55 +3,64 @@ 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';
|
||||||
|
|
||||||
class CategorySelector extends StatelessWidget {
|
class CategorySelector extends StatefulWidget {
|
||||||
const CategorySelector({
|
const CategorySelector({
|
||||||
required this.filter,
|
required this.filter,
|
||||||
required this.options,
|
required this.options,
|
||||||
required this.onTapCategory,
|
required this.onTapCategory,
|
||||||
|
required this.isOnTop,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? filter;
|
final String? filter;
|
||||||
final TimelineOptions options;
|
final TimelineOptions options;
|
||||||
final void Function(String? categoryKey) onTapCategory;
|
final void Function(String? categoryKey) onTapCategory;
|
||||||
|
final bool isOnTop;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CategorySelector> createState() => _CategorySelectorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CategorySelectorState extends State<CategorySelector> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (options.categoriesOptions.categoriesBuilder == null) {
|
if (widget.options.categoriesOptions.categoriesBuilder == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
var categories = options.categoriesOptions.categoriesBuilder!(context);
|
var categories =
|
||||||
|
widget.options.categoriesOptions.categoriesBuilder!(context);
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width:
|
width: widget.options.categoriesOptions
|
||||||
options.categoriesOptions.categorySelectorHorizontalPadding ??
|
.categorySelectorHorizontalPadding ??
|
||||||
max(options.padding.horizontal - 4, 0),
|
max(widget.options.padding.horizontal - 20, 0),
|
||||||
),
|
),
|
||||||
for (var category in categories) ...[
|
for (var category in categories) ...[
|
||||||
options.categoriesOptions.categoryButtonBuilder?.call(
|
widget.options.categoriesOptions.categoryButtonBuilder?.call(
|
||||||
categoryKey: category.key,
|
categoryKey: category.key,
|
||||||
categoryName: category.title,
|
categoryName: category.title,
|
||||||
onTap: () => onTapCategory(category.key),
|
onTap: () => widget.onTapCategory(category.key),
|
||||||
selected: filter == category.key,
|
selected: widget.filter == category.key,
|
||||||
) ??
|
) ??
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
child: CategorySelectorButton(
|
child: CategorySelectorButton(
|
||||||
|
isOnTop: widget.isOnTop,
|
||||||
category: category,
|
category: category,
|
||||||
selected: filter == category.key,
|
selected: widget.filter == category.key,
|
||||||
onTap: () => onTapCategory(category.key),
|
onTap: () => widget.onTapCategory(category.key),
|
||||||
|
options: widget.options,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width:
|
width: widget.options.categoriesOptions
|
||||||
options.categoriesOptions.categorySelectorHorizontalPadding ??
|
.categorySelectorHorizontalPadding ??
|
||||||
max(options.padding.horizontal - 4, 0),
|
max(widget.options.padding.horizontal - 4, 0),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,50 +1,76 @@
|
||||||
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';
|
||||||
|
|
||||||
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,
|
||||||
|
required this.options,
|
||||||
|
required this.isOnTop,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final TimelineCategory category;
|
final TimelineCategory category;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final void Function() onTap;
|
final void Function() onTap;
|
||||||
|
final TimelineOptions options;
|
||||||
|
final bool isOnTop;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
return TextButton(
|
return AnimatedContainer(
|
||||||
onPressed: onTap,
|
height: isOnTop ? 140 : 40,
|
||||||
style: ButtonStyle(
|
duration: const Duration(milliseconds: 100),
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
child: TextButton(
|
||||||
padding: const MaterialStatePropertyAll(
|
onPressed: onTap,
|
||||||
EdgeInsets.symmetric(
|
style: ButtonStyle(
|
||||||
vertical: 5,
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
horizontal: 12,
|
padding: const MaterialStatePropertyAll(
|
||||||
|
EdgeInsets.symmetric(
|
||||||
|
vertical: 5,
|
||||||
|
horizontal: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
fixedSize: MaterialStatePropertyAll(Size(140, isOnTop ? 140 : 20)),
|
||||||
minimumSize: const MaterialStatePropertyAll(Size.zero),
|
backgroundColor: MaterialStatePropertyAll(
|
||||||
backgroundColor: MaterialStatePropertyAll(
|
selected ? theme.colorScheme.primary : Colors.transparent,
|
||||||
selected ? theme.colorScheme.primary : theme.colorScheme.surface,
|
),
|
||||||
),
|
shape: MaterialStatePropertyAll(
|
||||||
shape: const MaterialStatePropertyAll(
|
RoundedRectangleBorder(
|
||||||
RoundedRectangleBorder(
|
borderRadius: const BorderRadius.all(
|
||||||
borderRadius: BorderRadius.all(
|
Radius.circular(8),
|
||||||
Radius.circular(45),
|
),
|
||||||
|
side: BorderSide(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
child: Row(
|
||||||
child: Text(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
category.title,
|
children: [
|
||||||
style: theme.textTheme.labelMedium?.copyWith(
|
Column(
|
||||||
color: selected
|
mainAxisAlignment:
|
||||||
? theme.colorScheme.onPrimary
|
isOnTop ? MainAxisAlignment.end : MainAxisAlignment.center,
|
||||||
: theme.colorScheme.onSurface,
|
children: [
|
||||||
|
Text(
|
||||||
|
category.title,
|
||||||
|
style: (options.theme.textStyles.categoryTitleStyle ??
|
||||||
|
theme.textTheme.labelLarge)
|
||||||
|
?.copyWith(
|
||||||
|
color: selected
|
||||||
|
? theme.colorScheme.onPrimary
|
||||||
|
: theme.colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,46 +30,43 @@ class _ReactionBottomState extends State<ReactionBottom> {
|
||||||
final TextEditingController _textEditingController = TextEditingController();
|
final TextEditingController _textEditingController = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Container(
|
Widget build(BuildContext context) => SafeArea(
|
||||||
color: Theme.of(context).colorScheme.background,
|
bottom: true,
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.symmetric(
|
color: Theme.of(context).colorScheme.background,
|
||||||
horizontal: 12,
|
child: Container(
|
||||||
vertical: 8,
|
margin: const EdgeInsets.symmetric(
|
||||||
),
|
horizontal: 12,
|
||||||
height: 45,
|
vertical: 8,
|
||||||
child: widget.messageInputBuilder(
|
),
|
||||||
_textEditingController,
|
height: 48,
|
||||||
Padding(
|
child: widget.messageInputBuilder(
|
||||||
padding: const EdgeInsets.only(right: 15.0),
|
_textEditingController,
|
||||||
child: Row(
|
Padding(
|
||||||
mainAxisSize: MainAxisSize.min,
|
padding: const EdgeInsets.symmetric(
|
||||||
children: [
|
horizontal: 4,
|
||||||
IconButton(
|
),
|
||||||
onPressed: widget.onPressSelectImage,
|
child: Row(
|
||||||
icon: Icon(
|
mainAxisSize: MainAxisSize.min,
|
||||||
Icons.image,
|
children: [
|
||||||
color: widget.iconColor,
|
IconButton(
|
||||||
),
|
onPressed: () async {
|
||||||
),
|
var value = _textEditingController.text;
|
||||||
IconButton(
|
if (value.isNotEmpty) {
|
||||||
onPressed: () async {
|
await widget.onReactionSubmit(value);
|
||||||
var value = _textEditingController.text;
|
_textEditingController.clear();
|
||||||
|
}
|
||||||
if (value.isNotEmpty) {
|
},
|
||||||
await widget.onReactionSubmit(value);
|
icon: Icon(
|
||||||
_textEditingController.clear();
|
Icons.send,
|
||||||
}
|
color: widget.iconColor,
|
||||||
},
|
),
|
||||||
icon: Icon(
|
),
|
||||||
Icons.send,
|
],
|
||||||
color: widget.iconColor,
|
),
|
||||||
),
|
),
|
||||||
),
|
widget.translations.writeComment,
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
widget.translations.writeComment,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -69,7 +69,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
|
||||||
if (widget.post.creator!.imageUrl != null) ...[
|
if (widget.post.creator!.imageUrl != null) ...[
|
||||||
widget.options.userAvatarBuilder?.call(
|
widget.options.userAvatarBuilder?.call(
|
||||||
widget.post.creator!,
|
widget.post.creator!,
|
||||||
40,
|
28,
|
||||||
) ??
|
) ??
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 20,
|
radius: 20,
|
||||||
|
@ -213,7 +213,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
|
||||||
icon: widget.options.theme.likeIcon ??
|
icon: widget.options.theme.likeIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
widget.post.likedBy?.contains(widget.userId) ?? false
|
widget.post.likedBy?.contains(widget.userId) ?? false
|
||||||
? Icons.favorite
|
? Icons.favorite_rounded
|
||||||
: Icons.favorite_outline_outlined,
|
: Icons.favorite_outline_outlined,
|
||||||
),
|
),
|
||||||
label: Text('${widget.post.likes}'),
|
label: Text('${widget.post.likes}'),
|
||||||
|
@ -240,7 +240,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: widget.options.theme.likedIcon ??
|
child: widget.options.theme.likedIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.thumb_up_rounded,
|
Icons.favorite_rounded,
|
||||||
color: widget.options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
size: widget.options.iconSize,
|
size: widget.options.iconSize,
|
||||||
),
|
),
|
||||||
|
@ -253,7 +253,7 @@ class _TimelinePostWidgetState extends State<TimelinePostWidget> {
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: widget.options.theme.likedIcon ??
|
child: widget.options.theme.likedIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.thumb_up_rounded,
|
Icons.favorite_outline,
|
||||||
color: widget.options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
size: widget.options.iconSize,
|
size: widget.options.iconSize,
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue