💫 Better animated post list

This commit is contained in:
LittleSheep 2024-08-02 04:42:38 +08:00
parent 70617be687
commit aefcbad02f
6 changed files with 203 additions and 167 deletions

View File

@ -145,7 +145,7 @@ PODS:
- SDWebImage/Core (= 5.19.4)
- SDWebImage/Core (5.19.4)
- Sentry/HybridSDK (8.32.0)
- sentry_flutter (8.5.0):
- sentry_flutter (8.6.0):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 8.32.0)
@ -309,7 +309,7 @@ SPEC CHECKSUMS:
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d
Sentry: 96ae1dcdf01a644bc3a3b1dc279cecaf48a833fb
sentry_flutter: f1d86adcb93a959bc47a40d8d55059bdf7569bc5
sentry_flutter: 090351ce1ff5f96a4b33ef9455b7e3b28185387d
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec

View File

@ -9,8 +9,13 @@ import 'package:solian/widgets/posts/post_replies.dart';
class PostDetailScreen extends StatefulWidget {
final String id;
final Post? post;
const PostDetailScreen({super.key, required this.id});
const PostDetailScreen({
super.key,
required this.id,
this.post,
});
@override
State<PostDetailScreen> createState() => _PostDetailScreenState();
@ -20,6 +25,11 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
Post? item;
Future<Post?> getDetail() async {
if (widget.post != null) {
item = widget.post;
return widget.post;
}
final PostProvider provider = Get.find();
try {

View File

@ -8,13 +8,15 @@ import 'package:solian/widgets/app_bar_leading.dart';
class TitleShell extends StatelessWidget {
final bool showAppBar;
final bool isCenteredTitle;
final GoRouterState state;
final String? title;
final GoRouterState? state;
final Widget child;
const TitleShell({
super.key,
required this.child,
required this.state,
this.title,
this.state,
this.showAppBar = true,
this.isCenteredTitle = false,
});
@ -25,7 +27,9 @@ class TitleShell extends StatelessWidget {
appBar: showAppBar
? AppBar(
leading: AppBarLeadingButton.adaptive(context),
title: AppBarTitle(state.topRoute?.name?.tr ?? 'page'.tr),
title: AppBarTitle(
title ?? (state!.topRoute?.name?.tr ?? 'page'.tr),
),
centerTitle: isCenteredTitle,
toolbarHeight: SolianTheme.toolbarHeight(context),
)

View File

@ -1,9 +1,11 @@
import 'package:animations/animations.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get_utils/get_utils.dart';
import 'package:intl/intl.dart';
import 'package:solian/models/post.dart';
import 'package:solian/router.dart';
import 'package:solian/screens/posts/post_detail.dart';
import 'package:solian/shells/title_shell.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/account/account_profile_popup.dart';
import 'package:solian/widgets/attachments/attachment_list.dart';
@ -159,66 +161,88 @@ class _PostItemState extends State<PostItem> {
}
Widget _buildReply(BuildContext context) {
return Column(
children: [
Row(
children: [
FaIcon(
FontAwesomeIcons.reply,
size: 16,
color: _unFocusColor,
),
Expanded(
child: Text(
'postRepliedNotify'.trParams(
{'username': '@${widget.item.replyTo!.author.name}'},
),
style: TextStyle(color: _unFocusColor),
).paddingOnly(left: 6),
),
],
).paddingOnly(left: 12),
Card(
elevation: 1,
child: PostItem(
item: widget.item.replyTo!,
isCompact: true,
overrideAttachmentParent: widget.item.id.toString(),
).paddingSymmetric(vertical: 8),
return OpenContainer(
closedBuilder: (_, openContainer) => Column(
children: [
Row(
children: [
FaIcon(
FontAwesomeIcons.reply,
size: 16,
color: _unFocusColor,
),
Expanded(
child: Text(
'postRepliedNotify'.trParams(
{'username': '@${widget.item.replyTo!.author.name}'},
),
style: TextStyle(color: _unFocusColor),
).paddingOnly(left: 6),
),
],
).paddingOnly(left: 12),
Card(
elevation: 1,
child: PostItem(
item: widget.item.replyTo!,
isCompact: true,
overrideAttachmentParent: widget.item.id.toString(),
).paddingSymmetric(vertical: 8),
),
],
),
openBuilder: (_, __) => TitleShell(
title: 'postDetail'.tr,
child: PostDetailScreen(
id: widget.item.replyTo!.id.toString(),
post: widget.item.replyTo!,
),
],
),
closedColor: Theme.of(context).colorScheme.surface,
openColor: Theme.of(context).colorScheme.surface,
);
}
Widget _buildRepost(BuildContext context) {
return Column(
children: [
Row(
children: [
FaIcon(
FontAwesomeIcons.retweet,
size: 16,
color: _unFocusColor,
),
Expanded(
child: Text(
'postRepostedNotify'.trParams(
{'username': '@${widget.item.repostTo!.author.name}'},
),
style: TextStyle(color: _unFocusColor),
).paddingOnly(left: 6),
),
],
).paddingOnly(left: 12),
Card(
elevation: 1,
child: PostItem(
item: widget.item.repostTo!,
isCompact: true,
overrideAttachmentParent: widget.item.id.toString(),
).paddingSymmetric(vertical: 8),
return OpenContainer(
closedBuilder: (_, openContainer) => Column(
children: [
Row(
children: [
FaIcon(
FontAwesomeIcons.retweet,
size: 16,
color: _unFocusColor,
),
Expanded(
child: Text(
'postRepostedNotify'.trParams(
{'username': '@${widget.item.repostTo!.author.name}'},
),
style: TextStyle(color: _unFocusColor),
).paddingOnly(left: 6),
),
],
).paddingOnly(left: 12),
Card(
elevation: 1,
child: PostItem(
item: widget.item.repostTo!,
isCompact: true,
overrideAttachmentParent: widget.item.id.toString(),
).paddingSymmetric(vertical: 8),
),
],
),
openBuilder: (_, __) => TitleShell(
title: 'postDetail'.tr,
child: PostDetailScreen(
id: widget.item.repostTo!.id.toString(),
post: widget.item.repostTo!,
),
],
),
closedColor: Theme.of(context).colorScheme.surface,
openColor: Theme.of(context).colorScheme.surface,
);
}
@ -264,100 +288,89 @@ class _PostItemState extends State<PostItem> {
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
child: AccountAvatar(content: item.author.avatar.toString()),
onTap: () {
showModalBottomSheet(
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: Theme.of(context).colorScheme.surface,
context: context,
builder: (context) => AccountProfilePopup(
account: item.author,
),
);
},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(),
SizedContainer(
maxWidth: 640,
child: MarkdownTextContent(
content: item.body['content'],
isSelectable: widget.isContentSelectable,
).paddingOnly(left: 12, right: 8),
),
if (widget.item.replyTo != null && widget.isShowEmbed)
GestureDetector(
child: _buildReply(context).paddingOnly(top: 4),
onTap: () {
if (!widget.isClickable) return;
AppRouter.instance.pushNamed(
'postDetail',
pathParameters: {
'id': widget.item.replyTo!.id.toString(),
},
);
},
return OpenContainer(
closedBuilder: (_, openContainer) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
child: AccountAvatar(content: item.author.avatar.toString()),
onTap: () {
showModalBottomSheet(
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: Theme.of(context).colorScheme.surface,
context: context,
builder: (context) => AccountProfilePopup(
account: item.author,
),
if (widget.item.repostTo != null && widget.isShowEmbed)
GestureDetector(
child: _buildRepost(context).paddingOnly(top: 4),
onTap: () {
if (!widget.isClickable) return;
AppRouter.instance.pushNamed(
'postDetail',
pathParameters: {
'alias': widget.item.repostTo!.id.toString(),
},
);
},
),
_buildFooter().paddingOnly(left: 12),
],
);
},
),
),
],
).paddingOnly(
top: 10,
bottom: hasAttachment ? 10 : 0,
right: 16,
left: 16,
),
AttachmentList(
parentId: widget.item.id.toString(),
attachmentsId: attachments,
isGrid: attachments.length > 1,
),
if (widget.isShowReply && widget.isReactable)
PostQuickAction(
isShowReply: widget.isShowReply,
isReactable: widget.isReactable,
item: widget.item,
onReact: (symbol, changes) {
setState(() {
item.metric!.reactionList[symbol] =
(item.metric!.reactionList[symbol] ?? 0) + changes;
});
},
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(),
SizedContainer(
maxWidth: 640,
child: MarkdownTextContent(
content: item.body['content'],
isSelectable: widget.isContentSelectable,
).paddingOnly(left: 12, right: 8),
),
if (widget.item.replyTo != null && widget.isShowEmbed)
_buildReply(context).paddingOnly(top: 4),
if (widget.item.repostTo != null && widget.isShowEmbed)
_buildRepost(context).paddingOnly(top: 4),
_buildFooter().paddingOnly(left: 12),
],
),
),
],
).paddingOnly(
top: hasAttachment ? 10 : 6,
left: hasAttachment ? 24 : 60,
top: 10,
bottom: hasAttachment ? 10 : 0,
right: 16,
bottom: 10,
)
else
const SizedBox(height: 10),
],
left: 16,
),
AttachmentList(
parentId: widget.item.id.toString(),
attachmentsId: attachments,
isGrid: attachments.length > 1,
),
if (widget.isShowReply && widget.isReactable)
PostQuickAction(
isShowReply: widget.isShowReply,
isReactable: widget.isReactable,
item: widget.item,
onReact: (symbol, changes) {
setState(() {
item.metric!.reactionList[symbol] =
(item.metric!.reactionList[symbol] ?? 0) + changes;
});
},
).paddingOnly(
top: hasAttachment ? 10 : 6,
left: hasAttachment ? 24 : 60,
right: 16,
bottom: 10,
)
else
const SizedBox(height: 10),
],
),
openBuilder: (_, __) => TitleShell(
title: 'postDetail'.tr,
child: PostDetailScreen(
id: item.id.toString(),
post: item,
),
),
closedColor: Theme.of(context).colorScheme.surface,
openColor: Theme.of(context).colorScheme.surface,
);
}
}

View File

@ -25,6 +25,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.4.1"
animations:
dependency: "direct main"
description:
name: animations
sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb
url: "https://pub.dev"
source: hosted
version: "2.0.11"
archive:
dependency: transitive
description:
@ -133,26 +141,26 @@ packages:
dependency: "direct main"
description:
name: cached_network_image
sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f"
sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
version: "3.4.0"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f"
sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "4.1.0"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7"
sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
carousel_slider:
dependency: "direct main"
description:
@ -301,10 +309,10 @@ packages:
dependency: transitive
description:
name: dev_build
sha256: "5600100e28f7424ed53728e8e7aa6bc0e0506ec04bb49a82616f62112c1822c3"
sha256: "76dd5b2587a891ab9c1e7f7eea6ca4a3504667321e0186ecb1e385183889d89b"
url: "https://pub.dev"
source: hosted
version: "1.0.0+8"
version: "1.0.0+10"
device_info_plus:
dependency: "direct main"
description:
@ -1092,10 +1100,10 @@ packages:
dependency: transitive
description:
name: octo_image
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.1.0"
package_config:
dependency: transitive
description:
@ -1436,18 +1444,18 @@ packages:
dependency: transitive
description:
name: sentry
sha256: "60756499f09c3ed944640d7993ac527a89f7c3033f13ec12ae145706b269b5c8"
sha256: "76ad4fab90ff82427c26939bd79dc4df345a081e2b1cd5954b947e340b9af9a5"
url: "https://pub.dev"
source: hosted
version: "8.5.0"
version: "8.6.0"
sentry_flutter:
dependency: "direct main"
description:
name: sentry_flutter
sha256: "26cfe89cb08a60d9bc0c4e748a0508e223ae378265aec8ed2a2b48f0d2c936b9"
sha256: a7c92014701093a7c0a373e1a47c54ec428d8468d8bf2b793fee7ea1b085c21b
url: "https://pub.dev"
source: hosted
version: "8.5.0"
version: "8.6.0"
share_plus:
dependency: "direct main"
description:
@ -1468,10 +1476,10 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: c3f888ba2d659f3e75f4686112cc1e71f46177f74452d40d8307edc332296ead
sha256: c272f9cabca5a81adc9b0894381e9c1def363e980f960fa903c604c471b22f68
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.3.1"
shared_preferences_android:
dependency: transitive
description:

View File

@ -66,6 +66,7 @@ dependencies:
dio: ^5.5.0+1
image_cropper: ^8.0.1
markdown_toolbar: ^0.5.0
animations: ^2.0.11
dev_dependencies:
flutter_test: