mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 10:33:44 +02:00
feat: add timelineoverview cards
This commit is contained in:
parent
55e4e50798
commit
c33d8cb893
8 changed files with 199 additions and 3 deletions
|
@ -5,4 +5,5 @@
|
|||
library flutter_timeline_interface;
|
||||
|
||||
export 'src/model/timeline_post.dart';
|
||||
export 'src/model/timeline_poster.dart';
|
||||
export 'src/model/timeline_reaction.dart';
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
// SPDX-FileCopyrightText: 2023 Iconica
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_timeline_interface/src/model/timeline_poster.dart';
|
||||
import 'package:flutter_timeline_interface/src/model/timeline_reaction.dart';
|
||||
|
||||
/// A post of the timeline.
|
||||
|
@ -14,6 +19,7 @@ class TimelinePost {
|
|||
required this.reaction,
|
||||
required this.createdAt,
|
||||
required this.reactionEnabled,
|
||||
this.creator,
|
||||
this.likedBy,
|
||||
this.reactions,
|
||||
this.imageUrl,
|
||||
|
@ -25,6 +31,9 @@ class TimelinePost {
|
|||
/// The unique identifier of the creator of the post.
|
||||
final String creatorId;
|
||||
|
||||
/// The creator of the post. If null it isn't loaded yet.
|
||||
final TimelinePosterUserModel? creator;
|
||||
|
||||
/// The title of the post.
|
||||
final String title;
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// SPDX-FileCopyrightText: 2023 Iconica
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class TimelinePosterUserModel {
|
||||
const TimelinePosterUserModel({
|
||||
required this.id,
|
||||
this.firstName,
|
||||
this.lastName,
|
||||
this.imageUrl,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String? firstName;
|
||||
final String? lastName;
|
||||
final String? imageUrl;
|
||||
|
||||
String? get fullName {
|
||||
var fullName = '';
|
||||
|
||||
if (firstName != null && lastName != null) {
|
||||
fullName += '$firstName $lastName';
|
||||
} else if (firstName != null) {
|
||||
fullName += firstName!;
|
||||
} else if (lastName != null) {
|
||||
fullName += lastName!;
|
||||
}
|
||||
|
||||
return fullName == '' ? null : fullName;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2023 Iconica
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_image_picker/flutter_image_picker.dart';
|
||||
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||
import 'package:flutter_timeline_view/src/config/timeline_translations.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
|
@ -15,6 +16,7 @@ class TimelineOptions {
|
|||
this.dateformat,
|
||||
this.buttonBuilder,
|
||||
this.textInputBuilder,
|
||||
this.userAvatarBuilder,
|
||||
});
|
||||
|
||||
/// The format to display the post time in
|
||||
|
@ -26,6 +28,8 @@ class TimelineOptions {
|
|||
|
||||
final TextInputBuilder? textInputBuilder;
|
||||
|
||||
final UserAvatarBuilder? userAvatarBuilder;
|
||||
|
||||
/// ImagePickerTheme can be used to change the UI of the
|
||||
/// Image Picker Widget to change the text/icons to your liking.
|
||||
final ImagePickerTheme imagePickerTheme;
|
||||
|
@ -42,9 +46,13 @@ typedef ButtonBuilder = Widget Function(
|
|||
bool enabled,
|
||||
});
|
||||
|
||||
|
||||
typedef TextInputBuilder = Widget Function(
|
||||
TextEditingController controller,
|
||||
Widget? suffixIcon,
|
||||
String hintText,
|
||||
);
|
||||
|
||||
typedef UserAvatarBuilder = Widget Function(
|
||||
TimelinePosterUserModel user,
|
||||
double size,
|
||||
);
|
||||
|
|
|
@ -11,7 +11,6 @@ class TimelinePostScreen extends StatelessWidget {
|
|||
super.key,
|
||||
});
|
||||
|
||||
|
||||
final TimelinePost post;
|
||||
|
||||
@override
|
||||
|
|
|
@ -4,16 +4,22 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||
import 'package:flutter_timeline_view/src/config/timeline_options.dart';
|
||||
import 'package:flutter_timeline_view/src/widgets/timeline_post_widget.dart';
|
||||
|
||||
class TimelineScreen extends StatefulWidget {
|
||||
const TimelineScreen({
|
||||
required this.options,
|
||||
required this.posts,
|
||||
required this.onPostTap,
|
||||
this.controller,
|
||||
this.timelineCategoryFilter,
|
||||
this.timelinePostHeight = 100.0,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final TimelineOptions options;
|
||||
|
||||
final ScrollController? controller;
|
||||
|
||||
final String? timelineCategoryFilter;
|
||||
|
@ -22,6 +28,8 @@ class TimelineScreen extends StatefulWidget {
|
|||
|
||||
final List<TimelinePost> posts;
|
||||
|
||||
final Function(TimelinePost) onPostTap;
|
||||
|
||||
@override
|
||||
State<TimelineScreen> createState() => _TimelineScreenState();
|
||||
}
|
||||
|
@ -36,5 +44,22 @@ class _TimelineScreenState extends State<TimelineScreen> {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => const Placeholder();
|
||||
Widget build(BuildContext context) => SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
for (var post in widget.posts)
|
||||
if (widget.timelineCategoryFilter == null ||
|
||||
post.category == widget.timelineCategoryFilter)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: TimelinePostWidget(
|
||||
options: widget.options,
|
||||
post: post,
|
||||
height: widget.timelinePostHeight,
|
||||
onTap: () => widget.onPostTap.call(post),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
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 TimelinePostWidget extends StatelessWidget {
|
||||
const TimelinePostWidget({
|
||||
required this.options,
|
||||
required this.post,
|
||||
required this.height,
|
||||
this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final TimelineOptions options;
|
||||
|
||||
final TimelinePost post;
|
||||
final double height;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (post.creator != null)
|
||||
Row(
|
||||
children: [
|
||||
if (post.creator!.imageUrl != null) ...[
|
||||
options.userAvatarBuilder?.call(
|
||||
post.creator!,
|
||||
40,
|
||||
) ??
|
||||
CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundImage: CachedNetworkImageProvider(
|
||||
post.creator!.imageUrl!,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 10),
|
||||
if (post.creator!.fullName != null) ...[
|
||||
Text(
|
||||
post.creator!.fullName!,
|
||||
style: theme.textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
|
||||
// three small dots at the end
|
||||
const Spacer(),
|
||||
const Icon(Icons.more_horiz),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// image of the post
|
||||
if (post.imageUrl != null) ...[
|
||||
Flexible(
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: post.imageUrl!,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
),
|
||||
],
|
||||
// post information
|
||||
Row(
|
||||
children: [
|
||||
// like icon
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.thumb_up_rounded),
|
||||
),
|
||||
// comment icon
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(
|
||||
Icons.chat_bubble_outline_rounded,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'${post.likes} ${options.translations.likesTitle}',
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
post.creator?.fullName ?? '',
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
post.content,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
options.translations.viewPost,
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue