diff --git a/lib/common/widgets/button/icon_button.dart b/lib/common/widgets/button/icon_button.dart index 94689ef70..3476c302b 100644 --- a/lib/common/widgets/button/icon_button.dart +++ b/lib/common/widgets/button/icon_button.dart @@ -41,8 +41,8 @@ Widget mediumButton({ child: IconButton( tooltip: tooltip, icon: Icon(icon), - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: onPressed, ), diff --git a/lib/common/widgets/button/toolbar_icon_button.dart b/lib/common/widgets/button/toolbar_icon_button.dart index 9ca8e28f6..e9c78cc82 100644 --- a/lib/common/widgets/button/toolbar_icon_button.dart +++ b/lib/common/widgets/button/toolbar_icon_button.dart @@ -29,7 +29,7 @@ class ToolbarIconButton extends StatelessWidget { ? theme.colorScheme.onSecondaryContainer : theme.colorScheme.outline, style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: const WidgetStatePropertyAll(EdgeInsets.zero), backgroundColor: WidgetStatePropertyAll( selected ? theme.colorScheme.secondaryContainer : null, ), diff --git a/lib/common/widgets/custom_toast.dart b/lib/common/widgets/custom_toast.dart index 7c6a64b40..70e8c1418 100644 --- a/lib/common/widgets/custom_toast.dart +++ b/lib/common/widgets/custom_toast.dart @@ -13,7 +13,7 @@ class CustomToast extends StatelessWidget { final ThemeData theme = Theme.of(context); return Container( margin: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 30, + bottom: MediaQuery.viewPaddingOf(context).bottom + 30, ), padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10), decoration: BoxDecoration( diff --git a/lib/common/widgets/dynamic_sliver_appbar_medium.dart b/lib/common/widgets/dynamic_sliver_appbar_medium.dart index 333ea7380..be8d03d9f 100644 --- a/lib/common/widgets/dynamic_sliver_appbar_medium.dart +++ b/lib/common/widgets/dynamic_sliver_appbar_medium.dart @@ -146,7 +146,7 @@ class _DynamicSliverAppBarMediumState extends State { ); } - final padding = MediaQuery.paddingOf(context).top; + final padding = MediaQuery.viewPaddingOf(context).top; return SliverAppBar.medium( leading: widget.leading, automaticallyImplyLeading: widget.automaticallyImplyLeading, diff --git a/lib/common/widgets/image/image_save.dart b/lib/common/widgets/image/image_save.dart index e3202d5f6..d9d77fdd7 100644 --- a/lib/common/widgets/image/image_save.dart +++ b/lib/common/widgets/image/image_save.dart @@ -67,12 +67,12 @@ void imageSaveDialog({ color: Colors.black.withValues(alpha: 0.3), shape: BoxShape.circle, ), - child: IconButton( + child: const IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: SmartDialog.dismiss, - icon: const Icon( + icon: Icon( Icons.close, size: 18, color: Colors.white, diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index 1870d63bc..4631323ac 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -335,7 +335,7 @@ class _InteractiveviewerGalleryState extends State right: 0, child: Container( padding: - MediaQuery.paddingOf(context) + + MediaQuery.viewPaddingOf(context) + const EdgeInsets.fromLTRB(12, 8, 20, 8), decoration: _enablePageView ? BoxDecoration( diff --git a/lib/common/widgets/loading_widget/http_error.dart b/lib/common/widgets/loading_widget/http_error.dart index fa740d881..5a7b28fe6 100644 --- a/lib/common/widgets/loading_widget/http_error.dart +++ b/lib/common/widgets/loading_widget/http_error.dart @@ -60,7 +60,7 @@ class HttpError extends StatelessWidget { style: TextStyle(color: theme.colorScheme.primary), ), ), - SizedBox(height: 40 + MediaQuery.paddingOf(context).bottom), + SizedBox(height: 40 + MediaQuery.viewPaddingOf(context).bottom), ], ); } diff --git a/lib/common/widgets/view_safe_area.dart b/lib/common/widgets/view_safe_area.dart new file mode 100644 index 000000000..faac79184 --- /dev/null +++ b/lib/common/widgets/view_safe_area.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class ViewSafeArea extends StatelessWidget { + const ViewSafeArea({ + super.key, + this.top = false, + this.left = true, + this.right = true, + required this.child, + }); + + final bool top; + final bool left; + final bool right; + final Widget child; + + @override + Widget build(BuildContext context) { + EdgeInsets padding = MediaQuery.viewPaddingOf(context); + return Padding( + padding: EdgeInsets.only( + top: top ? padding.top : 0.0, + left: left ? padding.left : 0.0, + right: right ? padding.right : 0.0, + ), + child: child, + ); + } +} diff --git a/lib/common/widgets/view_sliver_safe_area.dart b/lib/common/widgets/view_sliver_safe_area.dart new file mode 100644 index 000000000..455b89973 --- /dev/null +++ b/lib/common/widgets/view_sliver_safe_area.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class ViewSliverSafeArea extends StatelessWidget { + const ViewSliverSafeArea({ + super.key, + required this.sliver, + }); + + final Widget sliver; + + @override + Widget build(BuildContext context) { + EdgeInsets padding = MediaQuery.viewPaddingOf(context); + return SliverPadding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + bottom: padding.bottom + 100, + ), + sliver: sliver, + ); + } +} diff --git a/lib/pages/about/view.dart b/lib/pages/about/view.dart index 357879369..ca6627df2 100644 --- a/lib/pages/about/view.dart +++ b/lib/pages/about/view.dart @@ -76,7 +76,11 @@ class _AboutPageState extends State { appBar: widget.showAppBar == false ? null : AppBar(title: const Text('关于')), + resizeToAvoidBottomInset: false, body: ListView( + padding: EdgeInsets.only( + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, + ), children: [ GestureDetector( onTap: () { @@ -298,7 +302,6 @@ Commit Hash: ${BuildConfig.commitHash}''', }, ), ), - const SizedBox(height: 80), ], ), ); diff --git a/lib/pages/article/view.dart b/lib/pages/article/view.dart index c0b947c9e..72b685ec3 100644 --- a/lib/pages/article/view.dart +++ b/lib/pages/article/view.dart @@ -75,9 +75,11 @@ class _ArticlePageState extends CommonDynPageState { return Scaffold( resizeToAvoidBottomInset: false, appBar: _buildAppBar(isPortrait), - body: SafeArea( - top: false, - bottom: false, + body: Padding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + ), child: Stack( clipBehavior: Clip.none, children: [ @@ -140,7 +142,7 @@ class _ArticlePageState extends CommonDynPageState { SliverPadding( padding: EdgeInsets.only( left: padding, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: this.padding.bottom + 100, ), sliver: _buildContent( theme, @@ -159,6 +161,7 @@ class _ArticlePageState extends CommonDynPageState { child: Scaffold( key: scaffoldKey, backgroundColor: Colors.transparent, + resizeToAvoidBottomInset: false, body: refreshIndicator( onRefresh: controller.onRefresh, child: Padding( @@ -438,9 +441,7 @@ class _ArticlePageState extends CommonDynPageState { controller.onLoadMore(); return Container( alignment: Alignment.center, - margin: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom, - ), + margin: EdgeInsets.only(bottom: padding.bottom), height: 125, child: Text( controller.isEnd ? '没有更多了' : '加载中...', @@ -633,12 +634,14 @@ class _ArticlePageState extends CommonDynPageState { child: const Icon(Icons.reply), ); - final bottom = MediaQuery.paddingOf(context).bottom; if (!controller.showDynActionBar) { return Align( alignment: Alignment.bottomRight, child: Padding( - padding: EdgeInsets.only(right: 14, bottom: bottom + 14), + padding: EdgeInsets.only( + right: 14, + bottom: padding.bottom + 14, + ), child: button(), ), ); @@ -681,7 +684,7 @@ class _ArticlePageState extends CommonDynPageState { Widget btn = Padding( padding: EdgeInsets.only( right: 14, - bottom: 14 + (stats != null ? 0 : bottom), + bottom: 14 + (stats != null ? 0 : padding.bottom), ), child: button(), ); @@ -709,7 +712,7 @@ class _ArticlePageState extends CommonDynPageState { ), ), ), - padding: EdgeInsets.only(bottom: bottom), + padding: EdgeInsets.only(bottom: padding.bottom), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ diff --git a/lib/pages/article_list/view.dart b/lib/pages/article_list/view.dart index 99b9ead51..15bd1e31e 100644 --- a/lib/pages/article_list/view.dart +++ b/lib/pages/article_list/view.dart @@ -29,29 +29,31 @@ class _ArticleListPageState extends State with GridMixin { tag: Utils.generateRandomString(8), ); + late EdgeInsets padding; + @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - return Scaffold( - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _controller.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - Obx(() => _buildHeader(theme, _controller.list.value)), - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx( - () => _buildBody(theme, _controller.loadingState.value), - ), + padding = MediaQuery.viewPaddingOf(context); + return Material( + color: theme.colorScheme.surface, + child: refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + Obx(() => _buildHeader(theme, _controller.list.value)), + SliverPadding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + bottom: padding.bottom + 100, ), - ], - ), + sliver: Obx( + () => _buildBody(theme, _controller.loadingState.value), + ), + ), + ], ), ), ); @@ -60,7 +62,7 @@ class _ArticleListPageState extends State with GridMixin { @override Widget get gridSkeleton => SliverPadding( padding: EdgeInsets.only( - top: MediaQuery.paddingOf(context).top + kToolbarHeight + 120, + top: padding.top + kToolbarHeight + 120, ), sliver: super.gridSkeleton, ); @@ -96,7 +98,6 @@ class _ArticleListPageState extends State with GridMixin { text: ' | ', style: TextStyle(color: theme.colorScheme.outline.withValues(alpha: 0.7)), ); - final padding = MediaQuery.paddingOf(context).top + kToolbarHeight; return SliverAppBar.medium( title: Text(item.name!), pinned: true, @@ -105,9 +106,9 @@ class _ArticleListPageState extends State with GridMixin { background: Container( height: 120, margin: EdgeInsets.only( - left: 12, + left: 12 + padding.left, right: 12, - top: padding, + top: padding.top + kToolbarHeight, ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/blacklist/view.dart b/lib/pages/blacklist/view.dart index bfc7beadf..c6f92d380 100644 --- a/lib/pages/blacklist/view.dart +++ b/lib/pages/blacklist/view.dart @@ -39,6 +39,7 @@ class _BlackListPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Obx( () => Text( @@ -54,7 +55,7 @@ class _BlackListPageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(_blackListController.loadingState.value), diff --git a/lib/pages/common/dyn/common_dyn_page.dart b/lib/pages/common/dyn/common_dyn_page.dart index dbed3ee65..fac24ccc5 100644 --- a/lib/pages/common/dyn/common_dyn_page.dart +++ b/lib/pages/common/dyn/common_dyn_page.dart @@ -30,6 +30,8 @@ abstract class CommonDynPageState extends State dynamic get arguments; + late EdgeInsets padding; + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -44,6 +46,7 @@ abstract class CommonDynPageState extends State ); } : null; + padding = MediaQuery.viewPaddingOf(context); } Widget buildReplyHeader(ThemeData theme) { @@ -109,9 +112,7 @@ abstract class CommonDynPageState extends State controller.onLoadMore(); return Container( alignment: Alignment.center, - margin: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom, - ), + margin: EdgeInsets.only(bottom: padding.bottom), height: 125, child: Text( controller.isEnd ? '没有更多了' : '加载中...', @@ -163,6 +164,7 @@ abstract class CommonDynPageState extends State int oid = replyItem.oid.toInt(); int rpid = replyItem.id.toInt(); Widget replyReplyPage({bool showBackBtn = true}) => Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( toolbarHeight: showBackBtn ? null : 45, title: const Text('评论详情'), @@ -178,9 +180,8 @@ abstract class CommonDynPageState extends State ), ], ), - body: SafeArea( - top: false, - bottom: false, + body: Padding( + padding: EdgeInsets.only(right: padding.right), child: VideoReplyReplyPanel( enableSlide: false, id: id, diff --git a/lib/pages/common/search/common_search_page.dart b/lib/pages/common/search/common_search_page.dart index 584a181b2..7bf9c8891 100644 --- a/lib/pages/common/search/common_search_page.dart +++ b/lib/pages/common/search/common_search_page.dart @@ -1,5 +1,6 @@ import 'package:PiliPlus/common/widgets/appbar/appbar.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; +import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/pages/common/multi_select/base.dart'; import 'package:PiliPlus/pages/common/search/common_search_controller.dart'; @@ -39,23 +40,15 @@ abstract class CommonSearchPageState Widget _build(bool multiSelect) { return Scaffold( - resizeToAvoidBottomInset: false, appBar: _buildBar(multiSelect), - body: SafeArea( - top: false, - bottom: false, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - controller: controller.scrollController, - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx(() => _buildBody(controller.loadingState.value)), - ), - ], - ), + body: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: controller.scrollController, + slivers: [ + ViewSliverSafeArea( + sliver: Obx(() => _buildBody(controller.loadingState.value)), + ), + ], ), ); } diff --git a/lib/pages/contact/view.dart b/lib/pages/contact/view.dart index 9c8d55bcc..ff603819d 100644 --- a/lib/pages/contact/view.dart +++ b/lib/pages/contact/view.dart @@ -34,6 +34,7 @@ class _ContactPageState extends State @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('通讯录'), bottom: TabBar( diff --git a/lib/pages/danmaku_block/view.dart b/lib/pages/danmaku_block/view.dart index d333fc749..cfd14b057 100644 --- a/lib/pages/danmaku_block/view.dart +++ b/lib/pages/danmaku_block/view.dart @@ -85,7 +85,7 @@ class _DanmakuBlockPageState extends State { return ListView.builder( itemCount: list.length, padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), itemBuilder: (context, itemIndex) { final SimpleRule item = list[itemIndex]; diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 3639145c9..724569ef7 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -33,7 +33,7 @@ class _DynamicsPageState extends State child: IconButton( tooltip: '发布动态', style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: const WidgetStatePropertyAll(EdgeInsets.zero), backgroundColor: WidgetStatePropertyAll( theme.colorScheme.secondaryContainer, ), @@ -90,8 +90,10 @@ class _DynamicsPageState extends State super.build(context); ThemeData theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, appBar: AppBar( + primary: false, leading: upPanelPosition == UpPanelPosition.rightDrawer ? _createDynamicBtn(theme, false) : null, @@ -127,11 +129,11 @@ class _DynamicsPageState extends State : [_createDynamicBtn(theme)], ), drawer: upPanelPosition == UpPanelPosition.leftDrawer - ? SafeArea(child: upPanelPart(theme)) + ? upPanelPart(theme) : null, drawerEnableOpenDragGesture: true, endDrawer: upPanelPosition == UpPanelPosition.rightDrawer - ? SafeArea(child: upPanelPart(theme)) + ? upPanelPart(theme) : null, endDrawerEnableOpenDragGesture: true, body: Row( diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 51869a152..9ccf7229d 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -230,8 +230,8 @@ class AuthorPanel extends StatelessWidget { height: 32, child: IconButton( tooltip: '更多', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => morePanel(context), icon: const Icon(Icons.more_vert_outlined, size: 18), @@ -266,7 +266,7 @@ class AuthorPanel extends StatelessWidget { final theme = Theme.of(context); return Padding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context1).bottom, + bottom: MediaQuery.viewPaddingOf(context1).bottom, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/dynamics_create/view.dart b/lib/pages/dynamics_create/view.dart index 254250bb8..db6e39096 100644 --- a/lib/pages/dynamics_create/view.dart +++ b/lib/pages/dynamics_create/view.dart @@ -301,7 +301,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState { child: IconButton( tooltip: '返回', style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: const WidgetStatePropertyAll(EdgeInsets.zero), backgroundColor: WidgetStatePropertyAll( theme.colorScheme.secondaryContainer, ), diff --git a/lib/pages/dynamics_create_reserve/view.dart b/lib/pages/dynamics_create_reserve/view.dart index b90cdad1e..d7f8a92e8 100644 --- a/lib/pages/dynamics_create_reserve/view.dart +++ b/lib/pages/dynamics_create_reserve/view.dart @@ -30,7 +30,7 @@ class _CreateReservePageState extends State { fontSize: 15, color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.9), ); - final padding = MediaQuery.paddingOf(context); + final padding = MediaQuery.viewPaddingOf(context); final divider = [ const SizedBox(height: 10), Divider( @@ -46,7 +46,7 @@ class _CreateReservePageState extends State { top: 16, left: padding.left + 16, right: padding.right + 16, - bottom: padding.bottom + 80, + bottom: padding.bottom + 100, ), children: [ Row( diff --git a/lib/pages/dynamics_create_vote/view.dart b/lib/pages/dynamics_create_vote/view.dart index b7e809c33..71acf83fd 100644 --- a/lib/pages/dynamics_create_vote/view.dart +++ b/lib/pages/dynamics_create_vote/view.dart @@ -36,7 +36,7 @@ class _CreateVotePageState extends State { fontSize: 15, color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.9), ); - final padding = MediaQuery.paddingOf(context); + final padding = MediaQuery.viewPaddingOf(context); final divider = Divider( height: 20, thickness: 1, @@ -50,7 +50,7 @@ class _CreateVotePageState extends State { padding: EdgeInsets.only( left: padding.left + 16, right: padding.right + 16, - bottom: padding.bottom + 80, + bottom: padding.bottom + 100, ), children: [ const Text( diff --git a/lib/pages/dynamics_detail/view.dart b/lib/pages/dynamics_detail/view.dart index aea0f0988..2cfe2eb40 100644 --- a/lib/pages/dynamics_detail/view.dart +++ b/lib/pages/dynamics_detail/view.dart @@ -125,9 +125,11 @@ class _DynamicDetailPageState extends CommonDynPageState { const SizedBox(width: 16), ], ), - body: SafeArea( - top: false, - bottom: false, + body: Padding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + ), child: isPortrait ? refreshIndicator( onRefresh: controller.onRefresh, @@ -184,7 +186,7 @@ class _DynamicDetailPageState extends CommonDynPageState { SliverPadding( padding: EdgeInsets.only( left: padding / 4, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: this.padding.bottom + 100, ), sliver: SliverToBoxAdapter( child: LayoutBuilder( @@ -205,6 +207,7 @@ class _DynamicDetailPageState extends CommonDynPageState { child: Scaffold( key: scaffoldKey, backgroundColor: Colors.transparent, + resizeToAvoidBottomInset: false, body: refreshIndicator( onRefresh: controller.onRefresh, child: CustomScrollView( @@ -261,14 +264,13 @@ class _DynamicDetailPageState extends CommonDynPageState { child: const Icon(Icons.reply), ); - final bottom = MediaQuery.paddingOf(context).bottom; if (!controller.showDynActionBar) { return Align( alignment: Alignment.bottomRight, child: Padding( padding: EdgeInsets.only( right: 14, - bottom: bottom + 14, + bottom: padding.bottom + 14, ), child: button(), ), @@ -326,7 +328,7 @@ class _DynamicDetailPageState extends CommonDynPageState { ), ), ), - padding: EdgeInsets.only(bottom: bottom), + padding: EdgeInsets.only(bottom: padding.bottom), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ diff --git a/lib/pages/dynamics_mention/view.dart b/lib/pages/dynamics_mention/view.dart index cd02c678f..7352a71af 100644 --- a/lib/pages/dynamics_mention/view.dart +++ b/lib/pages/dynamics_mention/view.dart @@ -188,9 +188,7 @@ class _DynMentionPanelState extends SearchState { () => _buildBody(theme, _controller.loadingState.value), ), SliverToBoxAdapter( - child: SizedBox( - height: padding + viewInset + 80, - ), + child: SizedBox(height: padding + viewInset + 100), ), ], ), diff --git a/lib/pages/dynamics_repost/view.dart b/lib/pages/dynamics_repost/view.dart index d177fbc8b..7ee6af3f4 100644 --- a/lib/pages/dynamics_repost/view.dart +++ b/lib/pages/dynamics_repost/view.dart @@ -284,7 +284,7 @@ class _RepostPanelState extends CommonRichTextPubPageState { child: IconButton( tooltip: '返回', style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: const WidgetStatePropertyAll(EdgeInsets.zero), backgroundColor: WidgetStatePropertyAll( theme.colorScheme.secondaryContainer, ), @@ -353,7 +353,7 @@ class _RepostPanelState extends CommonRichTextPubPageState { ), ), ), - SizedBox(height: 10 + MediaQuery.paddingOf(context).bottom), + SizedBox(height: 10 + MediaQuery.viewPaddingOf(context).bottom), ]; @override diff --git a/lib/pages/dynamics_select_topic/view.dart b/lib/pages/dynamics_select_topic/view.dart index 24500445c..928db1c16 100644 --- a/lib/pages/dynamics_select_topic/view.dart +++ b/lib/pages/dynamics_select_topic/view.dart @@ -191,10 +191,7 @@ class _SelectTopicPanelState extends SearchState { response?.isNotEmpty == true ? ListView.builder( padding: EdgeInsets.only( - bottom: - MediaQuery.paddingOf(context).bottom + - MediaQuery.viewInsetsOf(context).bottom + - 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), controller: widget.scrollController, itemBuilder: (context, index) { diff --git a/lib/pages/dynamics_tab/view.dart b/lib/pages/dynamics_tab/view.dart index f869248a4..7aa8aa602 100644 --- a/lib/pages/dynamics_tab/view.dart +++ b/lib/pages/dynamics_tab/view.dart @@ -88,7 +88,7 @@ class _DynamicsTabPageState slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: buildPage( Obx(() => _buildBody(controller.loadingState.value)), diff --git a/lib/pages/dynamics_topic/view.dart b/lib/pages/dynamics_topic/view.dart index 46ad17cdc..4cab8241c 100644 --- a/lib/pages/dynamics_topic/view.dart +++ b/lib/pages/dynamics_topic/view.dart @@ -41,7 +41,7 @@ class _DynTopicPageState extends State with DynMixin { @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - final padding = MediaQuery.paddingOf(context); + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( resizeToAvoidBottomInset: false, floatingActionButton: FloatingActionButton.extended( @@ -61,91 +61,90 @@ class _DynTopicPageState extends State with DynMixin { icon: const Icon(CustomIcon.topic_tag, size: 20), label: const Text('参与话题'), ), - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _controller.onRefresh, - child: CustomScrollView( - controller: _controller.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - Obx( - () => _buildAppBar( - theme, - padding.top, - _controller.topState.value, - ), + body: refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + controller: _controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + Obx( + () => _buildAppBar( + theme, + padding, + _controller.topState.value, ), - Obx(() { - final allSortBy = _controller.topicSortByConf.value?.allSortBy; - if (allSortBy != null && allSortBy.isNotEmpty) { - return SliverPersistentHeader( - pinned: true, - delegate: CustomSliverPersistentHeaderDelegate( - extent: 30, - bgColor: theme.colorScheme.surface, - child: SizedBox( - height: 30, - child: Builder( - builder: (context) { - return Padding( - padding: const EdgeInsets.only( - left: 12, - bottom: 6, - ), - child: ToggleButtons( - fillColor: theme.colorScheme.secondaryContainer, - selectedColor: - theme.colorScheme.onSecondaryContainer, - constraints: const BoxConstraints( - minWidth: 54, - minHeight: 24, + ), + Obx(() { + final allSortBy = _controller.topicSortByConf.value?.allSortBy; + if (allSortBy != null && allSortBy.isNotEmpty) { + return SliverPersistentHeader( + pinned: true, + delegate: CustomSliverPersistentHeaderDelegate( + extent: 30, + needRebuild: true, + bgColor: theme.colorScheme.surface, + child: Container( + height: 30, + padding: EdgeInsets.only( + left: 12 + padding.left, + bottom: 6, + ), + child: Builder( + builder: (context) { + return ToggleButtons( + fillColor: theme.colorScheme.secondaryContainer, + selectedColor: + theme.colorScheme.onSecondaryContainer, + constraints: const BoxConstraints( + minWidth: 54, + minHeight: 24, + ), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + borderRadius: const BorderRadius.all( + Radius.circular(25), + ), + onPressed: (index) { + _controller.onSort(allSortBy[index].sortBy!); + (context as Element).markNeedsBuild(); + }, + isSelected: allSortBy.map((e) { + return e.sortBy == _controller.sortBy; + }).toList(), + children: allSortBy.map((e) { + return Text( + e.sortName!, + style: const TextStyle( + fontSize: 13, + height: 1, ), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - borderRadius: const BorderRadius.all( - Radius.circular(25), + strutStyle: const StrutStyle( + height: 1, + leading: 0, + fontSize: 13, ), - onPressed: (index) { - _controller.onSort(allSortBy[index].sortBy!); - (context as Element).markNeedsBuild(); - }, - isSelected: allSortBy.map((e) { - return e.sortBy == _controller.sortBy; - }).toList(), - children: allSortBy.map((e) { - return Text( - e.sortName!, - style: const TextStyle( - fontSize: 13, - height: 1, - ), - strutStyle: const StrutStyle( - height: 1, - leading: 0, - fontSize: 13, - ), - textScaler: TextScaler.noScaling, - ); - }).toList(), - ), - ); - }, - ), + textScaler: TextScaler.noScaling, + ); + }).toList(), + ); + }, ), ), - ); - } - return const SliverToBoxAdapter(); - }), - SliverPadding( - padding: EdgeInsets.only(bottom: padding.bottom + 80), - sliver: buildPage( - Obx(() => _buildBody(_controller.loadingState.value)), - ), + ), + ); + } + return const SliverToBoxAdapter(); + }), + SliverPadding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + bottom: padding.bottom + 100, ), - ], - ), + sliver: buildPage( + Obx(() => _buildBody(_controller.loadingState.value)), + ), + ), + ], ), ), ); @@ -153,7 +152,7 @@ class _DynTopicPageState extends State with DynMixin { Widget _buildAppBar( ThemeData theme, - double paddingTop, + EdgeInsets padding, LoadingState topState, ) { return switch (topState) { @@ -162,7 +161,7 @@ class _DynTopicPageState extends State with DynMixin { DynamicSliverAppBarMedium( pinned: true, callback: (value) => - _controller.appbarOffset = value - kToolbarHeight - paddingTop, + _controller.appbarOffset = value - kToolbarHeight - padding.top, title: IgnorePointer(child: Text(response!.topicItem!.name)), flexibleSpace: Container( decoration: BoxDecoration( @@ -175,9 +174,9 @@ class _DynTopicPageState extends State with DynMixin { ), ), padding: EdgeInsets.only( - top: paddingTop, - left: 12, - right: 12, + top: padding.top, + left: 12 + padding.left, + right: 12 + padding.right, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/dynamics_topic_rcmd/view.dart b/lib/pages/dynamics_topic_rcmd/view.dart index d23d35e92..b8df5357e 100644 --- a/lib/pages/dynamics_topic_rcmd/view.dart +++ b/lib/pages/dynamics_topic_rcmd/view.dart @@ -1,6 +1,7 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/topic_item.dart'; import 'package:PiliPlus/pages/dynamics_select_topic/widgets/item.dart'; @@ -21,23 +22,17 @@ class _DynTopicRcmdPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('话题')), - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _controller.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx(() => _buildBody(_controller.loadingState.value)), - ), - ], - ), + body: refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + ViewSliverSafeArea( + sliver: Obx(() => _buildBody(_controller.loadingState.value)), + ), + ], ), ), ); diff --git a/lib/pages/emote/view.dart b/lib/pages/emote/view.dart index 11d63c61a..3c9d81344 100644 --- a/lib/pages/emote/view.dart +++ b/lib/pages/emote/view.dart @@ -208,7 +208,9 @@ class _EmotePanelState extends State ), ], ), - SizedBox(height: MediaQuery.paddingOf(context).bottom), + SizedBox( + height: MediaQuery.viewPaddingOf(context).bottom, + ), ], ) : _errorWidget(), diff --git a/lib/pages/episode_panel/view.dart b/lib/pages/episode_panel/view.dart index 506b356b1..27a59038b 100644 --- a/lib/pages/episode_panel/view.dart +++ b/lib/pages/episode_panel/view.dart @@ -263,7 +263,7 @@ class _EpisodePanelState extends CommonCollapseSlidePageState { builder: (context) => ScrollablePositionedList.separated( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), reverse: _isReversed[tabIndex], itemCount: episodes.length, diff --git a/lib/pages/fan/view.dart b/lib/pages/fan/view.dart index dfafb685e..c10267584 100644 --- a/lib/pages/fan/view.dart +++ b/lib/pages/fan/view.dart @@ -3,6 +3,7 @@ import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models_new/fans/list.dart'; @@ -51,27 +52,22 @@ class _FansPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: widget.mid != null ? null : AppBar(title: Text(isOwner ? '我的粉丝' : '$name的粉丝')), - body: SafeArea( - bottom: false, - child: refreshIndicator( - onRefresh: _fansController.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - controller: _fansController.scrollController, - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx( - () => _buildBody(_fansController.loadingState.value), - ), + body: refreshIndicator( + onRefresh: _fansController.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: _fansController.scrollController, + slivers: [ + ViewSliverSafeArea( + sliver: Obx( + () => _buildBody(_fansController.loadingState.value), ), - ], - ), + ), + ], ), ), ); @@ -98,47 +94,65 @@ class _FansPageState extends State { _fansController.onLoadMore(); } final item = response[index]; - return ListTile( - onTap: () { - if (widget.onSelect != null) { - widget.onSelect!( - UserModel( - mid: item.mid!, - name: item.uname!, - avatar: item.face!, - ), - ); - return; - } - Get.toNamed('/member?mid=${item.mid}'); - }, - onLongPress: widget.onSelect != null - ? null - : isOwner - ? () => showConfirmDialog( - context: context, - title: '确定移除 ${item.uname} ?', - onConfirm: () => - _fansController.onRemoveFan(index, item.mid!), - ) - : null, - leading: NetworkImgLayer( - width: 45, - height: 45, - type: ImageType.avatar, - src: item.face, + return SizedBox( + height: 66, + child: InkWell( + onTap: () { + if (widget.onSelect != null) { + widget.onSelect!( + UserModel( + mid: item.mid!, + name: item.uname!, + avatar: item.face!, + ), + ); + return; + } + Get.toNamed('/member?mid=${item.mid}'); + }, + onLongPress: widget.onSelect != null + ? null + : isOwner + ? () => showConfirmDialog( + context: context, + title: '确定移除 ${item.uname} ?', + onConfirm: () => + _fansController.onRemoveFan(index, item.mid!), + ) + : null, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + child: Row( + spacing: 10, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + NetworkImgLayer( + width: 45, + height: 45, + type: ImageType.avatar, + src: item.face, + ), + Column( + spacing: 5, + children: [ + Text( + item.uname!, + style: const TextStyle(fontSize: 14), + ), + Text( + item.sign ?? '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ], + ), + ), ), - title: Text( - item.uname!, - style: const TextStyle(fontSize: 14), - ), - subtitle: Text( - item.sign ?? '', - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - dense: true, - trailing: const SizedBox(width: 6), ); }, itemCount: response!.length, diff --git a/lib/pages/fav/article/view.dart b/lib/pages/fav/article/view.dart index ec6bde068..c7497d36d 100644 --- a/lib/pages/fav/article/view.dart +++ b/lib/pages/fav/article/view.dart @@ -37,7 +37,7 @@ class _FavArticlePageState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(_favArticleController.loadingState.value), diff --git a/lib/pages/fav/cheese/view.dart b/lib/pages/fav/cheese/view.dart index ebfe6f43b..590a02304 100644 --- a/lib/pages/fav/cheese/view.dart +++ b/lib/pages/fav/cheese/view.dart @@ -36,7 +36,7 @@ class _FavCheesePageState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), diff --git a/lib/pages/fav/note/child_view.dart b/lib/pages/fav/note/child_view.dart index 64c43cf92..d4a72fc42 100644 --- a/lib/pages/fav/note/child_view.dart +++ b/lib/pages/fav/note/child_view.dart @@ -30,7 +30,7 @@ class _FavNoteChildPageState extends State Widget build(BuildContext context) { super.build(context); final theme = Theme.of(context); - final padding = MediaQuery.paddingOf(context); + final padding = MediaQuery.viewPaddingOf(context); final bottomH = 50 + padding.bottom; return Stack( clipBehavior: Clip.none, @@ -42,7 +42,7 @@ class _FavNoteChildPageState extends State physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( - padding: EdgeInsets.only(bottom: padding.bottom + 80), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: Obx( () => _buildBody(_favNoteController.loadingState.value), ), diff --git a/lib/pages/fav/note/view.dart b/lib/pages/fav/note/view.dart index 7c9782a37..2768b6669 100644 --- a/lib/pages/fav/note/view.dart +++ b/lib/pages/fav/note/view.dart @@ -38,7 +38,7 @@ class _FavNotePageState extends State children: [ Expanded( child: TabBar( - overlayColor: WidgetStateProperty.all(Colors.transparent), + overlayColor: const WidgetStatePropertyAll(Colors.transparent), splashFactory: NoSplash.splashFactory, isScrollable: true, tabAlignment: TabAlignment.start, diff --git a/lib/pages/fav/pgc/child_view.dart b/lib/pages/fav/pgc/child_view.dart index 9963db461..690823fcc 100644 --- a/lib/pages/fav/pgc/child_view.dart +++ b/lib/pages/fav/pgc/child_view.dart @@ -36,7 +36,7 @@ class _FavPgcChildPageState extends State Widget build(BuildContext context) { super.build(context); final theme = Theme.of(context); - final padding = MediaQuery.paddingOf(context); + final padding = MediaQuery.viewPaddingOf(context); final bottomH = 50 + padding.bottom; return Stack( clipBehavior: Clip.none, @@ -48,7 +48,7 @@ class _FavPgcChildPageState extends State physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( - padding: EdgeInsets.only(bottom: padding.bottom + 80), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: Obx( () => _buildBody(_favPgcController.loadingState.value), ), diff --git a/lib/pages/fav/pgc/view.dart b/lib/pages/fav/pgc/view.dart index ee7806a36..9a1201964 100644 --- a/lib/pages/fav/pgc/view.dart +++ b/lib/pages/fav/pgc/view.dart @@ -41,7 +41,7 @@ class _FavPgcPageState extends State children: [ Expanded( child: TabBar( - overlayColor: WidgetStateProperty.all(Colors.transparent), + overlayColor: WidgetStatePropertyAll(Colors.transparent), splashFactory: NoSplash.splashFactory, isScrollable: true, tabAlignment: TabAlignment.start, diff --git a/lib/pages/fav/topic/view.dart b/lib/pages/fav/topic/view.dart index adfb04605..e5555928d 100644 --- a/lib/pages/fav/topic/view.dart +++ b/lib/pages/fav/topic/view.dart @@ -38,7 +38,7 @@ class _FavTopicPageState extends State left: StyleString.safeSpace, right: StyleString.safeSpace, top: StyleString.safeSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), diff --git a/lib/pages/fav/video/view.dart b/lib/pages/fav/video/view.dart index 9fff2b95e..63d99767e 100644 --- a/lib/pages/fav/video/view.dart +++ b/lib/pages/fav/video/view.dart @@ -35,7 +35,7 @@ class _FavVideoPageState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: 80 + MediaQuery.paddingOf(context).bottom, + bottom: 100 + MediaQuery.viewPaddingOf(context).bottom, ), sliver: Obx( () => _buildBody(_favController.loadingState.value), diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 71d1f50bf..fcc51cdde 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/fav_type.dart'; import 'package:PiliPlus/models_new/fav/fav_folder/list.dart'; @@ -52,6 +53,7 @@ class _FavPageState extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('我的收藏'), actions: [ @@ -152,9 +154,7 @@ class _FavPageState extends State with SingleTickerProviderStateMixin { }, ), ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: tabBarView( controller: _tabController, children: FavTabType.values.map((item) => item.page).toList(), diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 5defea260..8ac32da46 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -40,9 +40,12 @@ class _FavDetailPageState extends State with GridMixin { mediaId = Get.parameters['mediaId']!; } + late EdgeInsets padding; + @override Widget build(BuildContext context) { final theme = Theme.of(context); + padding = MediaQuery.viewPaddingOf(context); return Obx( () { final enableMultiSelect = _favDetailController.enableMultiSelect.value; @@ -64,30 +67,28 @@ class _FavDetailPageState extends State with GridMixin { ) : const SizedBox.shrink(), ), - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _favDetailController.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - controller: _favDetailController.scrollController, - slivers: [ - _buildHeader(enableMultiSelect, theme), - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 85, - ), - sliver: Obx( - () => _buildBody( - enableMultiSelect, - theme, - _favDetailController.loadingState.value, - ), + body: refreshIndicator( + onRefresh: _favDetailController.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: _favDetailController.scrollController, + slivers: [ + _buildHeader(enableMultiSelect, theme), + SliverPadding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + bottom: padding.bottom + 100, + ), + sliver: Obx( + () => _buildBody( + enableMultiSelect, + theme, + _favDetailController.loadingState.value, ), ), - ], - ), + ), + ], ), ), ), @@ -341,8 +342,8 @@ class _FavDetailPageState extends State with GridMixin { return FlexibleSpaceBar( background: Padding( padding: EdgeInsets.only( - top: kToolbarHeight + MediaQuery.paddingOf(context).top + 10, - left: 14, + top: kToolbarHeight + padding.top + 10, + left: 14 + padding.left, right: 20, bottom: 10, ), diff --git a/lib/pages/fav_folder_sort/view.dart b/lib/pages/fav_folder_sort/view.dart index 630ced2c2..003d0327c 100644 --- a/lib/pages/fav_folder_sort/view.dart +++ b/lib/pages/fav_folder_sort/view.dart @@ -27,6 +27,7 @@ class _FavFolderSortPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('收藏夹排序'), actions: [ @@ -48,11 +49,7 @@ class _FavFolderSortPageState extends State { const SizedBox(width: 16), ], ), - body: SafeArea( - top: false, - bottom: false, - child: _buildBody, - ), + body: _buildBody, ); } @@ -77,10 +74,10 @@ class _FavFolderSortPageState extends State { key: _key, onReorder: onReorder, physics: const AlwaysScrollableScrollPhysics(), - footer: SizedBox( - height: MediaQuery.paddingOf(context).bottom + 80, - ), itemCount: sortList.length, + padding: + MediaQuery.viewPaddingOf(context).copyWith(top: 0) + + const EdgeInsets.only(bottom: 100), itemBuilder: (context, index) { final item = sortList[index]; final key = item.id.toString(); diff --git a/lib/pages/fav_panel/view.dart b/lib/pages/fav_panel/view.dart index 676269454..0b041d14e 100644 --- a/lib/pages/fav_panel/view.dart +++ b/lib/pages/fav_panel/view.dart @@ -141,7 +141,7 @@ class _FavPanelState extends State { left: 20, right: 20, top: 12, - bottom: MediaQuery.paddingOf(context).bottom + 12, + bottom: MediaQuery.viewPaddingOf(context).bottom + 12, ), child: Row( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/pages/fav_sort/view.dart b/lib/pages/fav_sort/view.dart index f6f49a6e4..e790591d3 100644 --- a/lib/pages/fav_sort/view.dart +++ b/lib/pages/fav_sort/view.dart @@ -68,6 +68,7 @@ class _FavSortPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Text('排序: ${_favDetailController.folderInfo.value.title}'), actions: [ @@ -94,11 +95,7 @@ class _FavSortPageState extends State { const SizedBox(width: 16), ], ), - body: SafeArea( - top: false, - bottom: false, - child: _buildBody, - ), + body: _buildBody, ); } @@ -127,9 +124,9 @@ class _FavSortPageState extends State { scrollController: _scrollController, onReorder: onReorder, physics: const AlwaysScrollableScrollPhysics(), - footer: SizedBox( - height: MediaQuery.paddingOf(context).bottom + 80, - ), + padding: + MediaQuery.viewPaddingOf(context).copyWith(top: 0) + + const EdgeInsets.only(bottom: 100), itemCount: sortList.length, itemBuilder: (context, index) { final item = sortList[index]; diff --git a/lib/pages/follow/child/child_view.dart b/lib/pages/follow/child/child_view.dart index dbc4ed85f..c11bcc12c 100644 --- a/lib/pages/follow/child/child_view.dart +++ b/lib/pages/follow/child/child_view.dart @@ -42,7 +42,7 @@ class _FollowChildPageState extends State @override Widget build(BuildContext context) { super.build(context); - final padding = MediaQuery.paddingOf(context); + final padding = MediaQuery.viewPaddingOf(context); Widget child = refreshIndicator( onRefresh: _followController.onRefresh, child: CustomScrollView( @@ -50,7 +50,7 @@ class _FollowChildPageState extends State physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( - padding: EdgeInsets.only(bottom: padding.bottom + 80), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: Obx( () => _buildBody(_followController.loadingState.value), ), diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart index 073bffdd3..ec7e738a6 100644 --- a/lib/pages/follow/view.dart +++ b/lib/pages/follow/view.dart @@ -1,6 +1,7 @@ import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/member/tags.dart'; import 'package:PiliPlus/pages/follow/child/child_controller.dart'; @@ -28,6 +29,7 @@ class _FollowPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Text( _followController.isOwner ? '我的关注' : '${_followController.name}的关注', @@ -92,9 +94,7 @@ class _FollowPageState extends State { Success() => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SafeArea( - top: false, - bottom: false, + ViewSafeArea( child: TabBar( isScrollable: true, tabAlignment: TabAlignment.start, diff --git a/lib/pages/group_panel/view.dart b/lib/pages/group_panel/view.dart index 3fa906cf8..008318214 100644 --- a/lib/pages/group_panel/view.dart +++ b/lib/pages/group_panel/view.dart @@ -140,7 +140,7 @@ class _GroupPanelState extends State { left: 20, right: 20, top: 12, - bottom: MediaQuery.paddingOf(context).bottom + 12, + bottom: MediaQuery.viewPaddingOf(context).bottom + 12, ), child: Row( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart index 1eb115cce..f2ad12127 100644 --- a/lib/pages/history/view.dart +++ b/lib/pages/history/view.dart @@ -50,6 +50,7 @@ class _HistoryPageState extends State @override Widget build(BuildContext context) { super.build(context); + final padding = MediaQuery.viewPaddingOf(context); Widget child = refreshIndicator( onRefresh: _historyController.onRefresh, child: CustomScrollView( @@ -59,7 +60,7 @@ class _HistoryPageState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: padding.bottom + 100, ), sliver: Obx( () => _buildBody(_historyController.loadingState.value), @@ -83,23 +84,22 @@ class _HistoryPageState extends State } }, child: Scaffold( + resizeToAvoidBottomInset: false, appBar: MultiSelectAppBarWidget( visible: enableMultiSelect, ctr: currCtr(), child: _buildAppBar, ), - body: Obx(() { - if (_historyController.tabs.isEmpty) { - return SafeArea( - top: false, - bottom: false, - child: child, - ); - } - return SafeArea( - top: false, - bottom: false, - child: Column( + body: Padding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + ), + child: Obx(() { + if (_historyController.tabs.isEmpty) { + return child; + } + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TabBar( @@ -139,9 +139,9 @@ class _HistoryPageState extends State ), ), ], - ), - ); - }), + ); + }), + ), ), ); }, diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 3d5cb050e..408d3d568 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -232,7 +232,7 @@ Widget defaultUser({ child: IconButton( tooltip: '默认用户头像', style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: const WidgetStatePropertyAll(EdgeInsets.zero), backgroundColor: WidgetStatePropertyAll( theme.colorScheme.onInverseSurface, ), diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index d5de2b462..9b79a50f1 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -1,6 +1,7 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/home_tab_type.dart'; import 'package:PiliPlus/models/model_hot_video_item.dart'; @@ -92,10 +93,9 @@ class _HotPageState extends CommonPageState } else { Get.to( Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('排行榜')), - body: const SafeArea( - top: false, - bottom: false, + body: const ViewSafeArea( child: RankPage(), ), ), @@ -137,7 +137,7 @@ class _HotPageState extends CommonPageState SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(controller.loadingState.value), diff --git a/lib/pages/later/child_view.dart b/lib/pages/later/child_view.dart index eb943e132..071cb1415 100644 --- a/lib/pages/later/child_view.dart +++ b/lib/pages/later/child_view.dart @@ -42,7 +42,7 @@ class _LaterViewChildPageState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 85, + bottom: MediaQuery.viewPaddingOf(context).bottom + 85, ), sliver: Obx( () => _buildBody(_laterController.loadingState.value), diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index ba8c3be08..2a4298d09 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -1,5 +1,6 @@ import 'package:PiliPlus/common/widgets/appbar/appbar.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/models/common/later_view_type.dart'; import 'package:PiliPlus/models_new/later/data.dart'; import 'package:PiliPlus/models_new/later/list.dart'; @@ -79,9 +80,7 @@ class _LaterPageState extends State ) : const SizedBox.shrink(), ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: Column( children: [ TabBar( diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index 7d008e58d..0d95c481f 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -53,7 +53,7 @@ class _LivePageState extends CommonPageState SliverPadding( padding: EdgeInsets.only( top: StyleString.cardSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: SliverMainAxisGroup( slivers: [ diff --git a/lib/pages/live_area/view.dart b/lib/pages/live_area/view.dart index fe616f459..917215322 100644 --- a/lib/pages/live_area/view.dart +++ b/lib/pages/live_area/view.dart @@ -29,7 +29,9 @@ class _LiveAreaPageState extends State { @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('全部标签'), actions: _controller.accountService.isLogin.value @@ -47,9 +49,8 @@ class _LiveAreaPageState extends State { ] : null, ), - body: SafeArea( - top: false, - bottom: false, + body: Padding( + padding: EdgeInsets.only(left: padding.left, right: padding.right), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -57,7 +58,11 @@ class _LiveAreaPageState extends State { Obx(() => _buildFavWidget(theme, _controller.favState.value)), Expanded( child: Obx( - () => _buildBody(theme, _controller.loadingState.value), + () => _buildBody( + theme, + padding.bottom, + _controller.loadingState.value, + ), ), ), ], @@ -68,6 +73,7 @@ class _LiveAreaPageState extends State { Widget _buildBody( ThemeData theme, + double bottom, LoadingState?> loadingState, ) { return switch (loadingState) { @@ -96,9 +102,7 @@ class _LiveAreaPageState extends State { return GridView.builder( padding: EdgeInsets.only( top: 12, - bottom: - MediaQuery.paddingOf(context).bottom + - 80, + bottom: bottom + 100, ), gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( diff --git a/lib/pages/live_area_detail/child/view.dart b/lib/pages/live_area_detail/child/view.dart index 90a22dc1f..13895f533 100644 --- a/lib/pages/live_area_detail/child/view.dart +++ b/lib/pages/live_area_detail/child/view.dart @@ -48,7 +48,7 @@ class _LiveAreaChildPageState extends State left: StyleString.safeSpace, right: StyleString.safeSpace, top: StyleString.safeSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), diff --git a/lib/pages/live_area_detail/view.dart b/lib/pages/live_area_detail/view.dart index f65d2ec19..cd04ebb7f 100644 --- a/lib/pages/live_area_detail/view.dart +++ b/lib/pages/live_area_detail/view.dart @@ -35,7 +35,9 @@ class _LiveAreaDetailPageState extends State { @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Text(widget.parentName), actions: [ @@ -46,16 +48,19 @@ class _LiveAreaDetailPageState extends State { const SizedBox(width: 16), ], ), - body: SafeArea( - top: false, - bottom: false, - child: Obx(() => _buildBody(theme, _controller.loadingState.value)), + body: Padding( + padding: EdgeInsets.only(left: padding.left, right: padding.right), + child: Obx( + () => + _buildBody(theme, padding.bottom, _controller.loadingState.value), + ), ), ); } Widget _buildBody( ThemeData theme, + double bottom, LoadingState?> loadingState, ) { return switch (loadingState) { @@ -100,7 +105,7 @@ class _LiveAreaDetailPageState extends State { icon: Icons.menu, bgColor: Colors.transparent, onPressed: () => - _showTags(context, theme, response), + _showTags(context, theme, bottom, response), ), ], ), @@ -162,7 +167,12 @@ class _LiveAreaDetailPageState extends State { ); } - void _showTags(BuildContext context, ThemeData theme, List list) { + void _showTags( + BuildContext context, + ThemeData theme, + double bottom, + List list, + ) { showModalBottomSheet( context: context, useSafeArea: true, @@ -196,7 +206,7 @@ class _LiveAreaDetailPageState extends State { controller: scrollController, padding: EdgeInsets.only( top: 12, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: bottom + 100, ), itemCount: list.length, gridDelegate: diff --git a/lib/pages/live_dm_block/view.dart b/lib/pages/live_dm_block/view.dart index 01992c590..60300001f 100644 --- a/lib/pages/live_dm_block/view.dart +++ b/lib/pages/live_dm_block/view.dart @@ -27,10 +27,12 @@ class _LiveDmBlockPageState extends State { tag: Utils.generateRandomString(8), ); late bool isPortrait; + late EdgeInsets padding; @override Widget build(BuildContext context) { isPortrait = context.isPortrait; + padding = MediaQuery.viewPaddingOf(context); final theme = Theme.of(context); Widget tabBar = TabBar( @@ -84,11 +86,9 @@ class _LiveDmBlockPageState extends State { return Scaffold( resizeToAvoidBottomInset: false, - appBar: AppBar( - title: const Text('弹幕屏蔽'), - ), - body: SafeArea( - bottom: false, + appBar: AppBar(title: const Text('弹幕屏蔽')), + body: Padding( + padding: EdgeInsets.only(left: padding.left, right: padding.right), child: Stack( clipBehavior: Clip.none, children: [ @@ -151,7 +151,7 @@ class _LiveDmBlockPageState extends State { ), Positioned( right: 16, - bottom: 16 + MediaQuery.paddingOf(context).bottom, + bottom: 16 + padding.bottom, child: FloatingActionButton( tooltip: '添加', onPressed: _addShieldKeyword, @@ -173,7 +173,7 @@ class _LiveDmBlockPageState extends State { top: 12, left: 12, right: 12, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: padding.bottom + 100, ), child: Wrap( spacing: 12, diff --git a/lib/pages/live_emote/view.dart b/lib/pages/live_emote/view.dart index 773edc725..5f9f96908 100644 --- a/lib/pages/live_emote/view.dart +++ b/lib/pages/live_emote/view.dart @@ -183,7 +183,9 @@ class _LiveEmotePanelState extends State ) .toList(), ), - SizedBox(height: MediaQuery.paddingOf(context).bottom), + SizedBox( + height: MediaQuery.viewPaddingOf(context).bottom, + ), ], ) : _errorWidget(), diff --git a/lib/pages/live_follow/view.dart b/lib/pages/live_follow/view.dart index 9f7675086..9712877c5 100644 --- a/lib/pages/live_follow/view.dart +++ b/lib/pages/live_follow/view.dart @@ -22,7 +22,9 @@ class _LiveFollowPageState extends State { @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Obx( () { @@ -31,24 +33,20 @@ class _LiveFollowPageState extends State { }, ), ), - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _controller.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx(() => _buildBody(_controller.loadingState.value)), + body: refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + left: StyleString.safeSpace + padding.left, + right: StyleString.safeSpace + padding.right, + bottom: padding.bottom + 100, ), - ], - ), + sliver: Obx(() => _buildBody(_controller.loadingState.value)), + ), + ], ), ), ); diff --git a/lib/pages/live_room/send_danmaku/view.dart b/lib/pages/live_room/send_danmaku/view.dart index be46093df..359415d8f 100644 --- a/lib/pages/live_room/send_danmaku/view.dart +++ b/lib/pages/live_room/send_danmaku/view.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:PiliPlus/common/widgets/text_field/text_field.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/live.dart'; import 'package:PiliPlus/models/common/publish_panel_type.dart'; import 'package:PiliPlus/pages/common/publish/common_rich_text_pub_page.dart'; @@ -49,8 +50,7 @@ class _ReplyPageState extends CommonRichTextPubPageState { @override Widget build(BuildContext context) { final theme = Theme.of(context); - return SafeArea( - bottom: false, + return ViewSafeArea( child: Align( alignment: Alignment.bottomCenter, child: Container( diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 3f71c2fdf..26f03542e 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -161,30 +161,27 @@ class _LiveRoomPageState extends State late double maxWidth; late double maxHeight; late EdgeInsets padding; + late bool isPortrait; @override Widget build(BuildContext context) { - padding = MediaQuery.paddingOf(context); - return LayoutBuilder( - builder: (context, constraints) { - maxWidth = constraints.maxWidth; - maxHeight = constraints.maxHeight; - final isPortrait = maxHeight >= maxWidth; - - if (Platform.isAndroid) { - return Floating().isPipMode - ? videoPlayerPanel( - isFullScreen, - width: maxWidth, - height: maxHeight, - isPipMode: true, - ) - : childWhenDisabled(isPortrait); - } else { - return childWhenDisabled(isPortrait); - } - }, - ); + padding = MediaQuery.viewPaddingOf(context); + final size = MediaQuery.sizeOf(context); + maxWidth = size.width; + maxHeight = size.height; + isPortrait = maxHeight >= maxWidth; + if (Platform.isAndroid) { + return Floating().isPipMode + ? videoPlayerPanel( + isFullScreen, + width: maxWidth, + height: maxHeight, + isPipMode: true, + ) + : childWhenDisabled; + } else { + return childWhenDisabled; + } } Widget videoPlayerPanel( @@ -257,7 +254,7 @@ class _LiveRoomPageState extends State ); } - Widget childWhenDisabled(bool isPortrait) { + Widget get childWhenDisabled { return AnnotatedRegion( value: _systemOverlayStyleForBrightness( Brightness.dark, @@ -297,22 +294,17 @@ class _LiveRoomPageState extends State ); }, ), - SafeArea( - top: !isFullScreen, - left: !isFullScreen, - right: !isFullScreen, - bottom: false, - child: isPortrait - ? Obx( - () { - if (_liveRoomController.isPortrait.value) { - return _buildPP; - } - return _buildPH; - }, - ) - : _buildBodyH, - ), + if (isPortrait) + Obx( + () { + if (_liveRoomController.isPortrait.value) { + return _buildPP; + } + return _buildPH; + }, + ) + else + _buildBodyH, ], ), ), @@ -321,67 +313,86 @@ class _LiveRoomPageState extends State Widget get _buildPH { final isFullScreen = this.isFullScreen; - final height = isFullScreen ? maxHeight : maxWidth * 9 / 16; + final height = maxWidth * 9 / 16; + final videoHeight = isFullScreen ? maxHeight : height; return Column( children: [ - if (!isFullScreen) _buildAppBar, + Offstage( + offstage: isFullScreen, + child: _buildAppBar, + ), SizedBox( width: maxWidth, - height: height, + height: videoHeight, child: videoPlayerPanel( isFullScreen, width: maxWidth, - height: height, + height: videoHeight, + ), + ), + Offstage( + offstage: isFullScreen, + child: SizedBox( + width: maxWidth, + height: maxHeight - padding.top - height - kToolbarHeight, + child: _buildBottomWidget, ), ), - ..._buildBottomWidget, ], ); } Widget get _buildPP { final isFullScreen = this.isFullScreen; - final bottomHeight = 85.0 + padding.bottom; - final height = isFullScreen - ? maxHeight - : maxHeight - padding.top - kToolbarHeight - bottomHeight; - Widget child = Stack( + final bottomHeight = 80.0 + padding.bottom; + final topPadding = padding.top + kToolbarHeight; + final videoHeight = maxHeight - bottomHeight - topPadding; + return Stack( clipBehavior: Clip.none, children: [ Positioned.fill( + top: isFullScreen ? 0 : topPadding, + bottom: isFullScreen ? 0 : bottomHeight, child: videoPlayerPanel( width: maxWidth, - height: height, + height: videoHeight, isFullScreen, - alignment: isFullScreen ? null : Alignment.topCenter, needDm: isFullScreen, + alignment: isFullScreen ? null : Alignment.topCenter, + ), + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: Offstage( + offstage: isFullScreen, + child: _buildAppBar, ), ), Positioned( left: 0, right: 0, - bottom: 55, - child: Visibility( - maintainState: true, - visible: !isFullScreen, + bottom: 55 + bottomHeight, + child: Offstage( + offstage: isFullScreen, child: SizedBox( height: maxHeight * 0.32, child: _buildChatWidget(true), ), ), ), - ], - ); - if (isFullScreen) { - return child; - } - return Column( - children: [ - _buildAppBar, - Expanded(child: child), - SizedBox( - height: bottomHeight, - child: _buildInputWidget, + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Offstage( + offstage: isFullScreen, + child: SizedBox( + height: bottomHeight, + child: _buildInputWidget, + ), + ), ), ], ); @@ -550,57 +561,69 @@ class _LiveRoomPageState extends State } Widget get _buildBodyH { - double videoWidth = + final videoWidth = clampDouble(maxHeight / maxWidth * 1.08, 0.58, 0.75) * maxWidth; + final videoHeight = maxHeight - padding.top; return Obx( () { final isFullScreen = this.isFullScreen; final width = isFullScreen ? maxWidth : videoWidth; - final height = isFullScreen ? maxHeight : maxWidth * 9 / 16; - Widget child = Row( - children: [ - Container( - margin: EdgeInsets.only(bottom: padding.bottom), - width: width, - height: height, - child: videoPlayerPanel( - isFullScreen, - fill: Colors.transparent, - width: width, - height: height, - ), - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildBottomWidget, - ), - ), - ], - ); - if (isFullScreen) { - return child; - } + final height = isFullScreen ? maxHeight : videoHeight; return Column( children: [ - _buildAppBar, - Expanded(child: child), + Offstage( + offstage: isFullScreen, + child: _buildAppBar, + ), + Expanded( + child: Padding( + padding: isFullScreen + ? EdgeInsets.zero + : EdgeInsets.only(left: padding.left, right: padding.right), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(bottom: padding.bottom), + width: width, + height: height, + child: videoPlayerPanel( + isFullScreen, + fill: Colors.transparent, + width: width, + height: height, + ), + ), + Offstage( + offstage: isFullScreen, + child: SizedBox( + width: maxWidth - videoWidth - padding.horizontal, + height: videoHeight, + child: _buildBottomWidget, + ), + ), + ], + ), + ), + ), ], ); }, ); } - List get _buildBottomWidget => [ - Expanded(child: _buildChatWidget()), - _buildInputWidget, - ]; + Widget get _buildBottomWidget => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: _buildChatWidget()), + _buildInputWidget, + ], + ); - Widget _buildChatWidget([bool? isPP]) => Padding( - padding: const EdgeInsets.symmetric(vertical: 16), + Widget _buildChatWidget([bool isPP = false]) => Padding( + padding: EdgeInsets.only(bottom: 16, top: !isPortrait ? 0 : 16), child: LiveRoomChat( key: chatKey, - isPP: isPP ?? false, + isPP: isPP, roomId: _liveRoomController.roomId, liveRoomController: _liveRoomController, ), @@ -613,6 +636,7 @@ class _LiveRoomPageState extends State right: 10, bottom: 15 + padding.bottom, ), + height: 80 + padding.bottom, decoration: const BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(20), diff --git a/lib/pages/live_search/child/view.dart b/lib/pages/live_search/child/view.dart index e57fe7b0e..e45aeec95 100644 --- a/lib/pages/live_search/child/view.dart +++ b/lib/pages/live_search/child/view.dart @@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/msg_feed_top.dart'; import 'package:PiliPlus/common/skeleton/video_card_v.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; +import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/live_search_type.dart'; import 'package:PiliPlus/pages/live_search/child/controller.dart'; @@ -33,7 +34,7 @@ class _LiveSearchChildPageState extends State Widget build(BuildContext context) { super.build(context); double padding = widget.searchType == LiveSearchType.room ? 12 : 0; - return RefreshIndicator( + return refreshIndicator( onRefresh: _controller.onRefresh, child: CustomScrollView( controller: _controller.scrollController, @@ -43,7 +44,7 @@ class _LiveSearchChildPageState extends State top: padding, left: padding, right: padding, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), diff --git a/lib/pages/live_search/view.dart b/lib/pages/live_search/view.dart index 2d54bcba0..8a196b0ca 100644 --- a/lib/pages/live_search/view.dart +++ b/lib/pages/live_search/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/models/common/live_search_type.dart'; import 'package:PiliPlus/pages/live_search/child/view.dart'; import 'package:PiliPlus/pages/live_search/controller.dart'; @@ -55,9 +56,7 @@ class _LiveSearchPageState extends State { }, ), ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: Obx(() { return Opacity( opacity: _controller.hasData.value ? 1 : 0, diff --git a/lib/pages/log_table/view.dart b/lib/pages/log_table/view.dart index def044c55..e08d9ae4d 100644 --- a/lib/pages/log_table/view.dart +++ b/lib/pages/log_table/view.dart @@ -17,26 +17,24 @@ class _LogPageState extends State> { @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: Text(_controller.title)), - body: SafeArea( - top: false, - bottom: false, - child: Center( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 680), - child: CustomScrollView( - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - left: 10, - right: 10, - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx(() => _buildBody(_controller.loadingState.value)), + body: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 680), + child: CustomScrollView( + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + left: 10 + padding.left, + right: 10 + padding.right, + bottom: padding.bottom + 100, ), - ], - ), + sliver: Obx(() => _buildBody(_controller.loadingState.value)), + ), + ], ), ), ), diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 1fc2979d2..8569f4d1d 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -504,7 +504,7 @@ class _LoginPageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); padding = - MediaQuery.paddingOf(context).copyWith(top: 0) + + MediaQuery.viewPaddingOf(context).copyWith(top: 0) + const EdgeInsets.only(bottom: 25); return OrientationBuilder( builder: (context, orientation) { diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 8470ffd8a..bebf78bd6 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:math' show max; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/grpc/dyn.dart'; import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart'; @@ -263,8 +264,8 @@ class MainController extends GetxController } else { Get.to( const Material( - child: SafeArea( - bottom: false, + child: ViewSafeArea( + top: true, child: MinePage(), ), ), diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 9b956b64a..0fe4aad39 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -93,6 +93,7 @@ class _MainAppState extends State @override Widget build(BuildContext context) { final theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); final bool isPortrait = context.isPortrait; final useBottomNav = isPortrait && !_mainController.useSideBar; Widget? bottomNav = useBottomNav @@ -163,9 +164,10 @@ class _MainAppState extends State ), child: Scaffold( extendBody: true, + resizeToAvoidBottomInset: false, appBar: AppBar(toolbarHeight: 0), - body: SafeArea( - bottom: false, + body: Padding( + padding: EdgeInsets.only(right: padding.right), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -206,7 +208,9 @@ class _MainAppState extends State (e) => NavigationDrawerDestination( label: Text(e.label), - icon: _buildIcon(type: e), + icon: _buildIcon( + type: e, + ), selectedIcon: _buildIcon( type: e, selected: true, @@ -250,7 +254,7 @@ class _MainAppState extends State ), VerticalDivider( width: 1, - endIndent: MediaQuery.paddingOf(context).bottom, + endIndent: padding.bottom, color: theme.colorScheme.outline.withValues(alpha: 0.06), ), ], diff --git a/lib/pages/match_info/view.dart b/lib/pages/match_info/view.dart index 47bd1bdd4..2394ce6fc 100644 --- a/lib/pages/match_info/view.dart +++ b/lib/pages/match_info/view.dart @@ -2,6 +2,8 @@ import 'package:PiliPlus/common/skeleton/video_reply.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; +import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart'; import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo; import 'package:PiliPlus/http/loading_state.dart'; @@ -42,26 +44,21 @@ class _MatchInfoPageState extends CommonDynPageState { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('比赛详情')), - body: SafeArea( - bottom: false, - child: refreshIndicator( - onRefresh: controller.onRefresh, - child: CustomScrollView( - controller: controller.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - Obx(() => _buildInfo(theme, controller.infoState.value)), - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx( - () => _buildReply(theme, controller.loadingState.value), - ), + body: refreshIndicator( + onRefresh: controller.onRefresh, + child: CustomScrollView( + controller: controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + Obx(() => _buildInfo(theme, controller.infoState.value)), + ViewSliverSafeArea( + sliver: Obx( + () => _buildReply(theme, controller.loadingState.value), ), - ], - ), + ), + ], ), ), floatingActionButton: SlideTransition( @@ -267,10 +264,9 @@ class _MatchInfoPageState extends CommonDynPageState { int rpid = replyItem.id.toInt(); Get.to( Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('评论详情')), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: VideoReplyReplyPanel( enableSlide: false, id: id, diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index e7d9a8b2c..37644436b 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -48,47 +48,57 @@ class _MemberPageState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); return Material( color: theme.colorScheme.surface, - child: Obx(() { - if (_userController.loadingState.value.isSuccess) { - return ExtendedNestedScrollView( - key: _userController.key, - controller: _userController.scrollController, - onlyOneScrollInBody: true, - pinnedHeaderSliverHeightBuilder: () => - kToolbarHeight + MediaQuery.paddingOf(context).top, - headerSliverBuilder: (context, innerBoxIsScrolled) { - return [ - _buildUserInfo( - theme, - _userController.loadingState.value, - ), - ]; - }, - body: _userController.tab2?.isNotEmpty == true - ? SafeArea( - top: false, - bottom: false, - child: Column( - children: [ - if ((_userController.tab2?.length ?? 0) > 1) - TabBar( - controller: _userController.tabController, - tabs: _userController.tabs, - onTap: _userController.onTapTab, - ), - Expanded(child: _buildBody), - ], - ), - ) - : const Center(child: Text('EMPTY')), + child: Obx( + () { + if (_userController.loadingState.value.isSuccess) { + return ExtendedNestedScrollView( + key: _userController.key, + controller: _userController.scrollController, + onlyOneScrollInBody: true, + pinnedHeaderSliverHeightBuilder: () => + kToolbarHeight + padding.top, + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + _buildUserInfo( + theme, + padding, + _userController.loadingState.value, + ), + ]; + }, + body: _userController.tab2?.isNotEmpty == true + ? Padding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + ), + child: Column( + children: [ + if ((_userController.tab2?.length ?? 0) > 1) + TabBar( + controller: _userController.tabController, + tabs: _userController.tabs, + onTap: _userController.onTapTab, + ), + Expanded(child: _buildBody), + ], + ), + ) + : const Center(child: Text('EMPTY')), + ); + } + return Center( + child: _buildUserInfo( + theme, + padding, + _userController.loadingState.value, + ), ); - } - return Center( - child: _buildUserInfo(theme, _userController.loadingState.value), - ); - }), + }, + ), ); } @@ -304,7 +314,11 @@ class _MemberPageState extends State { }).toList(), ); - Widget _buildUserInfo(ThemeData theme, LoadingState userState) { + Widget _buildUserInfo( + ThemeData theme, + EdgeInsets padding, + LoadingState userState, + ) { switch (userState) { case Loading(): return const CircularProgressIndicator(); @@ -324,6 +338,7 @@ class _MemberPageState extends State { onFollow: () => _userController.onFollow(context), live: _userController.live, silence: _userController.silence, + padding: padding, ), ), ); diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart index fbe91ec4c..a5f4b0487 100644 --- a/lib/pages/member/widget/user_info_card.dart +++ b/lib/pages/member/widget/user_info_card.dart @@ -29,6 +29,7 @@ class UserInfoCard extends StatelessWidget { required this.onFollow, this.live, this.silence, + required this.padding, }); final bool isOwner; @@ -38,6 +39,7 @@ class UserInfoCard extends StatelessWidget { final VoidCallback onFollow; final Live? live; final int? silence; + final EdgeInsets padding; @override Widget build(BuildContext context) { @@ -529,8 +531,12 @@ class UserInfoCard extends StatelessWidget { children: [ // _buildHeader(context), const SizedBox(height: 56), - SafeArea( - bottom: false, + Padding( + padding: EdgeInsets.only( + top: padding.top, + left: padding.left, + right: padding.right, + ), child: Row( children: [ const SizedBox(width: 20), diff --git a/lib/pages/member_article/view.dart b/lib/pages/member_article/view.dart index ed7a3ec41..b1dbef3f4 100644 --- a/lib/pages/member_article/view.dart +++ b/lib/pages/member_article/view.dart @@ -43,7 +43,7 @@ class _MemberArticleState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), diff --git a/lib/pages/member_audio/view.dart b/lib/pages/member_audio/view.dart index f5dd0bae0..335962c3d 100644 --- a/lib/pages/member_audio/view.dart +++ b/lib/pages/member_audio/view.dart @@ -41,7 +41,7 @@ class _MemberAudioState extends State slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), diff --git a/lib/pages/member_cheese/view.dart b/lib/pages/member_cheese/view.dart index 21c7e1e31..f0653cbe7 100644 --- a/lib/pages/member_cheese/view.dart +++ b/lib/pages/member_cheese/view.dart @@ -40,7 +40,7 @@ class _MemberCheeseState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), diff --git a/lib/pages/member_coin_arc/view.dart b/lib/pages/member_coin_arc/view.dart index a3de2fd3c..95b94d1d4 100644 --- a/lib/pages/member_coin_arc/view.dart +++ b/lib/pages/member_coin_arc/view.dart @@ -36,31 +36,29 @@ class _MemberCoinArcPageState extends State { @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Text( '${widget.mid == accountService.mid ? '我' : '${widget.name}'}的最近投币', ), ), - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _ctr.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - top: 7, - left: StyleString.safeSpace, - right: StyleString.safeSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx(() => _buildBody(_ctr.loadingState.value)), + body: refreshIndicator( + onRefresh: _ctr.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + top: 7, + left: StyleString.safeSpace + padding.left, + right: StyleString.safeSpace + padding.right, + bottom: padding.bottom + 100, ), - ], - ), + sliver: Obx(() => _buildBody(_ctr.loadingState.value)), + ), + ], ), ), ); diff --git a/lib/pages/member_comic/view.dart b/lib/pages/member_comic/view.dart index e49b25560..e37f7ab94 100644 --- a/lib/pages/member_comic/view.dart +++ b/lib/pages/member_comic/view.dart @@ -39,7 +39,7 @@ class _MemberComicState extends State slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), diff --git a/lib/pages/member_contribute/view.dart b/lib/pages/member_contribute/view.dart index e3f05fdb2..2e5219a9b 100644 --- a/lib/pages/member_contribute/view.dart +++ b/lib/pages/member_contribute/view.dart @@ -49,7 +49,9 @@ class _MemberContributeState extends State SizedBox( width: double.infinity, child: TabBar( - overlayColor: WidgetStateProperty.all(Colors.transparent), + overlayColor: const WidgetStatePropertyAll( + Colors.transparent, + ), splashFactory: NoSplash.splashFactory, padding: const EdgeInsets.symmetric(horizontal: 8), isScrollable: true, diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index fefbf035a..91aac6c31 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -43,26 +43,29 @@ class _MemberDynamicsPageState extends State @override Widget build(BuildContext context) { super.build(context); + final padding = MediaQuery.viewPaddingOf(context); return widget.mid == null ? Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('我的动态')), - body: SafeArea( - bottom: false, - child: _buildBody, + body: Padding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + ), + child: _buildBody(padding), ), ) - : _buildBody; + : _buildBody(padding); } - Widget get _buildBody => refreshIndicator( + Widget _buildBody(EdgeInsets padding) => refreshIndicator( onRefresh: _memberDynamicController.onRefresh, child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: buildPage( Obx( () => _buildContent(_memberDynamicController.loadingState.value), diff --git a/lib/pages/member_favorite/view.dart b/lib/pages/member_favorite/view.dart index d66edadd3..d561853d2 100644 --- a/lib/pages/member_favorite/view.dart +++ b/lib/pages/member_favorite/view.dart @@ -44,7 +44,7 @@ class _MemberFavoriteState extends State slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), diff --git a/lib/pages/member_home/view.dart b/lib/pages/member_home/view.dart index 86ff14644..9c0021fe4 100644 --- a/lib/pages/member_home/view.dart +++ b/lib/pages/member_home/view.dart @@ -253,7 +253,7 @@ class _MemberHomeState extends State ], SliverToBoxAdapter( child: SizedBox( - height: 80 + MediaQuery.paddingOf(context).bottom, + height: 100 + MediaQuery.viewPaddingOf(context).bottom, ), ), ], diff --git a/lib/pages/member_like_arc/view.dart b/lib/pages/member_like_arc/view.dart index dd1e88a09..0e4b93e02 100644 --- a/lib/pages/member_like_arc/view.dart +++ b/lib/pages/member_like_arc/view.dart @@ -36,31 +36,29 @@ class _MemberLikeArcPageState extends State { @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Text( '${widget.mid == accountService.mid ? '我' : '${widget.name}'}的推荐', ), ), - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _ctr.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - top: 7, - left: StyleString.safeSpace, - right: StyleString.safeSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx(() => _buildBody(_ctr.loadingState.value)), + body: refreshIndicator( + onRefresh: _ctr.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + top: 7, + left: StyleString.safeSpace + padding.left, + right: StyleString.safeSpace + padding.right, + bottom: padding.bottom + 100, ), - ], - ), + sliver: Obx(() => _buildBody(_ctr.loadingState.value)), + ), + ], ), ), ); diff --git a/lib/pages/member_opus/view.dart b/lib/pages/member_opus/view.dart index e3ccfefcb..e6cb1d18d 100644 --- a/lib/pages/member_opus/view.dart +++ b/lib/pages/member_opus/view.dart @@ -42,7 +42,7 @@ class _MemberOpusState extends State @override Widget build(BuildContext context) { super.build(context); - final bottom = MediaQuery.paddingOf(context).bottom; + final bottom = MediaQuery.viewPaddingOf(context).bottom; return Stack( children: [ refreshIndicator( diff --git a/lib/pages/member_pgc/view.dart b/lib/pages/member_pgc/view.dart index d08e718b1..ddcd550cd 100644 --- a/lib/pages/member_pgc/view.dart +++ b/lib/pages/member_pgc/view.dart @@ -49,10 +49,7 @@ class _MemberBangumiState extends State left: StyleString.safeSpace, right: StyleString.safeSpace, top: StyleString.safeSpace, - bottom: - StyleString.safeSpace + - MediaQuery.paddingOf(context).bottom + - 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(_controller.loadingState.value), diff --git a/lib/pages/member_profile/view.dart b/lib/pages/member_profile/view.dart index 93bb7df5f..4d2939c50 100644 --- a/lib/pages/member_profile/view.dart +++ b/lib/pages/member_profile/view.dart @@ -58,6 +58,7 @@ class _EditProfilePageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('账号资料')), body: _buildBody(theme, _loadingState), ); @@ -127,7 +128,7 @@ class _EditProfilePageState extends State { Loading() => loadingWidget, Success(:var response) => ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 25, + bottom: MediaQuery.viewPaddingOf(context).bottom + 25, ), children: [ divider1, diff --git a/lib/pages/member_search/child/view.dart b/lib/pages/member_search/child/view.dart index 5e78ece63..501c01eef 100644 --- a/lib/pages/member_search/child/view.dart +++ b/lib/pages/member_search/child/view.dart @@ -43,7 +43,7 @@ class _MemberSearchChildPageState extends State SliverPadding( padding: EdgeInsets.only( top: widget.searchType == MemberSearchType.archive ? 7 : 0, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: switch (widget.searchType) { MemberSearchType.archive => Obx( diff --git a/lib/pages/member_search/view.dart b/lib/pages/member_search/view.dart index 2313f34be..049396ec0 100644 --- a/lib/pages/member_search/view.dart +++ b/lib/pages/member_search/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/models/common/member/search_type.dart'; import 'package:PiliPlus/pages/member_search/child/view.dart'; import 'package:PiliPlus/pages/member_search/controller.dart'; @@ -55,9 +56,7 @@ class _MemberSearchPageState extends State { }, ), ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: Stack( clipBehavior: Clip.none, children: [ diff --git a/lib/pages/member_season_series/view.dart b/lib/pages/member_season_series/view.dart index e10f15169..219f5f7b4 100644 --- a/lib/pages/member_season_series/view.dart +++ b/lib/pages/member_season_series/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/member/contribute_type.dart'; import 'package:PiliPlus/models_new/space/space_season_series/season.dart' @@ -41,7 +42,7 @@ class _SeasonSeriesPageState extends State slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(_controller.loadingState.value), @@ -72,12 +73,9 @@ class _SeasonSeriesPageState extends State : item.meta!.seriesId; Get.to( Scaffold( - appBar: AppBar( - title: Text(item.meta!.name!), - ), - body: SafeArea( - top: false, - bottom: false, + resizeToAvoidBottomInset: false, + appBar: AppBar(title: Text(item.meta!.name!)), + body: ViewSafeArea( child: MemberVideo( type: isSeason ? ContributeType.season diff --git a/lib/pages/member_upower_rank/view.dart b/lib/pages/member_upower_rank/view.dart index b8b773220..92ea3b032 100644 --- a/lib/pages/member_upower_rank/view.dart +++ b/lib/pages/member_upower_rank/view.dart @@ -47,6 +47,7 @@ class _UpowerRankPageState extends State Widget build(BuildContext context) { super.build(context); final theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); final child = refreshIndicator( onRefresh: _controller.onRefresh, child: CustomScrollView( @@ -54,9 +55,7 @@ class _UpowerRankPageState extends State physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: Obx( () => _bilidBody(theme, _controller.loadingState.value), ), @@ -66,6 +65,7 @@ class _UpowerRankPageState extends State ); if (widget.privilegeType == null) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Obx(() { final name = _controller.name.value; @@ -76,9 +76,8 @@ class _UpowerRankPageState extends State ); }), ), - body: SafeArea( - top: false, - bottom: false, + body: Padding( + padding: EdgeInsets.only(left: padding.left, right: padding.right), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 625), diff --git a/lib/pages/member_video/view.dart b/lib/pages/member_video/view.dart index b91f74235..814d105c7 100644 --- a/lib/pages/member_video/view.dart +++ b/lib/pages/member_video/view.dart @@ -58,15 +58,14 @@ class _MemberVideoState extends State Widget build(BuildContext context) { super.build(context); final theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); Widget child = refreshIndicator( onRefresh: _controller.onRefresh, child: CustomScrollView( physics: ReloadScrollPhysics(controller: _controller), slivers: [ SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), ), @@ -83,38 +82,34 @@ class _MemberVideoState extends State Obx( () => _controller.isLocating.value != true ? Positioned( - right: 15, - bottom: 15, - child: SafeArea( - top: false, - left: false, - child: FloatingActionButton.extended( - onPressed: () { - final fromViewAid = _controller.fromViewAid; + right: 15 + padding.right, + bottom: 15 + padding.bottom, + child: FloatingActionButton.extended( + onPressed: () { + final fromViewAid = _controller.fromViewAid; + _controller + ..isLocating.value = true + ..lastAid = fromViewAid; + final locatedIndex = _controller + .loadingState + .value + .dataOrNull + ?.indexWhere((i) => i.param == fromViewAid); + if (locatedIndex == null || locatedIndex == -1) { _controller - ..isLocating.value = true - ..lastAid = fromViewAid; - final locatedIndex = _controller - .loadingState - .value - .dataOrNull - ?.indexWhere((i) => i.param == fromViewAid); - if (locatedIndex == null || locatedIndex == -1) { - _controller - ..reload = true - ..page = 0 - ..loadingState.value = LoadingState.loading() - ..queryData(); - } else { - PrimaryScrollController.of(context).jumpTo( - gridDelegate.layoutCache! - .getGeometryForChildIndex(locatedIndex) - .scrollOffset, - ); - } - }, - label: const Text('定位至上次观看'), - ), + ..reload = true + ..page = 0 + ..loadingState.value = LoadingState.loading() + ..queryData(); + } else { + PrimaryScrollController.of(context).jumpTo( + gridDelegate.layoutCache! + .getGeometryForChildIndex(locatedIndex) + .scrollOffset, + ); + } + }, + label: const Text('定位至上次观看'), ), ) : const SizedBox.shrink(), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 38dbc62c8..50e20fc87 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -177,7 +177,7 @@ class MineController top: 15, left: 20, right: 20, - bottom: MediaQuery.paddingOf(context).bottom + 15, + bottom: MediaQuery.viewPaddingOf(context).bottom + 15, ), child: Column( mainAxisSize: MainAxisSize.min, @@ -248,7 +248,7 @@ class MineController top: 15, left: 20, right: 20, - bottom: MediaQuery.paddingOf(context).bottom + 15, + bottom: MediaQuery.viewPaddingOf(context).bottom + 15, ), child: Row( children: [ diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index aef6385a7..c017a6969 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -492,7 +492,7 @@ class _MediaPageState extends CommonPageState child: IconButton( tooltip: '查看更多', style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: const WidgetStatePropertyAll(EdgeInsets.zero), backgroundColor: WidgetStatePropertyAll( theme.colorScheme.secondaryContainer.withValues( alpha: 0.5, diff --git a/lib/pages/msg_feed_top/at_me/view.dart b/lib/pages/msg_feed_top/at_me/view.dart index c9ae30ac4..e5e63362f 100644 --- a/lib/pages/msg_feed_top/at_me/view.dart +++ b/lib/pages/msg_feed_top/at_me/view.dart @@ -29,6 +29,7 @@ class _AtMePageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('@我的'), actions: [ @@ -54,7 +55,7 @@ class _AtMePageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _atMeController.loadingState.value), diff --git a/lib/pages/msg_feed_top/like_detail/view.dart b/lib/pages/msg_feed_top/like_detail/view.dart index 472cfb306..a3e3c6c21 100644 --- a/lib/pages/msg_feed_top/like_detail/view.dart +++ b/lib/pages/msg_feed_top/like_detail/view.dart @@ -30,6 +30,7 @@ class _LikeDetailPageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('点赞详情')), body: refreshIndicator( onRefresh: _controller.onRefresh, @@ -38,7 +39,7 @@ class _LikeDetailPageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), diff --git a/lib/pages/msg_feed_top/like_me/view.dart b/lib/pages/msg_feed_top/like_me/view.dart index a437ad04a..ba0ee9a77 100644 --- a/lib/pages/msg_feed_top/like_me/view.dart +++ b/lib/pages/msg_feed_top/like_me/view.dart @@ -30,6 +30,7 @@ class _LikeMePageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('收到的赞'), actions: [ @@ -55,7 +56,7 @@ class _LikeMePageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _likeMeController.loadingState.value), diff --git a/lib/pages/msg_feed_top/reply_me/view.dart b/lib/pages/msg_feed_top/reply_me/view.dart index 76c9c4308..dc618b6c8 100644 --- a/lib/pages/msg_feed_top/reply_me/view.dart +++ b/lib/pages/msg_feed_top/reply_me/view.dart @@ -29,6 +29,7 @@ class _ReplyMePageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('回复我的'), actions: [ @@ -54,7 +55,7 @@ class _ReplyMePageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _replyMeController.loadingState.value), diff --git a/lib/pages/msg_feed_top/sys_msg/view.dart b/lib/pages/msg_feed_top/sys_msg/view.dart index 7cbe034ac..71669eef4 100644 --- a/lib/pages/msg_feed_top/sys_msg/view.dart +++ b/lib/pages/msg_feed_top/sys_msg/view.dart @@ -31,9 +31,8 @@ class _SysMsgPageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( - appBar: AppBar( - title: const Text('系统通知'), - ), + resizeToAvoidBottomInset: false, + appBar: AppBar(title: const Text('系统通知')), body: refreshIndicator( onRefresh: _sysMsgController.onRefresh, child: CustomScrollView( @@ -41,7 +40,7 @@ class _SysMsgPageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _sysMsgController.loadingState.value), diff --git a/lib/pages/pgc/view.dart b/lib/pages/pgc/view.dart index f9719ee4d..efe46c0b1 100644 --- a/lib/pages/pgc/view.dart +++ b/lib/pages/pgc/view.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/fav_type.dart'; import 'package:PiliPlus/models/common/home_tab_type.dart'; @@ -105,7 +106,7 @@ class _PgcPageState extends CommonPageState isScrollable: true, tabAlignment: TabAlignment.start, dividerHeight: 0, - overlayColor: WidgetStateProperty.all( + overlayColor: const WidgetStatePropertyAll( Colors.transparent, ), splashFactory: NoSplash.splashFactory, @@ -160,6 +161,7 @@ class _PgcPageState extends CommonPageState physics: const AlwaysScrollableScrollPhysics(), scrollDirection: Axis.horizontal, itemCount: item.episodes!.length, + padding: EdgeInsets.zero, itemBuilder: (context, index) { return Container( width: Grid.smallCardWidth / 2, @@ -204,7 +206,7 @@ class _PgcPageState extends CommonPageState padding: EdgeInsets.only( left: StyleString.safeSpace, right: StyleString.safeSpace, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildRcmdBody(controller.loadingState.value), @@ -243,6 +245,7 @@ class _PgcPageState extends CommonPageState List types = const [102, 2, 5, 3, 7]; Get.to( Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('索引')), body: DefaultTabController( length: types.length, @@ -250,9 +253,7 @@ class _PgcPageState extends CommonPageState builder: (context) { return Column( children: [ - SafeArea( - top: false, - bottom: false, + ViewSafeArea( child: TabBar( tabs: titles .map((title) => Tab(text: title)) @@ -436,6 +437,7 @@ class _PgcPageState extends CommonPageState controller: controller.followController, scrollDirection: Axis.horizontal, itemCount: response!.length, + padding: EdgeInsets.zero, itemBuilder: (context, index) { if (index == response.length - 1) { controller.queryPgcFollow(false); diff --git a/lib/pages/pgc_index/view.dart b/lib/pages/pgc_index/view.dart index 89d4710cb..800d4d104 100644 --- a/lib/pages/pgc_index/view.dart +++ b/lib/pages/pgc_index/view.dart @@ -38,6 +38,7 @@ class _PgcIndexPageState extends State final theme = Theme.of(context); return widget.indexType == null ? Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('索引')), body: Obx(() => _buildBody(theme, _ctr.conditionState.value)), ) @@ -48,6 +49,7 @@ class _PgcIndexPageState extends State ThemeData theme, LoadingState loadingState, ) { + final padding = MediaQuery.viewPaddingOf(context); return switch (loadingState) { Loading() => loadingWidget, Success(:var response) => Builder( @@ -56,8 +58,8 @@ class _PgcIndexPageState extends State (response.order?.isNotEmpty == true ? 1 : 0) + (response.filter?.length ?? 0); if (count == 0) return const SizedBox.shrink(); - return SafeArea( - bottom: false, + return Padding( + padding: EdgeInsets.only(left: padding.left, right: padding.right), child: CustomScrollView( controller: _ctr.scrollController, slivers: [ @@ -78,7 +80,7 @@ class _PgcIndexPageState extends State left: StyleString.safeSpace, right: StyleString.safeSpace, top: 12, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: padding.bottom + 100, ), sliver: Obx(() => _buildList(_ctr.loadingState.value)), ), diff --git a/lib/pages/pgc_review/child/view.dart b/lib/pages/pgc_review/child/view.dart index a89d300cd..053ce55f1 100644 --- a/lib/pages/pgc_review/child/view.dart +++ b/lib/pages/pgc_review/child/view.dart @@ -62,7 +62,7 @@ class _PgcReviewChildPageState extends State _buildHeader(theme), SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 100, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), diff --git a/lib/pages/pgc_review/view.dart b/lib/pages/pgc_review/view.dart index dc34a5aa0..705d34be7 100644 --- a/lib/pages/pgc_review/view.dart +++ b/lib/pages/pgc_review/view.dart @@ -50,7 +50,7 @@ class _PgcReviewPageState extends State tabAlignment: TabAlignment.start, dividerHeight: 0, indicatorWeight: 0, - overlayColor: WidgetStateProperty.all(Colors.transparent), + overlayColor: const WidgetStatePropertyAll(Colors.transparent), splashFactory: NoSplash.splashFactory, padding: const EdgeInsets.only(left: 6), indicatorPadding: const EdgeInsets.symmetric( @@ -104,7 +104,7 @@ class _PgcReviewPageState extends State ), Positioned( right: 16, - bottom: MediaQuery.paddingOf(context).bottom + 16, + bottom: MediaQuery.viewPaddingOf(context).bottom + 16, child: FloatingActionButton( onPressed: () => showDialog( context: context, diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 53eb59a2a..105ae6f80 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -28,7 +28,7 @@ class _RankPageState extends State width: 64, child: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: List.generate( RankType.values.length, diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index 3097f97d0..a4ef30610 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -43,7 +43,7 @@ class _ZonePageState extends CommonPageState SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(controller.loadingState.value)), ), diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index d89e3ad8f..c371e5a0b 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -41,7 +41,7 @@ class _RcmdPageState extends CommonPageState SliverPadding( padding: EdgeInsets.only( top: StyleString.cardSpace, - bottom: MediaQuery.paddingOf(context).bottom, + bottom: MediaQuery.viewPaddingOf(context).bottom, ), sliver: Obx(() => _buildBody(controller.loadingState.value)), ), diff --git a/lib/pages/save_panel/view.dart b/lib/pages/save_panel/view.dart index 24193ee85..76bdcf93a 100644 --- a/lib/pages/save_panel/view.dart +++ b/lib/pages/save_panel/view.dart @@ -296,6 +296,7 @@ class _SavePanelState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); return GestureDetector( behavior: HitTestBehavior.opaque, onTap: Get.back, @@ -304,212 +305,208 @@ class _SavePanelState extends State { alignment: Alignment.center, children: [ SingleChildScrollView( - padding: const EdgeInsets.only(top: 12, bottom: 80), - child: SafeArea( - child: GestureDetector( - onTap: () {}, - child: Container( - width: context.mediaQueryShortestSide, - margin: const EdgeInsets.symmetric(horizontal: 12), - child: RepaintBoundary( - key: boundaryKey, - child: Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: theme.colorScheme.surface, - borderRadius: const BorderRadius.all( - Radius.circular(12), - ), + padding: EdgeInsets.only( + top: 12 + padding.top, + bottom: 80 + padding.bottom, + ), + child: GestureDetector( + onTap: () {}, + child: Container( + width: context.mediaQueryShortestSide, + margin: const EdgeInsets.symmetric(horizontal: 12), + child: RepaintBoundary( + key: boundaryKey, + child: Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: theme.colorScheme.surface, + borderRadius: const BorderRadius.all( + Radius.circular(12), ), - child: AnimatedSize( - curve: Curves.easeInOut, - alignment: Alignment.topCenter, - duration: const Duration(milliseconds: 255), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_item is ReplyInfo) - IgnorePointer( - child: ReplyItemGrpc( - replyItem: _item, - replyLevel: 0, - needDivider: false, - upMid: widget.upMid, - ), - ) - else if (_item is DynamicItemModel) - IgnorePointer( - child: LayoutBuilder( - builder: (_, constrains) => DynamicPanel( - item: _item, - isDetail: true, - isSave: true, - maxWidth: constrains.maxWidth, - ), + ), + child: AnimatedSize( + curve: Curves.easeInOut, + alignment: Alignment.topCenter, + duration: const Duration(milliseconds: 255), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_item is ReplyInfo) + IgnorePointer( + child: ReplyItemGrpc( + replyItem: _item, + replyLevel: 0, + needDivider: false, + upMid: widget.upMid, + ), + ) + else if (_item is DynamicItemModel) + IgnorePointer( + child: LayoutBuilder( + builder: (_, constrains) => DynamicPanel( + item: _item, + isDetail: true, + isSave: true, + maxWidth: constrains.maxWidth, ), ), - if (cover?.isNotEmpty == true && - title?.isNotEmpty == true) - Container( - height: 81, - clipBehavior: Clip.hardEdge, - margin: const EdgeInsets.symmetric( - horizontal: 12, + ), + if (cover?.isNotEmpty == true && + title?.isNotEmpty == true) + Container( + height: 81, + clipBehavior: Clip.hardEdge, + margin: const EdgeInsets.symmetric( + horizontal: 12, + ), + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: theme.colorScheme.onInverseSurface, + borderRadius: const BorderRadius.all( + Radius.circular(8), ), - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: theme.colorScheme.onInverseSurface, - borderRadius: const BorderRadius.all( - Radius.circular(8), + ), + child: Row( + children: [ + NetworkImgLayer( + radius: 6, + src: cover!, + height: MediaQuery.textScalerOf( + context, + ).scale(65), + width: + MediaQuery.textScalerOf( + context, + ).scale(65) * + 16 / + 9, + quality: 100, ), - ), - child: Row( - children: [ - NetworkImgLayer( - radius: 6, - src: cover!, - height: MediaQuery.textScalerOf( - context, - ).scale(65), - width: - MediaQuery.textScalerOf( - context, - ).scale(65) * - 16 / - 9, - quality: 100, - ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '$title\n', + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + if (pubdate != null) ...[ + const Spacer(), Text( - '$title\n', - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - if (pubdate != null) ...[ - const Spacer(), - Text( - DateUtil.format( - pubdate, - format: DateUtil.longFormatDs, - ), - style: TextStyle( - color: - theme.colorScheme.outline, - ), + DateUtil.format( + pubdate, + format: DateUtil.longFormatDs, ), - ], + style: TextStyle( + color: theme.colorScheme.outline, + ), + ), ], - ), + ], ), - ], - ), + ), + ], ), - showBottom - ? Stack( - clipBehavior: Clip.none, - children: [ - if (uri.isNotEmpty) - Align( - alignment: Alignment.centerRight, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisSize: - MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - if (uname?.isNotEmpty == - true) ...[ - Text( - '@$uname', - maxLines: 1, - overflow: TextOverflow - .ellipsis, - style: TextStyle( - color: theme - .colorScheme - .primary, - ), - ), - const SizedBox(height: 4), - ], + ), + showBottom + ? Stack( + clipBehavior: Clip.none, + children: [ + if (uri.isNotEmpty) + Align( + alignment: Alignment.centerRight, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + if (uname?.isNotEmpty == + true) ...[ Text( - '识别二维码,$viewType$itemType', - textAlign: TextAlign.end, + '@$uname', + maxLines: 1, + overflow: + TextOverflow.ellipsis, style: TextStyle( color: theme .colorScheme - .onSurfaceVariant, + .primary, ), ), const SizedBox(height: 4), - Text( - DateUtil.longFormatDs - .format( - DateTime.now(), - ), - textAlign: TextAlign.end, - style: TextStyle( - fontSize: 13, - color: theme - .colorScheme - .outline, - ), - ), ], - ), - ), - Container( - width: 100, - height: 100, - padding: const EdgeInsets.all( - 12, - ), - child: Container( - color: Get.isDarkMode - ? Colors.white - : theme - .colorScheme - .surface, - padding: const EdgeInsets.all( - 3, + Text( + '识别二维码,$viewType$itemType', + textAlign: TextAlign.end, + style: TextStyle( + color: theme + .colorScheme + .onSurfaceVariant, + ), ), - child: PrettyQrView.data( - data: uri, - decoration: - const PrettyQrDecoration( - shape: - PrettyQrSquaresSymbol(), + const SizedBox(height: 4), + Text( + DateUtil.longFormatDs + .format( + DateTime.now(), ), + textAlign: TextAlign.end, + style: TextStyle( + fontSize: 13, + color: theme + .colorScheme + .outline, + ), ), + ], + ), + ), + Container( + width: 100, + height: 100, + padding: const EdgeInsets.all( + 12, + ), + child: Container( + color: Get.isDarkMode + ? Colors.white + : theme.colorScheme.surface, + padding: const EdgeInsets.all( + 3, + ), + child: PrettyQrView.data( + data: uri, + decoration: + const PrettyQrDecoration( + shape: + PrettyQrSquaresSymbol(), + ), ), ), - ], - ), - ), - Align( - alignment: Alignment.centerLeft, - child: Image.asset( - 'assets/images/logo/logo_2.png', - width: 100, - color: theme - .colorScheme - .onSurfaceVariant, + ), + ], ), ), - ], - ) - : const SizedBox(height: 12), - ], - ), + Align( + alignment: Alignment.centerLeft, + child: Image.asset( + 'assets/images/logo/logo_2.png', + width: 100, + color: + theme.colorScheme.onSurfaceVariant, + ), + ), + ], + ) + : const SizedBox(height: 12), + ], ), ), ), @@ -518,10 +515,10 @@ class _SavePanelState extends State { ), ), Positioned( - left: 0, - right: 0, - bottom: 0, - child: Container( + left: padding.left, + right: padding.right, + bottom: 25 + padding.bottom, + child: DecoratedBox( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, @@ -532,51 +529,45 @@ class _SavePanelState extends State { ], ), ), - padding: const EdgeInsets.only(bottom: 25, top: 10), - child: SafeArea( - top: false, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - iconButton( - size: 42, - tooltip: '关闭', - context: context, - icon: Icons.clear, - onPressed: Get.back, - bgColor: theme.colorScheme.onInverseSurface, - iconColor: theme.colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 40), - iconButton( - size: 42, - tooltip: showBottom ? '隐藏' : '显示', - context: context, - icon: showBottom - ? Icons.visibility_off - : Icons.visibility, - onPressed: () => setState(() { - showBottom = !showBottom; - }), - ), - const SizedBox(width: 40), - iconButton( - size: 42, - tooltip: '分享', - context: context, - icon: Icons.share, - onPressed: () => _onSaveOrSharePic(true), - ), - const SizedBox(width: 40), - iconButton( - size: 42, - tooltip: '保存', - context: context, - icon: Icons.save_alt, - onPressed: _onSaveOrSharePic, - ), - ], - ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + iconButton( + size: 42, + tooltip: '关闭', + context: context, + icon: Icons.clear, + onPressed: Get.back, + bgColor: theme.colorScheme.onInverseSurface, + iconColor: theme.colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 40), + iconButton( + size: 42, + tooltip: showBottom ? '隐藏' : '显示', + context: context, + icon: showBottom ? Icons.visibility_off : Icons.visibility, + onPressed: () => setState(() { + showBottom = !showBottom; + }), + ), + const SizedBox(width: 40), + iconButton( + size: 42, + tooltip: '分享', + context: context, + icon: Icons.share, + onPressed: () => _onSaveOrSharePic(true), + ), + const SizedBox(width: 40), + iconButton( + size: 42, + tooltip: '保存', + context: context, + icon: Icons.save_alt, + onPressed: _onSaveOrSharePic, + ), + ], ), ), ), diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index 09620e721..57e5b6923 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -80,7 +80,7 @@ class _SearchPageState extends State { ), ), body: ListView( - padding: MediaQuery.paddingOf(context).copyWith(top: 0), + padding: MediaQuery.viewPaddingOf(context).copyWith(top: 0), children: [ if (_searchController.searchSuggestion) _searchSuggest(), if (isPortrait) ...[ diff --git a/lib/pages/search_panel/article/controller.dart b/lib/pages/search_panel/article/controller.dart index dc5e2ca02..74858e343 100644 --- a/lib/pages/search_panel/article/controller.dart +++ b/lib/pages/search_panel/article/controller.dart @@ -60,7 +60,7 @@ class SearchArticleController top: 20, left: 16, right: 16, - bottom: 80 + MediaQuery.paddingOf(context).bottom, + bottom: 100 + MediaQuery.viewPaddingOf(context).bottom, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/search_panel/article/view.dart b/lib/pages/search_panel/article/view.dart index c492025cc..29592750c 100644 --- a/lib/pages/search_panel/article/view.dart +++ b/lib/pages/search_panel/article/view.dart @@ -71,8 +71,8 @@ class _SearchArticlePanelState height: 32, child: IconButton( tooltip: '筛选', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterDialog(context), icon: Icon( diff --git a/lib/pages/search_panel/user/controller.dart b/lib/pages/search_panel/user/controller.dart index 4bbe0519a..f44cf91a4 100644 --- a/lib/pages/search_panel/user/controller.dart +++ b/lib/pages/search_panel/user/controller.dart @@ -40,7 +40,7 @@ class SearchUserController top: 20, left: 16, right: 16, - bottom: 80 + MediaQuery.paddingOf(context).bottom, + bottom: 100 + MediaQuery.viewPaddingOf(context).bottom, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/search_panel/user/view.dart b/lib/pages/search_panel/user/view.dart index 695f246b6..be5e956eb 100644 --- a/lib/pages/search_panel/user/view.dart +++ b/lib/pages/search_panel/user/view.dart @@ -71,8 +71,8 @@ class _SearchUserPanelState height: 32, child: IconButton( tooltip: '筛选', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterDialog(context), icon: Icon( diff --git a/lib/pages/search_panel/video/controller.dart b/lib/pages/search_panel/video/controller.dart index 5d8f8150d..05086e076 100644 --- a/lib/pages/search_panel/video/controller.dart +++ b/lib/pages/search_panel/video/controller.dart @@ -160,7 +160,7 @@ class SearchVideoController top: 20, left: 16, right: 16, - bottom: 80 + MediaQuery.paddingOf(context).bottom, + bottom: 100 + MediaQuery.viewPaddingOf(context).bottom, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/search_panel/video/view.dart b/lib/pages/search_panel/video/view.dart index 481af69b3..fa7e08265 100644 --- a/lib/pages/search_panel/video/view.dart +++ b/lib/pages/search_panel/video/view.dart @@ -83,8 +83,8 @@ class _SearchVideoPanelState height: 32, child: IconButton( tooltip: '筛选', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterDialog(context), icon: Icon( diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index ba489b1cc..ee41fd7a3 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -58,7 +58,7 @@ abstract class CommonSearchPanelState< response?.isNotEmpty == true ? SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: buildList(theme, response!), ) diff --git a/lib/pages/search_result/view.dart b/lib/pages/search_result/view.dart index 73a260cc1..61fc1093f 100644 --- a/lib/pages/search_result/view.dart +++ b/lib/pages/search_result/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/models/common/search/search_type.dart'; import 'package:PiliPlus/pages/search/controller.dart'; import 'package:PiliPlus/pages/search_panel/article/view.dart'; @@ -65,6 +66,7 @@ class _SearchResultPageState extends State Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( shape: Border( bottom: BorderSide( @@ -94,15 +96,13 @@ class _SearchResultPageState extends State ), ), ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: Column( children: [ SizedBox( width: double.infinity, child: TabBar( - overlayColor: WidgetStateProperty.all(Colors.transparent), + overlayColor: const WidgetStatePropertyAll(Colors.transparent), splashFactory: NoSplash.splashFactory, padding: const EdgeInsets.only(top: 4, left: 8, right: 8), controller: _tabController, diff --git a/lib/pages/search_trending/view.dart b/lib/pages/search_trending/view.dart index 98072c913..53d3af2bc 100644 --- a/lib/pages/search_trending/view.dart +++ b/lib/pages/search_trending/view.dart @@ -61,12 +61,14 @@ class _SearchTrendingPageState extends State { ? min(640.0, maxWidth * 0.6) : maxWidth; final height = width * 528 / 1125; - _offset = height - 56 - MediaQuery.paddingOf(context).top; + final padding = MediaQuery.viewPaddingOf(context); + _offset = height - 56 - padding.top; listener(); final removePadding = maxWidth > width; return Scaffold( extendBody: true, extendBodyBehindAppBar: true, + resizeToAvoidBottomInset: false, appBar: PreferredSize( preferredSize: const Size.fromHeight(56), child: Obx( @@ -130,9 +132,7 @@ class _SearchTrendingPageState extends State { ), ), SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 100, - ), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index b6ded4596..988182d98 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -16,12 +16,13 @@ class _ExtraSettingState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: widget.showAppBar == false ? null : AppBar(title: const Text('其它设置')), body: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: settings.map((item) => item.widget).toList(), ), diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart index 5fd71f10c..7d2180323 100644 --- a/lib/pages/setting/models/style_settings.dart +++ b/lib/pages/setting/models/style_settings.dart @@ -172,14 +172,6 @@ List get styleSettings => [ } }, ), - const SettingsModel( - settingsType: SettingsType.sw1tch, - title: '播放页移除安全边距', - subtitle: '隐藏状态栏、撑满屏幕,但播放控件仍处于安全域内', - leading: Icon(Icons.fit_screen_outlined), - setKey: SettingBoxKey.videoPlayerRemoveSafeArea, - defaultVal: false, - ), const SettingsModel( settingsType: SettingsType.sw1tch, title: '动态页启用瀑布流', diff --git a/lib/pages/setting/pages/bar_set.dart b/lib/pages/setting/pages/bar_set.dart index b57ad34a1..973af68a4 100644 --- a/lib/pages/setting/pages/bar_set.dart +++ b/lib/pages/setting/pages/bar_set.dart @@ -58,6 +58,7 @@ class _BarSetPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Text('$title编辑'), actions: [ @@ -68,7 +69,7 @@ class _BarSetPageState extends State { body: ReorderableListView( onReorder: onReorder, footer: SizedBox( - height: MediaQuery.paddingOf(context).bottom + 30, + height: MediaQuery.viewPaddingOf(context).bottom + 30, child: const Align( alignment: Alignment.centerRight, child: Text('*长按拖动排序 '), diff --git a/lib/pages/setting/pages/color_select.dart b/lib/pages/setting/pages/color_select.dart index dde568fc4..a278b926d 100644 --- a/lib/pages/setting/pages/color_select.dart +++ b/lib/pages/setting/pages/color_select.dart @@ -55,6 +55,7 @@ class _ColorSelectPageState extends State { ); final size = Get.size; return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('选择应用主题')), body: SafeArea( bottom: false, diff --git a/lib/pages/setting/pages/display_mode.dart b/lib/pages/setting/pages/display_mode.dart index d3436e6b3..8437def8d 100644 --- a/lib/pages/setting/pages/display_mode.dart +++ b/lib/pages/setting/pages/display_mode.dart @@ -60,9 +60,9 @@ class _SetDisplayModeState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('屏幕帧率设置')), body: SafeArea( - top: false, bottom: false, child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/setting/pages/font_size_select.dart b/lib/pages/setting/pages/font_size_select.dart index 78a2f6a5e..79daa932b 100644 --- a/lib/pages/setting/pages/font_size_select.dart +++ b/lib/pages/setting/pages/font_size_select.dart @@ -1,3 +1,4 @@ +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; @@ -28,6 +29,7 @@ class _FontSizeSelectPageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( actions: [ TextButton( @@ -41,7 +43,7 @@ class _FontSizeSelectPageState extends State { const SizedBox(width: 12), ], ), - body: SafeArea( + body: ViewSafeArea( child: Column( children: [ Expanded( diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart index 1d4de6bb5..66af092e1 100644 --- a/lib/pages/setting/pages/logs.dart +++ b/lib/pages/setting/pages/logs.dart @@ -113,7 +113,9 @@ class _LogsPageState extends State { @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('日志'), actions: [ @@ -162,77 +164,76 @@ class _LogsPageState extends State { ], ), body: logsContent.isNotEmpty - ? SafeArea( - bottom: false, - child: ListView.separated( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - itemCount: logsContent.length, - itemBuilder: (context, index) { - final log = logsContent[index]; - if (log['date'] is DateTime) { - latestLog ??= log['date']; - } - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - spacing: 5, - children: [ - Row( - spacing: 10, - children: [ - Text( - log['date'].toString(), - style: TextStyle( - fontSize: Theme.of( - context, - ).textTheme.titleMedium!.fontSize, - ), + ? ListView.separated( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + bottom: padding.bottom + 100, + ), + itemCount: logsContent.length, + itemBuilder: (context, index) { + final log = logsContent[index]; + if (log['date'] is DateTime) { + latestLog ??= log['date']; + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + spacing: 5, + children: [ + Row( + spacing: 10, + children: [ + Text( + log['date'].toString(), + style: TextStyle( + fontSize: Theme.of( + context, + ).textTheme.titleMedium!.fontSize, ), - TextButton.icon( - style: TextButton.styleFrom( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - visualDensity: VisualDensity.compact, - ), - onPressed: () { - Utils.copyText( - '```\n${log['body']}\n```', - needToast: false, - ); - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - '已将 ${log['date'].toString()} 复制至剪贴板', - ), - ), - ); - } - }, - icon: const Icon(Icons.copy_outlined, size: 16), - label: const Text('复制'), - ), - ], - ), - Card( - child: Container( - width: double.infinity, - padding: const EdgeInsets.all(12.0), - child: SelectableText(log['body']), ), + TextButton.icon( + style: TextButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + ), + onPressed: () { + Utils.copyText( + '```\n${log['body']}\n```', + needToast: false, + ); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '已将 ${log['date'].toString()} 复制至剪贴板', + ), + ), + ); + } + }, + icon: const Icon(Icons.copy_outlined, size: 16), + label: const Text('复制'), + ), + ], + ), + Card( + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(12.0), + child: SelectableText(log['body']), ), - ], - ), - ); - }, - separatorBuilder: (context, index) => const Divider( - indent: 12, - endIndent: 12, - height: 24, - ), + ), + ], + ), + ); + }, + separatorBuilder: (context, index) => const Divider( + indent: 12, + endIndent: 12, + height: 24, ), ) : scrollErrorWidget(), diff --git a/lib/pages/setting/pages/play_speed_set.dart b/lib/pages/setting/pages/play_speed_set.dart index a296ff5fd..de0e1f26d 100644 --- a/lib/pages/setting/pages/play_speed_set.dart +++ b/lib/pages/setting/pages/play_speed_set.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; import 'package:PiliPlus/utils/context_ext.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -148,7 +149,7 @@ class _PlaySpeedPageState extends State { ), ), ), - SizedBox(height: 25 + MediaQuery.paddingOf(context).bottom), + SizedBox(height: 25 + MediaQuery.viewPaddingOf(context).bottom), ], ); }, @@ -186,6 +187,7 @@ class _PlaySpeedPageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('倍速设置'), actions: [ @@ -200,8 +202,7 @@ class _PlaySpeedPageState extends State { const SizedBox(width: 16), ], ), - body: SafeArea( - bottom: false, + body: ViewSafeArea( child: ListView( children: [ Padding( diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 489129771..cccb1d4c7 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -16,12 +16,13 @@ class _PlaySettingState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: widget.showAppBar == false ? null : AppBar(title: const Text('播放器设置')), body: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: settings.map((item) => item.widget).toList(), ), diff --git a/lib/pages/setting/privacy_setting.dart b/lib/pages/setting/privacy_setting.dart index 3d63a550b..903942a39 100644 --- a/lib/pages/setting/privacy_setting.dart +++ b/lib/pages/setting/privacy_setting.dart @@ -16,12 +16,13 @@ class _PrivacySettingState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: widget.showAppBar == false ? null : AppBar(title: const Text('隐私设置')), body: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: settings.map((item) => item.widget).toList(), ), diff --git a/lib/pages/setting/recommend_setting.dart b/lib/pages/setting/recommend_setting.dart index 479b8b9ca..19f76c0f8 100644 --- a/lib/pages/setting/recommend_setting.dart +++ b/lib/pages/setting/recommend_setting.dart @@ -26,12 +26,13 @@ class _RecommendSettingState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: widget.showAppBar == false ? null : AppBar(title: const Text('推荐流设置')), body: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: [ ...part.map((item) => item.widget), diff --git a/lib/pages/setting/slide_color_picker.dart b/lib/pages/setting/slide_color_picker.dart index 0dd75882f..a9771bf13 100644 --- a/lib/pages/setting/slide_color_picker.dart +++ b/lib/pages/setting/slide_color_picker.dart @@ -64,7 +64,7 @@ class _SlideColorPickerState extends State { child: SliderTheme( data: SliderTheme.of(context).copyWith( trackHeight: 10, - thumbSize: WidgetStateProperty.all(const Size(4, 25)), + thumbSize: const WidgetStatePropertyAll(Size(4, 25)), ), child: Slider( padding: EdgeInsets.zero, diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 79b24d648..57f0ca54b 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -16,12 +16,13 @@ class _StyleSettingState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: widget.showAppBar == false ? null : AppBar(title: const Text('外观设置')), body: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: settings.map((item) => item.widget).toList(), ), diff --git a/lib/pages/setting/video_setting.dart b/lib/pages/setting/video_setting.dart index 0a01df6d3..fe58900e8 100644 --- a/lib/pages/setting/video_setting.dart +++ b/lib/pages/setting/video_setting.dart @@ -16,12 +16,13 @@ class _VideoSettingState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: widget.showAppBar == false ? null : AppBar(title: const Text('音视频设置')), body: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: settings.map((item) => item.widget).toList(), ), diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index af611a17a..a6a867e20 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -89,33 +89,27 @@ class _SettingPageState extends State { final theme = Theme.of(context); _isPortrait = context.isPortrait; return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: _isPortrait ? const Text('设置') : Text(_type.title), ), body: _isPortrait ? _buildList(theme) - : Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 4, - child: MediaQuery.removePadding( - context: context, - removeRight: true, - removeTop: true, + : SafeArea( + bottom: false, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 4, child: _buildList(theme), ), - ), - VerticalDivider( - width: 1, - color: theme.colorScheme.outline.withValues(alpha: 0.1), - ), - Expanded( - flex: 6, - child: MediaQuery.removePadding( - context: context, - removeLeft: true, - removeTop: true, + VerticalDivider( + width: 1, + color: theme.colorScheme.outline.withValues(alpha: 0.1), + ), + Expanded( + flex: 6, child: switch (_type) { SettingType.privacySetting => const PrivacySetting( showAppBar: false, @@ -141,8 +135,8 @@ class _SettingPageState extends State { SettingType.about => const AboutPage(showAppBar: false), }, ), - ), - ], + ], + ), ), ); } @@ -175,10 +169,12 @@ class _SettingPageState extends State { TextStyle subTitleStyle = theme.textTheme.labelMedium!.copyWith( color: theme.colorScheme.outline, ); - final padding = MediaQuery.paddingOf(context); return ListView( + padding: EdgeInsets.only( + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, + ), children: [ - _buildSearchItem(theme, padding), + _buildSearchItem(theme), ..._items .sublist(0, _items.length - 1) .map( @@ -212,7 +208,6 @@ class _SettingPageState extends State { leading: Icon(_items.last.icon), title: Text(_items.last.type.title, style: titleStyle), ), - SizedBox(height: padding.bottom + 80), ], ); } @@ -284,12 +279,8 @@ class _SettingPageState extends State { ); } - Widget _buildSearchItem(ThemeData theme, EdgeInsets padding) => Padding( - padding: EdgeInsets.only( - left: 16 + padding.left, - right: 16, - bottom: 8, - ), + Widget _buildSearchItem(ThemeData theme) => Padding( + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 8), child: Material( type: MaterialType.transparency, child: InkWell( diff --git a/lib/pages/settings_search/view.dart b/lib/pages/settings_search/view.dart index af59b58ab..1684ad239 100644 --- a/lib/pages/settings_search/view.dart +++ b/lib/pages/settings_search/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; +import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart'; import 'package:PiliPlus/pages/search/controller.dart' show SearchState; import 'package:PiliPlus/pages/setting/models/extra_settings.dart'; import 'package:PiliPlus/pages/setting/models/model.dart'; @@ -87,31 +88,25 @@ class _SettingsSearchPageState extends SearchState { ), ), ), - body: SafeArea( - bottom: false, - child: CustomScrollView( - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: Obx( - () => _list.isEmpty - ? const HttpError() - : SliverWaterfallFlow( - gridDelegate: - SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - ), - delegate: SliverChildBuilderDelegate( - (_, index) => _list[index].widget, - childCount: _list.length, - ), + body: CustomScrollView( + slivers: [ + ViewSliverSafeArea( + sliver: Obx( + () => _list.isEmpty + ? const HttpError() + : SliverWaterfallFlow( + gridDelegate: + SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: Grid.smallCardWidth * 2, + ), + delegate: SliverChildBuilderDelegate( + (_, index) => _list[index].widget, + childCount: _list.length, ), - ), + ), ), - ], - ), + ), + ], ), ); } diff --git a/lib/pages/space_setting/view.dart b/lib/pages/space_setting/view.dart index cbf966b47..cafac3a5c 100644 --- a/lib/pages/space_setting/view.dart +++ b/lib/pages/space_setting/view.dart @@ -21,9 +21,8 @@ class _SpaceSettingPageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( - appBar: AppBar( - title: const Text('空间设置'), - ), + resizeToAvoidBottomInset: false, + appBar: AppBar(title: const Text('空间设置')), body: Obx(() => _buildBody(theme, _controller.loadingState.value)), ); } @@ -42,7 +41,7 @@ class _SpaceSettingPageState extends State { ? scrollErrorWidget(onReload: _controller.onReload) : Builder( builder: (context) { - final padding = MediaQuery.paddingOf(context); + final padding = MediaQuery.viewPaddingOf(context); final divider = Divider( height: 1, indent: max(16, padding.left), @@ -83,9 +82,7 @@ class _SpaceSettingPageState extends State { ), dividerL, SliverToBoxAdapter( - child: SizedBox( - height: padding.bottom + 80, - ), + child: SizedBox(height: padding.bottom + 100), ), ], ); diff --git a/lib/pages/sponsor_block/view.dart b/lib/pages/sponsor_block/view.dart index 10f64086f..2103f54bd 100644 --- a/lib/pages/sponsor_block/view.dart +++ b/lib/pages/sponsor_block/view.dart @@ -458,6 +458,7 @@ class _SponsorBlockPageState extends State { ); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('空降助手')), body: CustomScrollView( slivers: [ @@ -491,7 +492,7 @@ class _SponsorBlockPageState extends State { dividerL, SliverToBoxAdapter( child: SizedBox( - height: 55 + MediaQuery.paddingOf(context).bottom, + height: 55 + MediaQuery.viewPaddingOf(context).bottom, ), ), ], diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index b1ba202e1..a410f8258 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -1,5 +1,6 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models_new/sub/sub/list.dart'; import 'package:PiliPlus/pages/subscription/controller.dart'; @@ -21,23 +22,19 @@ class _SubPageState extends State with GridMixin { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('我的订阅')), - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _subController.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - Obx(() => _buildBody(_subController.loadingState.value)), - SliverToBoxAdapter( - child: SizedBox( - height: MediaQuery.paddingOf(context).bottom + 80, - ), + body: refreshIndicator( + onRefresh: _subController.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + ViewSliverSafeArea( + sliver: Obx( + () => _buildBody(_subController.loadingState.value), ), - ], - ), + ), + ], ), ), ); diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index f3f653af6..c03206e9f 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -43,29 +43,28 @@ class _SubDetailPageState extends State with GridMixin { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final padding = MediaQuery.paddingOf(context); - return Scaffold( - body: SafeArea( - top: false, - bottom: false, - child: refreshIndicator( - onRefresh: _subDetailController.onRefresh, - child: CustomScrollView( - controller: _subDetailController.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - _appBar(theme, padding), - SliverPadding( - padding: EdgeInsets.only( - top: 7, - bottom: padding.bottom + 80, - ), - sliver: Obx( - () => _buildBody(_subDetailController.loadingState.value), - ), + final padding = MediaQuery.viewPaddingOf(context); + return Material( + color: theme.colorScheme.surface, + child: refreshIndicator( + onRefresh: _subDetailController.onRefresh, + child: CustomScrollView( + controller: _subDetailController.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + _appBar(theme, padding), + SliverPadding( + padding: EdgeInsets.only( + top: 7, + left: padding.left, + right: padding.right, + bottom: padding.bottom + 100, ), - ], - ), + sliver: Obx( + () => _buildBody(_subDetailController.loadingState.value), + ), + ), + ], ), ), ); @@ -157,7 +156,7 @@ class _SubDetailPageState extends State with GridMixin { ), padding: EdgeInsets.only( top: kToolbarHeight + padding.top + 10, - left: 12, + left: 12 + padding.left, right: 12, bottom: 12, ), diff --git a/lib/pages/video/ai_conclusion/view.dart b/lib/pages/video/ai_conclusion/view.dart index 3a93b72f6..1363ac589 100644 --- a/lib/pages/video/ai_conclusion/view.dart +++ b/lib/pages/video/ai_conclusion/view.dart @@ -91,7 +91,7 @@ class _AiDetailState extends CommonCollapseSlidePageState { padding: EdgeInsets.only( left: 14, right: 14, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: SliverList.builder( itemCount: widget.item.outline!.length, diff --git a/lib/pages/video/introduction/pgc/view.dart b/lib/pages/video/introduction/pgc/view.dart index 90b2f8dad..97e35256d 100644 --- a/lib/pages/video/introduction/pgc/view.dart +++ b/lib/pages/video/introduction/pgc/view.dart @@ -27,6 +27,7 @@ class PgcIntroPage extends StatefulWidget { final Function showEpisodes; final Function showIntroDetail; final double maxWidth; + final bool isLandscape; const PgcIntroPage({ super.key, @@ -35,6 +36,7 @@ class PgcIntroPage extends StatefulWidget { required this.showEpisodes, required this.showIntroDetail, required this.maxWidth, + required this.isLandscape, }); @override @@ -62,7 +64,7 @@ class _PgcIntroPageState extends TripleState super.build(context); final ThemeData theme = Theme.of(context); final item = introController.pgcItem; - final isLandscape = context.isLandscape; + final isLandscape = widget.isLandscape; Widget sliver = SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/video/introduction/pgc/widgets/intro_detail.dart b/lib/pages/video/introduction/pgc/widgets/intro_detail.dart index a3f3c023c..294c69a27 100644 --- a/lib/pages/video/introduction/pgc/widgets/intro_detail.dart +++ b/lib/pages/video/introduction/pgc/widgets/intro_detail.dart @@ -105,7 +105,7 @@ class _IntroDetailState extends CommonCollapseSlidePageState { left: 14, right: 14, top: 14, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: [ Text( diff --git a/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart b/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart index 9685288b6..6214e1402 100644 --- a/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart +++ b/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart @@ -109,8 +109,8 @@ class _PgcPanelState extends State { SizedBox( height: 34, child: TextButton( - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => widget.showEpisodes( null, @@ -134,13 +134,13 @@ class _PgcPanelState extends State { SizedBox( height: 60, child: ListView.builder( + padding: EdgeInsets.zero, controller: listViewScrollCtr, scrollDirection: Axis.horizontal, itemCount: widget.pages.length, itemExtent: 150, - itemBuilder: (BuildContext context, int index) { - return _buildItem(theme, isPugv, index); - }, + itemBuilder: (BuildContext context, int index) => + _buildItem(theme, isPugv, index), ), ), ], diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index 462836cab..c4ea61222 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -17,7 +17,6 @@ import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/season.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/triple_state.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; -import 'package:PiliPlus/utils/context_ext.dart'; import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; @@ -41,12 +40,14 @@ class UgcIntroPanel extends StatefulWidget { required this.showAiBottomSheet, required this.showEpisodes, required this.onShowMemberPage, + required this.isPortrait, required this.isHorizontal, }); final String heroTag; final Function showAiBottomSheet; final Function showEpisodes; final ValueChanged onShowMemberPage; + final bool isPortrait; final bool isHorizontal; @override @@ -77,7 +78,7 @@ class _UgcIntroPanelState extends TripleState fadeCurve: Curves.ease, sizeCurve: Curves.linear, ); - final isPortrait = context.isPortrait; + final isPortrait = widget.isPortrait; final isHorizontal = !isPortrait && widget.isHorizontal; return SliverPadding( padding: const EdgeInsets.only( diff --git a/lib/pages/video/introduction/ugc/widgets/page.dart b/lib/pages/video/introduction/ugc/widgets/page.dart index bc8fd6b85..f6bb1a776 100644 --- a/lib/pages/video/introduction/ugc/widgets/page.dart +++ b/lib/pages/video/introduction/ugc/widgets/page.dart @@ -113,8 +113,8 @@ class _PagesPanelState extends State { SizedBox( height: 34, child: TextButton( - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => widget.showEpisodes!( null, diff --git a/lib/pages/video/medialist/view.dart b/lib/pages/video/medialist/view.dart index 4f13ae2e6..c4c9f734c 100644 --- a/lib/pages/video/medialist/view.dart +++ b/lib/pages/video/medialist/view.dart @@ -127,7 +127,7 @@ class _MediaListPanelState itemCount: widget.mediaList.length, padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), itemBuilder: ((context, index) { if (index == widget.mediaList.length - 1 && diff --git a/lib/pages/video/member/view.dart b/lib/pages/video/member/view.dart index 3926dcec7..b3b1eca38 100644 --- a/lib/pages/video/member/view.dart +++ b/lib/pages/video/member/view.dart @@ -96,9 +96,16 @@ class _HorizontalMemberPageState extends State physics: const AlwaysScrollableScrollPhysics(), slivers: [ _buildSliverHeader(theme), - Obx( - () => - _buildVideoList(theme, _controller.loadingState.value), + SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, + ), + sliver: Obx( + () => _buildVideoList( + theme, + _controller.loadingState.value, + ), + ), ), ], ), @@ -176,34 +183,29 @@ class _HorizontalMemberPageState extends State Loading() => gridSkeleton, Success(:var response) => response?.isNotEmpty == true - ? SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: SliverGrid.builder( - gridDelegate: gridDelegate, - itemBuilder: (context, index) { - if (index == response.length - 1 && _controller.hasNext) { - _controller.onLoadMore(); - } - final SpaceArchiveItem videoItem = response[index]; - return VideoCardHMemberVideo( - videoItem: videoItem, - bvid: _bvid, - onTap: () { - Get.back(); - widget.ugcIntroController.onChangeEpisode( - BaseEpisodeItem( - bvid: videoItem.bvid, - cid: videoItem.cid, - cover: videoItem.cover, - ), - ); - }, - ); - }, - itemCount: response!.length, - ), + ? SliverGrid.builder( + gridDelegate: gridDelegate, + itemBuilder: (context, index) { + if (index == response.length - 1 && _controller.hasNext) { + _controller.onLoadMore(); + } + final SpaceArchiveItem videoItem = response[index]; + return VideoCardHMemberVideo( + videoItem: videoItem, + bvid: _bvid, + onTap: () { + Get.back(); + widget.ugcIntroController.onChangeEpisode( + BaseEpisodeItem( + bvid: videoItem.bvid, + cid: videoItem.cid, + cover: videoItem.cover, + ), + ); + }, + ); + }, + itemCount: response!.length, ) : HttpError(onReload: _controller.onReload), Error(:var errMsg) => HttpError( diff --git a/lib/pages/video/note/view.dart b/lib/pages/video/note/view.dart index f0d12ba02..309585238 100644 --- a/lib/pages/video/note/view.dart +++ b/lib/pages/video/note/view.dart @@ -105,7 +105,7 @@ class _NoteListPageState extends CommonSlidePageState { physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( - padding: const EdgeInsets.only(bottom: 80), + padding: const EdgeInsets.only(bottom: 100), sliver: Obx( () => _buildBody(theme, _controller.loadingState.value), ), @@ -118,7 +118,7 @@ class _NoteListPageState extends CommonSlidePageState { left: 12, right: 12, top: 6, - bottom: MediaQuery.paddingOf(context).bottom + 6, + bottom: MediaQuery.viewPaddingOf(context).bottom + 6, ), width: double.infinity, decoration: BoxDecoration( diff --git a/lib/pages/video/pay_coins/view.dart b/lib/pages/video/pay_coins/view.dart index ccaf40446..cce9753bb 100644 --- a/lib/pages/video/pay_coins/view.dart +++ b/lib/pages/video/pay_coins/view.dart @@ -428,7 +428,8 @@ class _PayCoinsPageState extends State ], ), SizedBox( - height: (isV ? 50 : 10) + MediaQuery.paddingOf(context).bottom, + height: + (isV ? 50 : 10) + MediaQuery.viewPaddingOf(context).bottom, ), ], ), diff --git a/lib/pages/video/post_panel/view.dart b/lib/pages/video/post_panel/view.dart index 33c6d3090..5697e1477 100644 --- a/lib/pages/video/post_panel/view.dart +++ b/lib/pages/video/post_panel/view.dart @@ -107,7 +107,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { if (list.isNullOrEmpty) { return errorWidget(); } - final bottom = MediaQuery.paddingOf(context).bottom; + final bottom = MediaQuery.viewPaddingOf(context).bottom; return Stack( clipBehavior: Clip.none, children: [ diff --git a/lib/pages/video/related/view.dart b/lib/pages/video/related/view.dart index cd278ea1a..c11fb5f51 100644 --- a/lib/pages/video/related/view.dart +++ b/lib/pages/video/related/view.dart @@ -28,10 +28,7 @@ class _RelatedVideoPanelState extends State Widget build(BuildContext context) { super.build(context); return SliverPadding( - padding: const EdgeInsets.only( - top: 7, - bottom: 80, - ), + padding: const EdgeInsets.only(top: 7, bottom: 100), sliver: Obx(() => _buildBody(_relatedController.loadingState.value)), ); } diff --git a/lib/pages/video/reply/view.dart b/lib/pages/video/reply/view.dart index 34b0de1e2..0a72a9c76 100644 --- a/lib/pages/video/reply/view.dart +++ b/lib/pages/video/reply/view.dart @@ -87,11 +87,13 @@ class _VideoReplyPanelState extends State } } + late double bottom; + @override Widget build(BuildContext context) { super.build(context); + bottom = MediaQuery.viewPaddingOf(context).bottom; final theme = Theme.of(context); - final bottom = MediaQuery.paddingOf(context).bottom; return refreshIndicator( onRefresh: _videoReplyController.onRefresh, child: Stack( @@ -154,15 +156,14 @@ class _VideoReplyPanelState extends State Obx( () => _buildBody( theme, - bottom, _videoReplyController.loadingState.value, ), ), ], ), Positioned( - bottom: bottom + 14, right: 14, + bottom: 14 + bottom, child: SlideTransition( position: _videoReplyController.anim, child: FloatingActionButton( @@ -185,7 +186,7 @@ class _VideoReplyPanelState extends State ); } - Widget _buildBody(ThemeData theme, double bottom, LoadingState loadingState) { + Widget _buildBody(ThemeData theme, LoadingState loadingState) { return switch (loadingState) { Loading() => SliverList.builder( itemBuilder: (context, index) => const VideoReplySkeleton(), @@ -198,11 +199,12 @@ class _VideoReplyPanelState extends State if (index == response.length) { _videoReplyController.onLoadMore(); return Container( + height: 125, alignment: Alignment.center, - padding: EdgeInsets.only(bottom: bottom), - height: bottom + 100, + margin: EdgeInsets.only(bottom: bottom), child: Text( _videoReplyController.isEnd ? '没有更多了' : '加载中...', + textAlign: TextAlign.center, style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index 039f1c834..7a37b6795 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -850,7 +850,7 @@ class ReplyItemGrpc extends StatelessWidget { return Padding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 20, + bottom: MediaQuery.viewPaddingOf(context).bottom + 20, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/video/reply_new/view.dart b/lib/pages/video/reply_new/view.dart index 0a33b2e6d..4d50c4e05 100644 --- a/lib/pages/video/reply_new/view.dart +++ b/lib/pages/video/reply_new/view.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/common/widgets/button/toolbar_icon_button.dart'; import 'package:PiliPlus/common/widgets/text_field/controller.dart' show RichTextType; import 'package:PiliPlus/common/widgets/text_field/text_field.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo; import 'package:PiliPlus/http/video.dart'; @@ -75,8 +76,7 @@ class _ReplyPageState extends CommonRichTextPubPageState { @override Widget build(BuildContext context) { - Widget child = SafeArea( - bottom: false, + Widget child = ViewSafeArea( child: Align( alignment: Alignment.bottomCenter, child: Container( @@ -93,7 +93,9 @@ class _ReplyPageState extends CommonRichTextPubPageState { children: [ ...buildInputView(), buildImagePreview(), - buildPanelContainer(themeData, Colors.transparent), + Flexible( + child: buildPanelContainer(themeData, Colors.transparent), + ), ], ), ), diff --git a/lib/pages/video/reply_reply/view.dart b/lib/pages/video/reply_reply/view.dart index a47ce8841..a9b065702 100644 --- a/lib/pages/video/reply_reply/view.dart +++ b/lib/pages/video/reply_reply/view.dart @@ -87,7 +87,8 @@ class _VideoReplyReplyPanelState } @override - Widget buildPage(ThemeData theme) { + void didChangeDependencies() { + super.didChangeDependencies(); _imageCallback = _horizontalPreview ? (imgList, index) => PageUtils.onHorizontalPreview( _key, @@ -96,6 +97,10 @@ class _VideoReplyReplyPanelState index, ) : null; + } + + @override + Widget buildPage(ThemeData theme) { return Scaffold( key: _key, resizeToAvoidBottomInset: false, @@ -305,13 +310,14 @@ class _VideoReplyReplyPanelState if (index == response!.length) { _controller.onLoadMore(); return Container( + height: 125, alignment: Alignment.center, margin: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom, + bottom: MediaQuery.viewPaddingOf(context).bottom, ), - height: 125, child: Text( _controller.isEnd ? '没有更多了' : '加载中...', + textAlign: TextAlign.center, style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, diff --git a/lib/pages/video/reply_search_item/child/view.dart b/lib/pages/video/reply_search_item/child/view.dart index c1cdb13c2..b7ba20288 100644 --- a/lib/pages/video/reply_search_item/child/view.dart +++ b/lib/pages/video/reply_search_item/child/view.dart @@ -40,7 +40,7 @@ class _ReplySearchChildPageState extends State SliverPadding( padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), diff --git a/lib/pages/video/reply_search_item/view.dart b/lib/pages/video/reply_search_item/view.dart index 4aa648857..fd6933ad4 100644 --- a/lib/pages/video/reply_search_item/view.dart +++ b/lib/pages/video/reply_search_item/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/models/common/reply/reply_search_type.dart'; import 'package:PiliPlus/pages/video/reply_search_item/child/view.dart'; import 'package:PiliPlus/pages/video/reply_search_item/controller.dart'; @@ -29,6 +30,7 @@ class _ReplySearchPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( actions: [ IconButton( @@ -56,9 +58,7 @@ class _ReplySearchPageState extends State { onSubmitted: (value) => _controller.submit(), ), ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: Column( children: [ TabBar( diff --git a/lib/pages/video/send_danmaku/view.dart b/lib/pages/video/send_danmaku/view.dart index 10e86572a..e88776917 100644 --- a/lib/pages/video/send_danmaku/view.dart +++ b/lib/pages/video/send_danmaku/view.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:PiliPlus/common/widgets/button/icon_button.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/danmaku.dart'; import 'package:PiliPlus/main.dart'; import 'package:PiliPlus/models/common/publish_panel_type.dart'; @@ -145,8 +146,7 @@ class _SendDanmakuPanelState extends CommonTextPubPageState { @override Widget build(BuildContext context) { - Widget child = SafeArea( - bottom: false, + Widget child = ViewSafeArea( child: Align( alignment: Alignment.bottomCenter, child: Container( @@ -216,7 +216,7 @@ class _SendDanmakuPanelState extends CommonTextPubPageState { _buildColorPanel, ], ), - SizedBox(height: 12 + MediaQuery.paddingOf(context).bottom), + SizedBox(height: 12 + MediaQuery.viewPaddingOf(context).bottom), ], ), ), diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index 227efbd43..fbd55870f 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -94,8 +94,6 @@ class _VideoDetailPageVState extends State videoDetailController.plPlayerController.enableVerticalExpand; bool get pipNoDanmaku => videoDetailController.plPlayerController.pipNoDanmaku; - bool get removeSafeArea => - videoDetailController.plPlayerController.removeSafeArea; bool isShowing = true; bool get isFullScreen => @@ -114,8 +112,6 @@ class _VideoDetailPageVState extends State bool get _horizontalPreview => !isPortrait && videoDetailController.plPlayerController.horizontalPreview; - StreamSubscription? _listenerFS; - final GlobalKey relatedVideoPanelKey = GlobalKey(); final GlobalKey videoPlayerKey = GlobalKey(); final GlobalKey playerKey = GlobalKey(); @@ -146,7 +142,6 @@ class _VideoDetailPageVState extends State pgcIntroController = Get.put(PgcIntroController(), tag: heroTag); } - if (removeSafeArea) hideStatusBar(); videoSourceInit(); autoScreen(); @@ -159,12 +154,6 @@ class _VideoDetailPageVState extends State videoDetailController.animationController.addListener(animListener); - _listenerFS = videoDetailController.plPlayerController.isFullScreen.listen(( - value, - ) { - refreshPage(); - }); - WidgetsBinding.instance.addObserver(this); } @@ -331,8 +320,6 @@ class _VideoDetailPageVState extends State @override void dispose() { - _listenerFS?.cancel(); - videoDetailController ..skipTimer?.cancel() ..skipTimer = null; @@ -471,6 +458,8 @@ class _VideoDetailPageVState extends State plPlayerController?.addPositionListener(positionListener); } + Function(List imgList, int index)? _imageCallback; + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -478,9 +467,21 @@ class _VideoDetailPageVState extends State this, ModalRoute.of(context)! as PageRoute, ); + final size = MediaQuery.sizeOf(context); + maxWidth = size.width; + maxHeight = size.height; + isPortrait = maxHeight >= maxWidth; themeData = videoDetailController.plPlayerController.darkVideoPage ? MyApp.darkThemeData ?? Theme.of(context) : Theme.of(context); + _imageCallback = _horizontalPreview + ? (imgList, index) => PageUtils.onHorizontalPreview( + videoDetailController.childKey, + this, + imgList, + index, + ) + : null; } void animListener() { @@ -537,510 +538,414 @@ class _VideoDetailPageVState extends State } Widget get childWhenDisabled { - final isFullScreen = this.isFullScreen; - final useSafeArea = !removeSafeArea && isPortrait && isFullScreen; - return SafeArea( - top: useSafeArea, - bottom: useSafeArea, - left: false, - right: false, - child: Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - appBar: (removeSafeArea || isFullScreen) - ? null - : PreferredSize( - preferredSize: const Size.fromHeight(0), - child: Obx( - () { - final scrollRatio = videoDetailController.scrollRatio.value; - bool shouldShow = - scrollRatio != 0 && - videoDetailController.scrollCtr.offset != 0 && - isPortrait; - return Stack( - clipBehavior: Clip.none, - children: [ - AppBar( - backgroundColor: Colors.black, - toolbarHeight: 0, - systemOverlayStyle: Platform.isAndroid - ? shouldShow - ? null - : SystemUiOverlayStyle( - statusBarIconBrightness: - Brightness.light, - systemNavigationBarIconBrightness: - themeData.brightness.reverse, - ) - : null, - ), - if (shouldShow) + if (mounted && isShowing && !isFullScreen) { + if (isPortrait) { + if (!videoDetailController.imageStatus) { + showStatusBar(); + } + } else if (!videoDetailController.horizontalScreen) { + hideStatusBar(); + } + } + return Obx( + () { + final isFullScreen = this.isFullScreen; + return Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + appBar: isFullScreen + ? null + : PreferredSize( + preferredSize: const Size.fromHeight(0), + child: Obx( + () { + final scrollRatio = + videoDetailController.scrollRatio.value; + bool shouldShow = + scrollRatio != 0 && + videoDetailController.scrollCtr.offset != 0 && + isPortrait; + return Stack( + clipBehavior: Clip.none, + children: [ AppBar( - backgroundColor: themeData.colorScheme.surface - .withValues(alpha: scrollRatio), + backgroundColor: Colors.black, toolbarHeight: 0, systemOverlayStyle: Platform.isAndroid - ? SystemUiOverlayStyle( - statusBarIconBrightness: - themeData.brightness.reverse, - systemNavigationBarIconBrightness: - themeData.brightness.reverse, - ) + ? shouldShow + ? null + : SystemUiOverlayStyle( + statusBarIconBrightness: + Brightness.light, + systemNavigationBarIconBrightness: + themeData.brightness.reverse, + ) : null, ), - ], - ); - }, + if (shouldShow) + AppBar( + backgroundColor: themeData.colorScheme.surface + .withValues(alpha: scrollRatio), + toolbarHeight: 0, + systemOverlayStyle: Platform.isAndroid + ? SystemUiOverlayStyle( + statusBarIconBrightness: + themeData.brightness.reverse, + systemNavigationBarIconBrightness: + themeData.brightness.reverse, + ) + : null, + ), + ], + ); + }, + ), ), - ), - body: ExtendedNestedScrollView( - key: videoDetailController.scrollKey, - physics: const NeverScrollableScrollPhysics( - parent: ClampingScrollPhysics(), - ), - controller: videoDetailController.scrollCtr, - onlyOneScrollInBody: true, - pinnedHeaderSliverHeightBuilder: () { - double pinnedHeight = this.isFullScreen || !isPortrait - ? maxHeight - : videoDetailController.isExpanding || - videoDetailController.isCollapsing - ? animHeight - : videoDetailController.isCollapsing || - plPlayerController?.playerStatus.status.value == - PlayerStatus.playing - ? videoDetailController.minVideoHeight - : kToolbarHeight; - if (videoDetailController.isExpanding && - videoDetailController.animationController.value == 1) { - videoDetailController.isExpanding = false; - WidgetsBinding.instance.addPostFrameCallback((_) { - videoDetailController.scrollRatio.value = 0; - refreshPage(); - }); - } else if (videoDetailController.isCollapsing && - videoDetailController.animationController.value == 1) { - videoDetailController.isCollapsing = false; - WidgetsBinding.instance.addPostFrameCallback((_) { - refreshPage(); - }); - } - return pinnedHeight; - }, - headerSliverBuilder: (context, innerBoxIsScrolled) { - final isFullScreen = this.isFullScreen; - return [ - SliverAppBar( - elevation: 0, - scrolledUnderElevation: 0, - primary: false, - automaticallyImplyLeading: false, - pinned: true, - expandedHeight: isFullScreen || !isPortrait - ? maxHeight - : videoDetailController.isExpanding || - videoDetailController.isCollapsing - ? animHeight - : videoDetailController.videoHeight, - flexibleSpace: Stack( - clipBehavior: Clip.none, - children: [ - Builder( - builder: (context) { - if (!isPortrait && - !videoDetailController.horizontalScreen && - !isFullScreen && - isShowing && - mounted) { - hideStatusBar(); - } - if (isPortrait && - !isFullScreen && - isShowing && - mounted) { - if (!videoDetailController.imageStatus && - !removeSafeArea) { - showStatusBar(); - } - } - final height = !isPortrait || isFullScreen - ? maxHeight - - (!isPortrait || removeSafeArea - ? 0 - : padding.top) - : videoDetailController.isExpanding || - videoDetailController.isCollapsing - ? animHeight - : videoDetailController.videoHeight; - return SizedBox( + body: ExtendedNestedScrollView( + key: videoDetailController.scrollKey, + physics: const NeverScrollableScrollPhysics( + parent: ClampingScrollPhysics(), + ), + controller: videoDetailController.scrollCtr, + onlyOneScrollInBody: true, + pinnedHeaderSliverHeightBuilder: () { + double pinnedHeight = this.isFullScreen || !isPortrait + ? maxHeight + : videoDetailController.isExpanding || + videoDetailController.isCollapsing + ? animHeight + : videoDetailController.isCollapsing || + plPlayerController?.playerStatus.status.value == + PlayerStatus.playing + ? videoDetailController.minVideoHeight + : kToolbarHeight; + if (videoDetailController.isExpanding && + videoDetailController.animationController.value == 1) { + videoDetailController.isExpanding = false; + WidgetsBinding.instance.addPostFrameCallback((_) { + videoDetailController.scrollRatio.value = 0; + refreshPage(); + }); + } else if (videoDetailController.isCollapsing && + videoDetailController.animationController.value == 1) { + videoDetailController.isCollapsing = false; + WidgetsBinding.instance.addPostFrameCallback((_) { + refreshPage(); + }); + } + return pinnedHeight; + }, + headerSliverBuilder: (context, innerBoxIsScrolled) { + final isFullScreen = this.isFullScreen; + final height = !isPortrait || isFullScreen + ? maxHeight + : videoDetailController.isExpanding || + videoDetailController.isCollapsing + ? animHeight + : videoDetailController.videoHeight; + return [ + SliverAppBar( + elevation: 0, + scrolledUnderElevation: 0, + primary: false, + automaticallyImplyLeading: false, + pinned: true, + expandedHeight: isFullScreen || !isPortrait + ? maxHeight + : videoDetailController.isExpanding || + videoDetailController.isCollapsing + ? animHeight + : videoDetailController.videoHeight, + flexibleSpace: Stack( + clipBehavior: Clip.none, + children: [ + SizedBox( + width: maxWidth, + height: height, + child: videoPlayer( width: maxWidth, height: height, - child: videoPlayer( - width: maxWidth, - height: height, - ), - ); - }, - ), - Obx( - () { - Widget toolbar() => Opacity( - opacity: videoDetailController.scrollRatio.value, - child: Container( - color: themeData.colorScheme.surface, - alignment: Alignment.topCenter, - child: SizedBox( - height: kToolbarHeight, - child: Stack( - clipBehavior: Clip.none, - children: [ - Align( - alignment: Alignment.centerLeft, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '返回', - icon: Icon( - FontAwesomeIcons.arrowLeft, - size: 15, - color: themeData - .colorScheme - .onSurface, - ), - onPressed: Get.back, - ), - ), - SizedBox( - width: 42, - height: 34, - child: IconButton( - tooltip: '返回主页', - icon: Icon( - FontAwesomeIcons.house, - size: 15, - color: themeData - .colorScheme - .onSurface, - ), - onPressed: () { - videoDetailController - .plPlayerController - .backToHome = - true; - Get.until( - (route) => route.isFirst, - ); - }, - ), - ), - ], - ), - ), - Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.play_arrow_rounded, - color: themeData.colorScheme.primary, - ), - Text( - '${videoDetailController.playedTime == null - ? '立即' - : plPlayerController!.playerStatus.status.value == PlayerStatus.completed - ? '重新' - : '继续'}播放', - style: TextStyle( - color: - themeData.colorScheme.primary, - ), - ), - ], - ), - ), - Align( - alignment: Alignment.centerRight, - child: - videoDetailController.playedTime == null - ? PopupMenuButton( - icon: Icon( - size: 22, - Icons.more_vert, - color: themeData - .colorScheme - .onSurface, - ), - onSelected: (String type) { - switch (type) { - case 'later': - introController.viewLater(); - break; - case 'report': - if (!Accounts.main.isLogin) { - SmartDialog.showToast( - '账号未登录', - ); - } else { - PageUtils.reportVideo( - videoDetailController.aid, - ); - } - break; - case 'note': - videoDetailController - .showNoteList(context); - break; - case 'savePic': - ImageUtil.downloadImg( - context, - [ - videoDetailController - .cover - .value, - ], - ); - break; - } - }, - itemBuilder: - ( - BuildContext context, - ) => >[ - const PopupMenuItem( - value: 'later', - child: Text('稍后再看'), - ), - if (videoDetailController - .epId == - null) - const PopupMenuItem( - value: 'note', - child: Text('查看笔记'), - ), - if (videoDetailController - .cover - .value - .isNotEmpty) - const PopupMenuItem( - value: 'savePic', - child: Text('保存封面'), - ), - const PopupMenuItem( - value: 'report', - child: Text('举报'), - ), - ], - ) - : SizedBox( + ), + ), + Obx( + () { + Widget toolbar() => Opacity( + opacity: videoDetailController.scrollRatio.value, + child: Container( + color: themeData.colorScheme.surface, + alignment: Alignment.topCenter, + child: SizedBox( + height: kToolbarHeight, + child: Stack( + clipBehavior: Clip.none, + children: [ + Align( + alignment: Alignment.centerLeft, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( width: 42, height: 34, child: IconButton( - tooltip: "更多设置", - style: ButtonStyle( - padding: - WidgetStateProperty.all( - EdgeInsets.zero, - ), - ), - onPressed: () => - videoDetailController - .headerCtrKey - .currentState - ?.showSettingSheet(), + tooltip: '返回', icon: Icon( - Icons.more_vert_outlined, - size: 19, + FontAwesomeIcons.arrowLeft, + size: 15, color: themeData .colorScheme .onSurface, ), + onPressed: Get.back, ), ), - ), - ], + SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: '返回主页', + icon: Icon( + FontAwesomeIcons.house, + size: 15, + color: themeData + .colorScheme + .onSurface, + ), + onPressed: () { + videoDetailController + .plPlayerController + .backToHome = + true; + Get.until( + (route) => route.isFirst, + ); + }, + ), + ), + ], + ), + ), + Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.play_arrow_rounded, + color: + themeData.colorScheme.primary, + ), + Text( + '${videoDetailController.playedTime == null + ? '立即' + : plPlayerController!.playerStatus.status.value == PlayerStatus.completed + ? '重新' + : '继续'}播放', + style: TextStyle( + color: + themeData.colorScheme.primary, + ), + ), + ], + ), + ), + Align( + alignment: Alignment.centerRight, + child: + videoDetailController.playedTime == + null + ? PopupMenuButton( + icon: Icon( + size: 22, + Icons.more_vert, + color: themeData + .colorScheme + .onSurface, + ), + onSelected: (String type) { + switch (type) { + case 'later': + introController.viewLater(); + break; + case 'report': + if (!Accounts + .main + .isLogin) { + SmartDialog.showToast( + '账号未登录', + ); + } else { + PageUtils.reportVideo( + videoDetailController + .aid, + ); + } + break; + case 'note': + videoDetailController + .showNoteList( + context, + ); + break; + case 'savePic': + ImageUtil.downloadImg( + context, + [ + videoDetailController + .cover + .value, + ], + ); + break; + } + }, + itemBuilder: + ( + BuildContext context, + ) => >[ + const PopupMenuItem( + value: 'later', + child: Text( + '稍后再看', + ), + ), + if (videoDetailController + .epId == + null) + const PopupMenuItem< + String + >( + value: 'note', + child: Text( + '查看笔记', + ), + ), + if (videoDetailController + .cover + .value + .isNotEmpty) + const PopupMenuItem< + String + >( + value: 'savePic', + child: Text( + '保存封面', + ), + ), + const PopupMenuItem( + value: 'report', + child: Text('举报'), + ), + ], + ) + : SizedBox( + width: 42, + height: 34, + child: IconButton( + tooltip: "更多设置", + style: const ButtonStyle( + padding: + WidgetStatePropertyAll( + EdgeInsets.zero, + ), + ), + onPressed: () => + videoDetailController + .headerCtrKey + .currentState + ?.showSettingSheet(), + icon: Icon( + Icons.more_vert_outlined, + size: 19, + color: themeData + .colorScheme + .onSurface, + ), + ), + ), + ), + ], + ), ), ), - ), - ); - return videoDetailController.scrollRatio.value == 0 || - videoDetailController.scrollCtr.offset == 0 || - !isPortrait - ? const SizedBox.shrink() - : Positioned.fill( - bottom: -2, - child: GestureDetector( - onTap: () async { - if (videoDetailController.isQuerying) { - if (kDebugMode) { - debugPrint('handlePlay: querying'); + ); + return videoDetailController.scrollRatio.value == 0 || + videoDetailController.scrollCtr.offset == 0 || + !isPortrait + ? const SizedBox.shrink() + : Positioned.fill( + bottom: -2, + child: GestureDetector( + onTap: () async { + if (videoDetailController.isQuerying) { + if (kDebugMode) { + debugPrint( + 'handlePlay: querying', + ); + } + return; } - return; - } - if (videoDetailController.videoUrl == - null || - videoDetailController.audioUrl == - null) { - if (kDebugMode) { - debugPrint( - 'handlePlay: videoUrl/audioUrl not initialized', - ); + if (videoDetailController.videoUrl == + null || + videoDetailController.audioUrl == + null) { + if (kDebugMode) { + debugPrint( + 'handlePlay: videoUrl/audioUrl not initialized', + ); + } + videoDetailController.queryVideoUrl(); + return; } - videoDetailController.queryVideoUrl(); - return; - } - videoDetailController.scrollRatio.value = 0; - if (plPlayerController == null || - videoDetailController.playedTime == - null) { - handlePlay(); - } else { - if (plPlayerController! - .videoPlayerController! - .state - .completed) { - await plPlayerController! - .videoPlayerController! - .seek(Duration.zero); - plPlayerController! - .videoPlayerController! - .play(); + videoDetailController.scrollRatio.value = + 0; + if (plPlayerController == null || + videoDetailController.playedTime == + null) { + handlePlay(); } else { - plPlayerController! + if (plPlayerController! .videoPlayerController! - .playOrPause(); + .state + .completed) { + await plPlayerController! + .videoPlayerController! + .seek(Duration.zero); + plPlayerController! + .videoPlayerController! + .play(); + } else { + plPlayerController! + .videoPlayerController! + .playOrPause(); + } } - } - }, - behavior: HitTestBehavior.opaque, - child: toolbar(), - ), - ); - }, - ), - ], - ), - ), - ]; - }, - body: Scaffold( - key: videoDetailController.childKey, - resizeToAvoidBottomInset: false, - backgroundColor: Colors.transparent, - body: Column( - children: [ - buildTabbar(onTap: videoDetailController.animToTop), - Expanded( - child: videoTabBarView( - controller: videoDetailController.tabCtr, - children: [ - videoIntro(isHorizontal: false, needCtr: false), - if (videoDetailController.showReply) - videoReplyPanel(false), - if (_shouldShowSeasonPanel) seasonPanel, + }, + behavior: HitTestBehavior.opaque, + child: toolbar(), + ), + ); + }, + ), ], ), ), - ], - ), - ), - ), - ), - ); - } - - Widget get childWhenDisabledAlmostSquareInner => Obx( - () { - final isFullScreen = this.isFullScreen; - if (videoDetailController.isVertical.value && enableVerticalExpand) { - final double videoHeight = - maxHeight - (removeSafeArea ? 0 : padding.vertical); - final double width = videoHeight * 9 / 16; - final videoWidth = isFullScreen ? maxWidth : width; - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: videoWidth, - height: videoHeight, - child: videoPlayer( - width: videoWidth, - height: videoHeight, - ), - ), - Expanded( - child: Scaffold( - key: videoDetailController.childKey, - resizeToAvoidBottomInset: false, - backgroundColor: Colors.transparent, - body: Column( - children: [ - buildTabbar(), - Expanded( - child: videoTabBarView( - controller: videoDetailController.tabCtr, - children: [ - videoIntro( - width: maxWidth - width, - height: maxHeight, - ), - if (videoDetailController.showReply) - videoReplyPanel(), - if (_shouldShowSeasonPanel) seasonPanel, - ], - ), - ), - ], - ), - ), - ), - ], - ); - } - final shouldShowSeasonPanel = _shouldShowSeasonPanel; - final double height = maxHeight / 2.5; - final videoHeight = isFullScreen - ? maxHeight - (removeSafeArea ? 0 : padding.vertical) - : height; - return Column( - children: [ - SizedBox( - width: maxWidth, - height: videoHeight, - child: videoPlayer( - width: maxWidth, - height: videoHeight, - ), - ), - Expanded( - child: Scaffold( + ]; + }, + body: Scaffold( key: videoDetailController.childKey, resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, body: Column( children: [ - buildTabbar(needIndicator: false), + buildTabbar(onTap: videoDetailController.animToTop), Expanded( - child: Row( + child: videoTabBarView( + controller: videoDetailController.tabCtr, children: [ - Expanded( - child: videoIntro( - width: () { - double flex = 1; - if (videoDetailController.showReply) flex++; - if (shouldShowSeasonPanel) flex++; - return maxWidth / flex; - }(), - height: maxHeight - height, - ), - ), + videoIntro(isHorizontal: false, needCtr: false), if (videoDetailController.showReply) - Expanded(child: videoReplyPanel()), - if (shouldShowSeasonPanel) Expanded(child: seasonPanel), + videoReplyPanel(false), + if (_shouldShowSeasonPanel) seasonPanel, ], ), ), @@ -1048,37 +953,71 @@ class _VideoDetailPageVState extends State ), ), ), - ], + ); + }, + ); + } + + Widget get childWhenDisabledLandscape => Obx( + () { + final isFullScreen = this.isFullScreen; + final padding = MediaQuery.viewPaddingOf(context); + return Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + appBar: isFullScreen + ? null + : AppBar(backgroundColor: Colors.black, toolbarHeight: 0), + extendBodyBehindAppBar: true, + body: Padding( + padding: !isFullScreen + ? padding.copyWith(bottom: 0) + : EdgeInsets.zero, + child: childWhenDisabledLandscapeInner(isFullScreen, padding), + ), ); }, ); - Widget get childWhenDisabledLandscapeInner => Obx( - () { - final isFullScreen = this.isFullScreen; - if (videoDetailController.isVertical.value && enableVerticalExpand) { - final double videoHeight = - maxHeight - (removeSafeArea ? 0 : padding.top); - final double width = videoHeight * 9 / 16; - final videoWidth = isFullScreen ? maxWidth : width; - return Row( - children: [ - if (!isFullScreen) - Expanded( - child: videoIntro( - width: (maxWidth - width) / 2, - height: maxHeight, - ), - ), - SizedBox( - width: videoWidth, - height: videoHeight, - child: videoPlayer( - width: videoWidth, - height: videoHeight, + Widget childWhenDisabledLandscapeInner( + bool isFullScreen, + EdgeInsets padding, + ) => Obx(() { + if (videoDetailController.isVertical.value && + enableVerticalExpand && + !isPortrait) { + final double videoHeight = maxHeight - padding.vertical; + final double width = videoHeight * 9 / 16; + final videoWidth = isFullScreen ? maxWidth : width; + final introWidth = (maxWidth - padding.horizontal - width) / 2; + final introHeight = maxHeight - padding.top; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Offstage( + offstage: isFullScreen, + child: SizedBox( + width: introWidth, + height: introHeight, + child: videoIntro( + width: introWidth, + height: introHeight, ), ), - Expanded( + ), + SizedBox( + width: videoWidth, + height: videoHeight, + child: videoPlayer( + width: videoWidth, + height: videoHeight, + ), + ), + Offstage( + offstage: isFullScreen, + child: SizedBox( + width: introWidth, + height: introHeight, child: Scaffold( key: videoDetailController.childKey, resizeToAvoidBottomInset: false, @@ -1100,86 +1039,221 @@ class _VideoDetailPageVState extends State ), ), ), - ], - ); - } - double width = - clampDouble(maxHeight / maxWidth * 1.08, 0.5, 0.7) * maxWidth; - if (maxWidth >= 560) { - width = min(width, maxWidth - 280); - } - final videoWidth = isFullScreen ? maxWidth : width; - final double height = width * 9 / 16; - final videoHeight = isFullScreen ? maxHeight : height; - final introHeight = - maxHeight - height - (removeSafeArea ? 0 : padding.top); - return Row( - children: [ - Column( - children: [ - SizedBox( + ), + ], + ); + } + double width = + clampDouble(maxHeight / maxWidth * 1.08, 0.5, 0.7) * maxWidth; + if (maxWidth >= 560) { + width = min(width, maxWidth - 280); + } + final videoWidth = isFullScreen ? maxWidth : width; + final double height = width * 9 / 16; + final videoHeight = isFullScreen ? maxHeight : height; + final introHeight = maxHeight - height - padding.top; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: videoWidth, + height: videoHeight, + child: videoPlayer( width: videoWidth, height: videoHeight, - child: videoPlayer( - width: videoWidth, - height: videoHeight, - ), ), - Offstage( - offstage: isFullScreen, - child: SizedBox( + ), + Offstage( + offstage: isFullScreen, + child: SizedBox( + width: width, + height: introHeight, + child: videoIntro( width: width, height: introHeight, - child: videoIntro( - width: width, - height: introHeight, - needRelated: false, - needCtr: false, + needRelated: false, + needCtr: false, + ), + ), + ), + ], + ), + Offstage( + offstage: isFullScreen, + child: SizedBox( + width: maxWidth - width - padding.horizontal, + height: maxHeight - padding.top, + child: Scaffold( + key: videoDetailController.childKey, + resizeToAvoidBottomInset: false, + backgroundColor: Colors.transparent, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildTabbar( + introText: '相关视频', + showIntro: + videoDetailController.isUgc && + videoDetailController + .plPlayerController + .showRelatedVideo, + ), + Expanded( + child: videoTabBarView( + controller: videoDetailController.tabCtr, + children: [ + if (videoDetailController.isUgc && + videoDetailController + .plPlayerController + .showRelatedVideo) + CustomScrollView( + controller: introScrollController, + slivers: [ + RelatedVideoPanel( + key: relatedVideoPanelKey, + heroTag: heroTag, + ), + ], + ), + if (videoDetailController.showReply) videoReplyPanel(), + if (_shouldShowSeasonPanel) seasonPanel, + ], + ), + ), + ], + ), + ), + ), + ), + ], + ); + }); + + Widget get childWhenDisabledAlmostSquare => Obx(() { + final isFullScreen = this.isFullScreen; + final padding = MediaQuery.viewPaddingOf(context); + return Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + appBar: isFullScreen + ? null + : AppBar(backgroundColor: Colors.black, toolbarHeight: 0), + extendBodyBehindAppBar: true, + body: Padding( + padding: !isFullScreen ? padding.copyWith(bottom: 0) : EdgeInsets.zero, + child: childWhenDisabledAlmostSquareInner(isFullScreen, padding), + ), + ); + }); + + Widget childWhenDisabledAlmostSquareInner( + bool isFullScreen, + EdgeInsets padding, + ) => Obx( + () { + final isFullScreen = this.isFullScreen; + if (videoDetailController.isVertical.value && + enableVerticalExpand && + !isPortrait) { + final double videoHeight = maxHeight - padding.vertical; + final double width = videoHeight * 9 / 16; + final videoWidth = isFullScreen ? maxWidth : width; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: videoWidth, + height: videoHeight, + child: videoPlayer( + width: videoWidth, + height: videoHeight, + ), + ), + Offstage( + offstage: isFullScreen, + child: SizedBox( + width: maxWidth - width - padding.horizontal, + height: maxHeight - padding.top, + child: Scaffold( + key: videoDetailController.childKey, + resizeToAvoidBottomInset: false, + backgroundColor: Colors.transparent, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildTabbar(), + Expanded( + child: videoTabBarView( + controller: videoDetailController.tabCtr, + children: [ + videoIntro( + width: maxWidth - width, + height: maxHeight, + ), + if (videoDetailController.showReply) + videoReplyPanel(), + if (_shouldShowSeasonPanel) seasonPanel, + ], + ), + ), + ], ), ), ), - ], + ), + ], + ); + } + final shouldShowSeasonPanel = _shouldShowSeasonPanel; + final double height = maxHeight / 2.5; + final videoHeight = isFullScreen ? maxHeight : height; + final bottomHeight = maxHeight - height - padding.top; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: maxWidth, + height: videoHeight, + child: videoPlayer( + width: maxWidth, + height: videoHeight, + ), ), Offstage( offstage: isFullScreen, child: SizedBox( - width: - maxWidth - width - (removeSafeArea ? 0 : padding.horizontal), - height: maxHeight - (removeSafeArea ? 0 : padding.top), + width: maxWidth - padding.horizontal, + height: bottomHeight, child: Scaffold( key: videoDetailController.childKey, resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, body: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - buildTabbar( - introText: '相关视频', - showIntro: - videoDetailController.isUgc && - videoDetailController - .plPlayerController - .showRelatedVideo, - ), + buildTabbar(needIndicator: false), Expanded( - child: videoTabBarView( - controller: videoDetailController.tabCtr, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (videoDetailController.isUgc && - videoDetailController - .plPlayerController - .showRelatedVideo) - CustomScrollView( - controller: introScrollController, - slivers: [ - RelatedVideoPanel( - key: relatedVideoPanelKey, - heroTag: heroTag, - ), - ], + Expanded( + child: videoIntro( + width: () { + double flex = 1; + if (videoDetailController.showReply) flex++; + if (shouldShowSeasonPanel) flex++; + return maxWidth / flex; + }(), + height: bottomHeight, ), + ), if (videoDetailController.showReply) - videoReplyPanel(), - if (_shouldShowSeasonPanel) seasonPanel, + Expanded(child: videoReplyPanel()), + if (shouldShowSeasonPanel) + Expanded(child: seasonPanel), ], ), ), @@ -1193,53 +1267,6 @@ class _VideoDetailPageVState extends State }, ); - Widget get childWhenDisabledLandscape { - final isFullScreen = this.isFullScreen; - return Stack( - clipBehavior: Clip.none, - children: [ - Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - appBar: (removeSafeArea || isFullScreen) - ? null - : AppBar( - backgroundColor: Colors.black, - toolbarHeight: 0, - ), - body: SafeArea( - left: !removeSafeArea && !isFullScreen, - right: !removeSafeArea && !isFullScreen, - top: !removeSafeArea && !isFullScreen, - bottom: false, - child: childWhenDisabledLandscapeInner, - ), - ), - ], - ); - } - - Widget get childWhenDisabledAlmostSquare { - final isFullScreen = this.isFullScreen; - return Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - appBar: (removeSafeArea || isFullScreen) - ? null - : AppBar( - backgroundColor: Colors.black, - toolbarHeight: 0, - ), - body: SafeArea( - left: !removeSafeArea && !isFullScreen, - right: !removeSafeArea && !isFullScreen, - top: !removeSafeArea && !isFullScreen, - bottom: false, - child: childWhenDisabledAlmostSquareInner, - ), - ); - } - Widget get manualPlayerWidget => Obx(() { if (!videoDetailController.autoPlay.value) { return Stack( @@ -1438,30 +1465,19 @@ class _VideoDetailPageVState extends State late bool isPortrait; late double maxWidth; late double maxHeight; - late EdgeInsets padding; @override Widget build(BuildContext context) { - padding = MediaQuery.paddingOf(context); - Widget child = LayoutBuilder( - builder: (context, constraints) { - maxWidth = constraints.maxWidth; - maxHeight = constraints.maxHeight; - isPortrait = maxHeight >= maxWidth; - - if (!videoDetailController.horizontalScreen) { - return autoChoose(childWhenDisabled); - } - - if (maxWidth > maxHeight * 1.25) { - return autoChoose(childWhenDisabledLandscape); - } - if (maxWidth * (9 / 16) < (2 / 5) * maxHeight) { - return autoChoose(childWhenDisabled); - } - return autoChoose(childWhenDisabledAlmostSquare); - }, - ); + Widget child; + if (!videoDetailController.horizontalScreen) { + child = autoChoose(childWhenDisabled); + } else if (maxWidth > maxHeight * 1.25) { + child = autoChoose(childWhenDisabledLandscape); + } else if (maxWidth * (9 / 16) < (2 / 5) * maxHeight) { + child = autoChoose(childWhenDisabled); + } else { + child = autoChoose(childWhenDisabledAlmostSquare); + } return videoDetailController.plPlayerController.darkVideoPage ? Theme(data: themeData, child: child) : child; @@ -1564,8 +1580,8 @@ class _VideoDetailPageVState extends State SizedBox( height: 32, child: TextButton( - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: videoDetailController.showShootDanmakuSheet, child: Text( @@ -1788,6 +1804,7 @@ class _VideoDetailPageVState extends State bool needRelated = true, bool needCtr = true, }) { + final bottom = MediaQuery.viewPaddingOf(context).bottom; Widget introPanel() => CustomScrollView( key: const PageStorageKey('简介'), controller: needCtr ? introScrollController : null, @@ -1802,6 +1819,7 @@ class _VideoDetailPageVState extends State showAiBottomSheet: showAiBottomSheet, showEpisodes: showEpisodes, onShowMemberPage: onShowMemberPage, + isPortrait: isPortrait, isHorizontal: isHorizontal ?? width! > height! * 1.25, ), if (needRelated && @@ -1818,10 +1836,7 @@ class _VideoDetailPageVState extends State ), ), RelatedVideoPanel(key: relatedVideoPanelKey, heroTag: heroTag), - ] else - SliverToBoxAdapter( - child: SizedBox(height: padding.bottom + StyleString.safeSpace), - ), + ], ] else PgcIntroPage( key: pgcPanelKey, @@ -1830,12 +1845,15 @@ class _VideoDetailPageVState extends State showEpisodes: showEpisodes, showIntroDetail: showIntroDetail, maxWidth: width ?? maxWidth, + isLandscape: !isPortrait, ), SliverToBoxAdapter( child: SizedBox( height: - padding.bottom + - (videoDetailController.isPlayAll && !isPortrait ? 75 : 0), + (videoDetailController.isPlayAll && !isPortrait + ? 80 + : StyleString.safeSpace) + + bottom, ), ), ], @@ -1848,7 +1866,7 @@ class _VideoDetailPageVState extends State Positioned( left: 12, right: 12, - bottom: padding.bottom + 12, + bottom: 12 + bottom, child: Material( type: MaterialType.transparency, child: InkWell( @@ -2000,14 +2018,7 @@ class _VideoDetailPageVState extends State replyReply: replyReply, onViewImage: videoDetailController.onViewImage, onDismissed: videoDetailController.onDismissed, - callback: _horizontalPreview - ? (imgList, index) => PageUtils.onHorizontalPreview( - videoDetailController.childKey, - this, - imgList, - index, - ) - : null, + callback: _imageCallback, ); // 展示二级回复 diff --git a/lib/pages/video/view_point/view.dart b/lib/pages/video/view_point/view.dart index 297b00eca..ab4f66475 100644 --- a/lib/pages/video/view_point/view.dart +++ b/lib/pages/video/view_point/view.dart @@ -43,6 +43,7 @@ class _ViewPointsPageState @override Widget buildPage(ThemeData theme) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( automaticallyImplyLeading: false, titleSpacing: 16, @@ -100,7 +101,7 @@ class _ViewPointsPageState physics: const AlwaysScrollableScrollPhysics(), padding: EdgeInsets.only( top: 7, - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), itemCount: videoDetailController.viewPointList.length, itemBuilder: (context, index) { diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index c754989e4..0863cd2e9 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -2071,8 +2071,8 @@ class HeaderControlState extends TripleState { height: 34, child: IconButton( tooltip: '提交片段', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => videoDetailCtr.onBlock(context), icon: const Stack( @@ -2100,8 +2100,8 @@ class HeaderControlState extends TripleState { height: 34, child: IconButton( tooltip: '片段信息', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => videoDetailCtr.showSBDetail(context), icon: const Icon( @@ -2118,8 +2118,8 @@ class HeaderControlState extends TripleState { height: 34, child: IconButton( tooltip: '发弹幕', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: videoDetailCtr.showShootDanmakuSheet, icon: const Icon( @@ -2138,8 +2138,8 @@ class HeaderControlState extends TripleState { plPlayerController.enableShowDanmaku.value; return IconButton( tooltip: "${enableShowDanmaku ? '关闭' : '开启'}弹幕", - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () { final newVal = !enableShowDanmaku; @@ -2165,8 +2165,8 @@ class HeaderControlState extends TripleState { height: 34, child: IconButton( tooltip: '画中画', - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = await Floating().isPipAvailable; @@ -2256,8 +2256,8 @@ class HeaderControlState extends TripleState { height: 34, child: IconButton( tooltip: "更多设置", - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: showSettingSheet, icon: const Icon( diff --git a/lib/pages/webdav/view.dart b/lib/pages/webdav/view.dart index 5ada4bfff..5780c8ee9 100644 --- a/lib/pages/webdav/view.dart +++ b/lib/pages/webdav/view.dart @@ -36,7 +36,7 @@ class _WebDavSettingPageState extends State { @override Widget build(BuildContext context) { - EdgeInsets padding = MediaQuery.paddingOf(context); + EdgeInsets padding = MediaQuery.viewPaddingOf(context); return Scaffold( resizeToAvoidBottomInset: widget.showAppBar, appBar: !widget.showAppBar diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index f201b754f..16c3ef7e7 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -22,7 +22,9 @@ class _WhisperPageState extends State { @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('消息'), actions: [ @@ -77,11 +79,9 @@ class _WhisperPageState extends State { child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), slivers: [ - _buildTopItems, + _buildTopItems(padding), SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 100, - ), + padding: EdgeInsets.only(bottom: padding.bottom + 100), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), ], @@ -131,11 +131,10 @@ class _WhisperPageState extends State { }; } - Widget get _buildTopItems { + Widget _buildTopItems(EdgeInsets padding) { final ThemeData theme = Theme.of(context); - return SliverSafeArea( - top: false, - bottom: false, + return SliverPadding( + padding: EdgeInsets.only(left: padding.left, right: padding.right), sliver: SliverToBoxAdapter( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, diff --git a/lib/pages/whisper_block/view.dart b/lib/pages/whisper_block/view.dart index 622ae8e1e..77c16feee 100644 --- a/lib/pages/whisper_block/view.dart +++ b/lib/pages/whisper_block/view.dart @@ -98,7 +98,7 @@ class _WhisperBlockPageState extends State { padding: EdgeInsets.only( left: 25, right: 25, - bottom: MediaQuery.paddingOf(context).bottom + 10, + bottom: MediaQuery.viewPaddingOf(context).bottom + 10, ), child: FilledButton.tonal( onPressed: _onAdd, diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 2b3d86c94..92e043dcb 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -44,6 +44,7 @@ class _WhisperDetailPageState @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( @@ -122,9 +123,11 @@ class _WhisperDetailPageState const SizedBox(width: 10), ], ), - body: SafeArea( - top: false, - bottom: false, + body: Padding( + padding: EdgeInsets.only( + left: padding.left, + right: padding.right, + ), child: Column( children: [ Expanded( @@ -146,7 +149,7 @@ class _WhisperDetailPageState _buildInputView(theme), buildPanelContainer(theme, theme.colorScheme.onInverseSurface), ] else - SizedBox(height: MediaQuery.paddingOf(context).bottom), + SizedBox(height: padding.bottom), ], ), ), diff --git a/lib/pages/whisper_link_setting/view.dart b/lib/pages/whisper_link_setting/view.dart index 463768267..dacfb6245 100644 --- a/lib/pages/whisper_link_setting/view.dart +++ b/lib/pages/whisper_link_setting/view.dart @@ -41,10 +41,11 @@ class _WhisperLinkSettingPageState extends State { color: theme.colorScheme.outline.withValues(alpha: 0.1), ); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('聊天设置')), body: ListView( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: [ divider, diff --git a/lib/pages/whisper_secondary/view.dart b/lib/pages/whisper_secondary/view.dart index c36e6ca89..9ba8abc50 100644 --- a/lib/pages/whisper_secondary/view.dart +++ b/lib/pages/whisper_secondary/view.dart @@ -32,6 +32,7 @@ class _WhisperSecPageState extends State { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Text(widget.name), actions: [ @@ -70,7 +71,7 @@ class _WhisperSecPageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), sliver: Obx(() => _buildBody(_controller.loadingState.value)), ), diff --git a/lib/pages/whisper_settings/view.dart b/lib/pages/whisper_settings/view.dart index a0ebffcea..313fe8543 100644 --- a/lib/pages/whisper_settings/view.dart +++ b/lib/pages/whisper_settings/view.dart @@ -34,6 +34,7 @@ class _WhisperSettingsPageState extends State { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: Obx(() => Text(_controller.title.value)), ), @@ -169,7 +170,7 @@ class _WhisperSettingsPageState extends State { final keys = response.keys.toList()..sort(); return ListView.separated( padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, + bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), itemCount: keys.length, itemBuilder: (context, index) { diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index ce5f8b4b8..18e79a6ed 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -331,7 +331,6 @@ class PlPlayerController { late final bool autoPlayEnable = Pref.autoPlayEnable; late final bool enableVerticalExpand = Pref.enableVerticalExpand; late final bool pipNoDanmaku = Pref.pipNoDanmaku; - late final bool removeSafeArea = Pref.removeSafeArea; late final bool tempPlayerConf = Pref.tempPlayerConf; @@ -1394,9 +1393,7 @@ class PlPlayerController { await landScape(); } } else if (isFullScreen.value && !status) { - if (Get.currentRoute.startsWith('/liveRoom') || !removeSafeArea) { - showStatusBar(); - } + showStatusBar(); toggleFullScreen(false); if (mode == FullScreenMode.none) { fsProcessing = false; diff --git a/lib/plugin/pl_player/utils/fullscreen.dart b/lib/plugin/pl_player/utils/fullscreen.dart index f62b9707a..9054a4262 100644 --- a/lib/plugin/pl_player/utils/fullscreen.dart +++ b/lib/plugin/pl_player/utils/fullscreen.dart @@ -64,13 +64,23 @@ Future fullAutoModeForceSensor() async { } Future hideStatusBar() async { + if (!_showStatusBar) { + return; + } + _showStatusBar = false; await SystemChrome.setEnabledSystemUIMode( SystemUiMode.immersiveSticky, ); } +bool _showStatusBar = true; + //退出全屏显示 Future showStatusBar() async { + if (_showStatusBar) { + return; + } + _showStatusBar = true; dynamic document; late SystemUiMode mode = SystemUiMode.edgeToEdge; try { diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 35aa46c91..101ef56dc 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -6,6 +6,7 @@ import 'dart:ui' as ui; import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/progress_bar/audio_video_progress_bar.dart'; import 'package:PiliPlus/common/widgets/progress_bar/segment_progress_bar.dart'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/models/common/super_resolution_type.dart'; import 'package:PiliPlus/models_new/video/video_detail/episode.dart'; @@ -262,7 +263,7 @@ class _PLVideoPlayerState extends State final isPgc = !widget.videoDetailController!.isUgc; final isPlayAll = widget.videoDetailController?.isPlayAll == true; final anySeason = isSeason || isPart || isPgc || isPlayAll; - + final isFullScreen = this.isFullScreen; final double widgetWidth = isLandscape && isFullScreen ? 42 : 35; Widget progressWidget( @@ -605,26 +606,24 @@ class _PLVideoPlayerState extends State ), /// 全屏 - BottomControlType.fullscreen => Obx( - () => ComBtn( - width: widgetWidth, - height: 30, - icon: isFullScreen - ? const Icon( - Icons.fullscreen_exit, - semanticLabel: '退出全屏', - size: 24, - color: Colors.white, - ) - : const Icon( - Icons.fullscreen, - semanticLabel: '全屏', - size: 24, - color: Colors.white, - ), - onTap: () => - plPlayerController.triggerFullScreen(status: !isFullScreen), - ), + BottomControlType.fullscreen => ComBtn( + width: widgetWidth, + height: 30, + icon: isFullScreen + ? const Icon( + Icons.fullscreen_exit, + semanticLabel: '退出全屏', + size: 24, + color: Colors.white, + ) + : const Icon( + Icons.fullscreen, + semanticLabel: '全屏', + size: 24, + color: Colors.white, + ), + onTap: () => + plPlayerController.triggerFullScreen(status: !isFullScreen), ), }; @@ -1010,6 +1009,7 @@ class _PLVideoPlayerState extends State color: Colors.white, fontSize: 12, ); + final isFullScreen = this.isFullScreen; final isLive = plPlayerController.isLive; return Stack( @@ -1274,11 +1274,13 @@ class _PLVideoPlayerState extends State AppBarAni( isTop: true, controller: animationController, + isFullScreen: isFullScreen, child: widget.headerControl, ), AppBarAni( isTop: false, controller: animationController, + isFullScreen: isFullScreen, child: widget.bottomControl ?? BottomControl( @@ -1471,41 +1473,42 @@ class _PLVideoPlayerState extends State ), // 锁 - if (!isLive) - SafeArea( - child: Obx( - () => Visibility( - visible: isFullScreen, - child: Align( - alignment: Alignment.centerLeft, - child: FractionalTranslation( - translation: const Offset(1, -0.4), - child: Visibility( - visible: - plPlayerController.showControls.value && - (isFullScreen || - plPlayerController.controlsLock.value), - child: DecoratedBox( - decoration: const BoxDecoration( - color: Color(0x45000000), - borderRadius: BorderRadius.all(Radius.circular(8)), - ), - child: ComBtn( - icon: Icon( - plPlayerController.controlsLock.value - ? FontAwesomeIcons.lock - : FontAwesomeIcons.lockOpen, - semanticLabel: plPlayerController.controlsLock.value - ? '解锁' - : '锁定', - size: 15, - color: Colors.white, - ), - onTap: () => plPlayerController.onLockControl( - !plPlayerController.controlsLock.value, - ), - ), + if (!isLive && isFullScreen) + ViewSafeArea( + right: false, + child: Align( + alignment: Alignment.centerLeft, + child: FractionalTranslation( + translation: const Offset(1, -0.4), + child: Obx( + () => Visibility( + visible: plPlayerController.showControls.value, + child: DecoratedBox( + decoration: const BoxDecoration( + color: Color(0x45000000), + borderRadius: BorderRadius.all(Radius.circular(8)), ), + child: Obx(() { + final controlsLock = + plPlayerController.controlsLock.value; + return ComBtn( + icon: controlsLock + ? const Icon( + FontAwesomeIcons.lock, + semanticLabel: '解锁', + size: 15, + color: Colors.white, + ) + : const Icon( + FontAwesomeIcons.lockOpen, + semanticLabel: '锁定', + size: 15, + color: Colors.white, + ), + onTap: () => + plPlayerController.onLockControl(!controlsLock), + ); + }), ), ), ), @@ -1514,95 +1517,96 @@ class _PLVideoPlayerState extends State ), // 截图 - SafeArea( - child: Obx( - () => Align( - alignment: Alignment.centerRight, - child: FractionalTranslation( - translation: const Offset(-1, -0.4), - child: Visibility( - visible: - plPlayerController.showControls.value && isFullScreen, - child: DecoratedBox( - decoration: const BoxDecoration( - color: Color(0x45000000), - borderRadius: BorderRadius.all(Radius.circular(8)), - ), - child: ComBtn( - icon: const Icon( - Icons.photo_camera, - semanticLabel: '截图', - size: 20, - color: Colors.white, + if (isFullScreen) + ViewSafeArea( + left: false, + child: Obx( + () => Align( + alignment: Alignment.centerRight, + child: FractionalTranslation( + translation: const Offset(-1, -0.4), + child: Visibility( + visible: plPlayerController.showControls.value, + child: DecoratedBox( + decoration: const BoxDecoration( + color: Color(0x45000000), + borderRadius: BorderRadius.all(Radius.circular(8)), ), - onTap: () { - SmartDialog.showToast('截图中'); - plPlayerController.videoPlayerController - ?.screenshot(format: 'image/png') - .then((value) { - if (value != null && context.mounted) { - SmartDialog.showToast('点击弹窗保存截图'); - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - // title: const Text('点击保存'), - titlePadding: EdgeInsets.zero, - contentPadding: const EdgeInsets.all( - 8, - ), - insetPadding: EdgeInsets.only( - left: maxWidth / 2, - ), - //移除圆角 - shape: const RoundedRectangleBorder(), - content: GestureDetector( - onTap: () async { - String name = DateTime.now() - .toString(); - final SaveResult result = - await SaverGallery.saveImage( - value, - fileName: name, - androidRelativePath: - "Pictures/Screenshots", - skipIfExists: false, - ); - - if (result.isSuccess) { - Get.back(); - SmartDialog.showToast( - '$name.png已保存到相册/截图', - ); - } else { - SmartDialog.showToast( - '保存失败,${result.errorMessage}', - ); - } - }, - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: maxWidth / 3, - maxHeight: maxHeight / 3, - ), - child: Image.memory(value), + child: ComBtn( + icon: const Icon( + Icons.photo_camera, + semanticLabel: '截图', + size: 20, + color: Colors.white, + ), + onTap: () { + SmartDialog.showToast('截图中'); + plPlayerController.videoPlayerController + ?.screenshot(format: 'image/png') + .then((value) { + if (value != null && context.mounted) { + SmartDialog.showToast('点击弹窗保存截图'); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + // title: const Text('点击保存'), + titlePadding: EdgeInsets.zero, + contentPadding: const EdgeInsets.all( + 8, ), - ), - ); - }, - ); - } else { - SmartDialog.showToast('截图失败'); - } - }); - }, + insetPadding: EdgeInsets.only( + left: maxWidth / 2, + ), + //移除圆角 + shape: const RoundedRectangleBorder(), + content: GestureDetector( + onTap: () async { + String name = DateTime.now() + .toString(); + final SaveResult result = + await SaverGallery.saveImage( + value, + fileName: name, + androidRelativePath: + "Pictures/Screenshots", + skipIfExists: false, + ); + + if (result.isSuccess) { + Get.back(); + SmartDialog.showToast( + '$name.png已保存到相册/截图', + ); + } else { + SmartDialog.showToast( + '保存失败,${result.errorMessage}', + ); + } + }, + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: maxWidth / 3, + maxHeight: maxHeight / 3, + ), + child: Image.memory(value), + ), + ), + ); + }, + ); + } else { + SmartDialog.showToast('截图失败'); + } + }); + }, + ), ), ), ), ), ), ), - ), Obx(() { if (plPlayerController.dataStatus.loading || diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart index 7a8bb9c79..0505ba25f 100644 --- a/lib/plugin/pl_player/widgets/app_bar_ani.dart +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -1,16 +1,19 @@ +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:flutter/material.dart'; class AppBarAni extends StatelessWidget { const AppBarAni({ + super.key, required this.child, required this.controller, required this.isTop, - super.key, + required this.isFullScreen, }); final Widget child; final AnimationController controller; final bool isTop; + final bool isFullScreen; @override Widget build(BuildContext context) { @@ -47,7 +50,12 @@ class AppBarAni extends StatelessWidget { tileMode: TileMode.mirror, ), ), - child: SafeArea(bottom: false, child: child), + child: ViewSafeArea( + top: isTop && isFullScreen, + left: isFullScreen, + right: isFullScreen, + child: child, + ), ), ); } diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index a2467388b..744896099 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/models/common/video/source_type.dart'; import 'package:PiliPlus/pages/subscription_detail/view.dart'; @@ -123,6 +124,7 @@ class PiliScheme { 'id': commentSecondaryId, }, () => Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('评论详情'), actions: [ @@ -139,9 +141,7 @@ class PiliScheme { ), ], ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: VideoReplyReplyPanel( enableSlide: false, oid: int.parse(oid), @@ -260,6 +260,7 @@ class PiliScheme { 'enterUri': queryParameters['enterUri'], }, () => Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('评论详情'), actions: [ @@ -279,9 +280,7 @@ class PiliScheme { ), ], ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: VideoReplyReplyPanel( enableSlide: false, oid: oid, @@ -309,6 +308,7 @@ class PiliScheme { 'type': type, }, () => Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('评论详情'), actions: [ @@ -329,9 +329,7 @@ class PiliScheme { ), ], ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: VideoReplyReplyPanel( enableSlide: false, oid: oid, @@ -384,6 +382,7 @@ class PiliScheme { 'id': commentSecondaryId, }, () => Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('评论详情'), actions: [ @@ -394,9 +393,7 @@ class PiliScheme { ), ], ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: VideoReplyReplyPanel( enableSlide: false, oid: oid ?? int.parse(dynId), @@ -781,6 +778,7 @@ class PiliScheme { 'id': commentSecondaryId, }, () => Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text('评论详情'), actions: pageType == '1' @@ -795,9 +793,7 @@ class PiliScheme { ] : null, ), - body: SafeArea( - top: false, - bottom: false, + body: ViewSafeArea( child: VideoReplyReplyPanel( enableSlide: false, oid: int.parse(oid), diff --git a/lib/utils/context_ext.dart b/lib/utils/context_ext.dart index 961e93db0..879566e16 100644 --- a/lib/utils/context_ext.dart +++ b/lib/utils/context_ext.dart @@ -28,7 +28,7 @@ extension ContextExtensions on BuildContext { TextTheme get textTheme => Theme.of(this).textTheme; /// similar to [MediaQuery.of(context).padding] - EdgeInsets get mediaQueryPadding => MediaQuery.paddingOf(this); + EdgeInsets get mediaQueryPadding => MediaQuery.viewPaddingOf(this); /// similar to [MediaQuery.of(context).padding] MediaQueryData get mediaQuery => MediaQuery.of(this); diff --git a/lib/utils/storage_key.dart b/lib/utils/storage_key.dart index bc41af6e4..f461261ac 100644 --- a/lib/utils/storage_key.dart +++ b/lib/utils/storage_key.dart @@ -185,7 +185,6 @@ class SettingBoxKey { enableSingleRow = 'enableSingleRow', displayMode = 'displayMode', smallCardWidth = 'smallCardWidth', - videoPlayerRemoveSafeArea = 'videoPlayerRemoveSafeArea', dynamicsWaterfallFlow = 'dynamicsWaterfallFlow', upPanelPosition = 'upPanelPosition', dynamicsShowAllFollowedUp = 'dynamicsShowAllFollowedUp', diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index bd41817fd..3f224067f 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -604,11 +604,6 @@ class Pref { static bool get enableVerticalExpand => _setting.get(SettingBoxKey.enableVerticalExpand, defaultValue: false); - static bool get removeSafeArea => _setting.get( - SettingBoxKey.videoPlayerRemoveSafeArea, - defaultValue: false, - ); - static double get defaultTextScale => _setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0);