diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index f8d30dcee..fff58dae1 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -117,7 +117,8 @@ class BangumiInfo extends StatefulWidget { State createState() => _BangumiInfoState(); } -class _BangumiInfoState extends State { +class _BangumiInfoState extends State + with SingleTickerProviderStateMixin { String heroTag = Get.arguments['heroTag']; late final BangumiIntroController bangumiIntroController; late final VideoDetailController videoDetailCtr; @@ -157,10 +158,28 @@ class _BangumiInfoState extends State { } showModalBottomSheet( context: context, - useRootNavigator: true, + useSafeArea: true, isScrollControlled: true, + transitionAnimationController: AnimationController( + duration: const Duration(milliseconds: 200), + vsync: this, + ), + sheetAnimationStyle: AnimationStyle(curve: Curves.ease), builder: (BuildContext context) { - return FavPanel(ctr: bangumiIntroController); + return DraggableScrollableSheet( + minChildSize: 0, + maxChildSize: 1, + initialChildSize: 0.6, + snap: true, + expand: false, + snapSizes: const [0.6], + builder: (BuildContext context, ScrollController scrollController) { + return FavPanel( + ctr: bangumiIntroController, + scrollController: scrollController, + ); + }, + ); }, ); } diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 9dc9c1fdf..27b6395f1 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -216,7 +216,7 @@ class _RepostPanelState extends State { ?.url; return AnimatedSize( alignment: Alignment.topCenter, - curve: Curves.linearToEaseOut, + curve: Curves.ease, duration: const Duration(milliseconds: 300), child: Column( mainAxisSize: _isMax ? MainAxisSize.max : MainAxisSize.min, @@ -345,7 +345,10 @@ class _RepostPanelState extends State { padding: const EdgeInsets.all(10), margin: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.onInverseSurface, + color: Theme.of(context).colorScheme.surfaceContainerHigh == + Theme.of(context).colorScheme.surface + ? Theme.of(context).colorScheme.onInverseSurface + : Theme.of(context).colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(12), ), child: Row( diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index bdfa16bfa..8fa55948f 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -42,7 +42,7 @@ class VideoIntroPanel extends StatefulWidget { } class _VideoIntroPanelState extends State - with AutomaticKeepAliveClientMixin { + with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { late String heroTag; late VideoIntroController videoIntroController; VideoDetailData? videoDetail; @@ -164,6 +164,33 @@ class _VideoInfoState extends State with TickerProviderStateMixin { enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); } + void _showFavBottomSheet() => showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + transitionAnimationController: AnimationController( + duration: const Duration(milliseconds: 200), + vsync: this, + ), + sheetAnimationStyle: AnimationStyle(curve: Curves.ease), + builder: (BuildContext context) { + return DraggableScrollableSheet( + minChildSize: 0, + maxChildSize: 1, + initialChildSize: 0.6, + snap: true, + expand: false, + snapSizes: const [0.6], + builder: (BuildContext context, ScrollController scrollController) { + return FavPanel( + ctr: videoIntroController, + scrollController: scrollController, + ); + }, + ); + }, + ); + // 收藏 showFavBottomSheet({type = 'tap'}) { if (videoIntroController.userInfo == null) { @@ -179,24 +206,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { if (type == 'tap') { videoIntroController.actionFavVideo(type: 'default'); } else { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavBottomSheet(); } } else if (type != 'longPress') { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavBottomSheet(); } } diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 799160cf6..6628e596a 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -6,8 +6,14 @@ import 'package:PiliPalaX/utils/feed_back.dart'; import '../../../../../utils/utils.dart'; class FavPanel extends StatefulWidget { - const FavPanel({super.key, this.ctr}); + const FavPanel({ + super.key, + this.ctr, + this.scrollController, + }); + final dynamic ctr; + final ScrollController? scrollController; @override State createState() => _FavPanelState(); @@ -24,118 +30,142 @@ class _FavPanelState extends State { @override Widget build(BuildContext context) { - return Container( - height: Utils.getSheetHeight(context), - color: Theme.of(context).colorScheme.surface, - child: Column( - children: [ - AppBar( - centerTitle: false, - elevation: 0, - leading: IconButton( - tooltip: '关闭', - onPressed: () => Get.back(), - icon: const Icon(Icons.close_outlined)), - title: - Text('添加到收藏夹', style: Theme.of(context).textTheme.titleMedium), - ), - Expanded( - child: Material( - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - return Obx( - () => ListView.builder( - itemCount: - widget.ctr!.favFolderData.value.list!.length, - itemBuilder: (context, index) { - return ListTile( - onTap: () => widget.ctr!.onChoose( - widget.ctr!.favFolderData.value.list![index] - .favState != - 1, - index), - dense: true, - leading: const Icon(Icons.folder_outlined), - minLeadingWidth: 0, - title: Text(widget.ctr!.favFolderData.value - .list![index].title!), - subtitle: Text( - '${widget.ctr!.favFolderData.value.list![index].mediaCount}个内容', - ), - trailing: Transform.scale( - scale: 0.9, - child: Checkbox( - value: widget.ctr!.favFolderData.value - .list![index].favState == - 1, - onChanged: (bool? checkValue) => - widget.ctr!.onChoose(checkValue!, index), + return NotificationListener( + onNotification: (notification) { + if (notification.extent <= 1e-5) { + Get.back(); + } + return false; + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + color: Theme.of(context).colorScheme.surface, + ), + child: Column( + children: [ + AppBar( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(18), + ), + ), + centerTitle: false, + elevation: 0, + leading: IconButton( + tooltip: '关闭', + onPressed: () => Get.back(), + icon: const Icon(Icons.close_outlined)), + title: Text('添加到收藏夹', + style: Theme.of(context).textTheme.titleMedium), + ), + Expanded( + child: Material( + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + return Obx( + () => ListView.builder( + controller: widget.scrollController, + itemCount: + widget.ctr!.favFolderData.value.list!.length, + itemBuilder: (context, index) { + return ListTile( + onTap: () => widget.ctr!.onChoose( + widget.ctr!.favFolderData.value.list![index] + .favState != + 1, + index), + dense: true, + leading: const Icon(Icons.folder_outlined), + minLeadingWidth: 0, + title: Text(widget.ctr!.favFolderData.value + .list![index].title!), + subtitle: Text( + '${widget.ctr!.favFolderData.value.list![index].mediaCount}个内容', ), - ), - ); - }, - ), - ); + trailing: Transform.scale( + scale: 0.9, + child: Checkbox( + value: widget.ctr!.favFolderData.value + .list![index].favState == + 1, + onChanged: (bool? checkValue) => widget.ctr! + .onChoose(checkValue!, index), + ), + ), + ); + }, + ), + ); + } else { + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ); + } } else { - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ); + // 骨架屏 + return const Text('请求中'); } - } else { - // 骨架屏 - return const Text('请求中'); - } - }, + }, + ), ), ), - ), - Divider( - height: 1, - color: Theme.of(context).disabledColor.withOpacity(0.08), - ), - Padding( - padding: EdgeInsets.only( - left: 20, - right: 20, - top: 12, - bottom: MediaQuery.of(context).padding.bottom + 12, + Divider( + height: 1, + color: Theme.of(context).disabledColor.withOpacity(0.08), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () => Get.back(), - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 30, right: 30), - backgroundColor: Theme.of(context) - .colorScheme - .onInverseSurface, // 设置按钮背景色 + Padding( + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 12, + bottom: MediaQuery.of(context).padding.bottom + 12, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => Get.back(), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + visualDensity: const VisualDensity( + horizontal: -1, + vertical: -2, + ), + foregroundColor: Theme.of(context).colorScheme.outline, + backgroundColor: Theme.of(context) + .colorScheme + .onInverseSurface, // 设置按钮背景色 + ), + child: const Text('取消'), ), - child: const Text('取消'), - ), - const SizedBox(width: 10), - TextButton( - onPressed: () async { - feedBack(); - await widget.ctr!.actionFavVideo(); - }, - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 30, right: 30), - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: - Theme.of(context).colorScheme.primary, // 设置按钮背景色 + const SizedBox(width: 25), + FilledButton.tonal( + onPressed: () async { + feedBack(); + await widget.ctr!.actionFavVideo(); + }, + style: FilledButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + visualDensity: const VisualDensity( + horizontal: -1, + vertical: -2, + ), + ), + child: const Text('完成'), ), - child: const Text('完成'), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ); }