From 0ab07a713e498503fb89b74e96b5177cc8bc0afc Mon Sep 17 00:00:00 2001 From: dom Date: Fri, 23 Jan 2026 19:50:11 +0800 Subject: [PATCH] tweaks Signed-off-by: dom --- .../widgets/dynamic_sliver_appbar_medium.dart | 57 ++- lib/common/widgets/only_layout_widget.dart | 17 + .../widgets/self_sized_horizontal_list.dart | 87 ++--- lib/pages/common/common_page.dart | 59 +-- lib/pages/danmaku/view.dart | 9 +- lib/pages/home/controller.dart | 7 +- lib/pages/home/view.dart | 4 +- lib/pages/live_area_detail/child/view.dart | 66 ++-- .../live_room/superchat/superchat_panel.dart | 2 +- lib/pages/live_room/view.dart | 3 +- lib/pages/live_room/widgets/chat_panel.dart | 2 +- lib/pages/main/controller.dart | 11 +- lib/pages/main/view.dart | 366 +++++++++--------- lib/pages/pgc_index/view.dart | 36 +- lib/pages/rank/view.dart | 131 +++---- lib/pages/setting/models/extra_settings.dart | 12 +- lib/pages/setting/models/style_settings.dart | 16 +- lib/pages/share/view.dart | 6 +- lib/pages/video/pay_coins/view.dart | 2 +- lib/pages/video/reply/view.dart | 2 +- lib/utils/storage_key.dart | 4 +- lib/utils/storage_pref.dart | 14 +- 22 files changed, 443 insertions(+), 470 deletions(-) create mode 100644 lib/common/widgets/only_layout_widget.dart diff --git a/lib/common/widgets/dynamic_sliver_appbar_medium.dart b/lib/common/widgets/dynamic_sliver_appbar_medium.dart index efd347ffb..18b59c946 100644 --- a/lib/common/widgets/dynamic_sliver_appbar_medium.dart +++ b/lib/common/widgets/dynamic_sliver_appbar_medium.dart @@ -1,8 +1,8 @@ +import 'package:PiliPlus/common/widgets/only_layout_widget.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -/// https://github.com/flutter/flutter/issues/18345#issuecomment-1627644396 class DynamicSliverAppBarMedium extends StatefulWidget { const DynamicSliverAppBarMedium({ this.flexibleSpace, @@ -93,56 +93,45 @@ class DynamicSliverAppBarMedium extends StatefulWidget { } class _DynamicSliverAppBarMediumState extends State { - final GlobalKey _childKey = GlobalKey(); - - // As long as the height is 0 instead of the sliver app bar a sliver to box adapter will be used - // to calculate dynamically the size for the sliver app bar - double _height = 0; - - void _updateHeight() { - // Gets the new height and updates the sliver app bar. Needs to be called after the last frame has been rebuild - // otherwise this will throw an error - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - if (_childKey.currentContext == null) return; - setState(() { - _height = (_childKey.currentContext!.findRenderObject()! as RenderBox) - .size - .height; - widget.afterCalc?.call(_height); - }); - }); - } - + final GlobalKey _key = GlobalKey(); + double? _height; double? _width; + late double _topPadding; @override void didChangeDependencies() { super.didChangeDependencies(); + _topPadding = MediaQuery.viewPaddingOf(context).top; final width = MediaQuery.widthOf(context); if (_width != width) { _width = width; - _height = 0; - _updateHeight(); + _height = null; } } @override Widget build(BuildContext context) { - //Needed to lay out the flexibleSpace the first time, so we can calculate its intrinsic height - if (_height == 0) { + if (_height == null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _height = + (_key.currentContext!.findRenderObject() as RenderBox).size.height; + widget.afterCalc?.call(_height!); + setState(() {}); + }); return SliverToBoxAdapter( - child: UnconstrainedBox( - alignment: Alignment.topLeft, - child: SizedBox( - key: _childKey, - width: _width, - child: widget.flexibleSpace, + child: OnlyLayoutWidget( + child: UnconstrainedBox( + alignment: Alignment.topLeft, + child: SizedBox( + key: _key, + width: _width, + child: widget.flexibleSpace, + ), ), ), ); } - final padding = MediaQuery.viewPaddingOf(context).top; return SliverAppBar.medium( leading: widget.leading, automaticallyImplyLeading: widget.automaticallyImplyLeading, @@ -170,8 +159,8 @@ class _DynamicSliverAppBarMediumState extends State { onStretchTrigger: widget.onStretchTrigger, shape: widget.shape, toolbarHeight: kToolbarHeight, - collapsedHeight: kToolbarHeight + padding + 1, - expandedHeight: _height - padding, + collapsedHeight: kToolbarHeight + _topPadding + 1, + expandedHeight: _height! - _topPadding, leadingWidth: widget.leadingWidth, toolbarTextStyle: widget.toolbarTextStyle, titleTextStyle: widget.titleTextStyle, diff --git a/lib/common/widgets/only_layout_widget.dart b/lib/common/widgets/only_layout_widget.dart new file mode 100644 index 000000000..91acee038 --- /dev/null +++ b/lib/common/widgets/only_layout_widget.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart' show RenderProxyBox; + +class OnlyLayoutWidget extends SingleChildRenderObjectWidget { + const OnlyLayoutWidget({ + super.key, + super.child, + }); + + @override + RenderObject createRenderObject(BuildContext context) => Layout(); +} + +class Layout extends RenderProxyBox { + @override + void paint(PaintingContext context, Offset offset) {} +} diff --git a/lib/common/widgets/self_sized_horizontal_list.dart b/lib/common/widgets/self_sized_horizontal_list.dart index ec15b6a90..3409d6d28 100644 --- a/lib/common/widgets/self_sized_horizontal_list.dart +++ b/lib/common/widgets/self_sized_horizontal_list.dart @@ -1,88 +1,61 @@ +import 'package:PiliPlus/common/widgets/only_layout_widget.dart'; import 'package:flutter/material.dart'; -/// https://stackoverflow.com/a/76605401 - class SelfSizedHorizontalList extends StatefulWidget { - final Widget Function(int index) childBuilder; - final int itemCount; - final double gapSize; - final EdgeInsetsGeometry? padding; - final ScrollController? controller; - const SelfSizedHorizontalList({ super.key, - required this.childBuilder, required this.itemCount, - this.gapSize = 5, - this.padding, + required this.itemBuilder, + required this.separatorBuilder, this.controller, + this.padding, }); + final int itemCount; + final EdgeInsets? padding; + final IndexedWidgetBuilder itemBuilder; + final IndexedWidgetBuilder separatorBuilder; + final ScrollController? controller; + @override State createState() => _SelfSizedHorizontalListState(); } class _SelfSizedHorizontalListState extends State { - final infoKey = GlobalKey(); - - double? prevHeight; - double? get height { - if (prevHeight != null) return prevHeight; - prevHeight = infoKey.globalPaintBounds?.height; - return prevHeight; - } - - bool get isInit => height == null; - - // @override - // void didUpdateWidget(SelfSizedHorizontalList oldWidget) { - // super.didUpdateWidget(oldWidget); - // if (BuildConfig.isDebug) { - // prevHeight = null; - // } - // } + final _key = GlobalKey(); + double? _height; @override Widget build(BuildContext context) { - if (height == null) { - WidgetsBinding.instance.addPostFrameCallback((v) => setState(() {})); - } - if (widget.itemCount == 0) return const SizedBox.shrink(); - if (isInit) { - return Align( - alignment: Alignment.centerLeft, + if (_height == null) { + WidgetsBinding.instance.addPostFrameCallback( + (_) { + _height = (_key.currentContext!.findRenderObject() as RenderBox) + .size + .height; + setState(() {}); + }, + ); + return OnlyLayoutWidget( + key: _key, child: Padding( - key: infoKey, - padding: widget.padding ?? EdgeInsets.zero, - child: widget.childBuilder(0), + padding: widget.padding ?? .zero, + child: widget.itemBuilder(context, 0), ), ); } return SizedBox( - height: height, + height: _height, child: ListView.separated( - controller: widget.controller, + scrollDirection: .horizontal, padding: widget.padding, - scrollDirection: Axis.horizontal, itemCount: widget.itemCount, - itemBuilder: (c, i) => widget.childBuilder(i), - separatorBuilder: (c, i) => SizedBox(width: widget.gapSize), + controller: widget.controller, + itemBuilder: widget.itemBuilder, + separatorBuilder: widget.separatorBuilder, ), ); } } - -extension GlobalKeyExtension on GlobalKey { - Rect? get globalPaintBounds { - final renderObject = currentContext?.findRenderObject(); - final translation = renderObject?.getTransformTo(null).getTranslation(); - if (translation != null && renderObject?.paintBounds != null) { - final offset = Offset(translation.x, translation.y); - return renderObject!.paintBounds.shift(offset); - } else { - return null; - } - } -} diff --git a/lib/pages/common/common_page.dart b/lib/pages/common/common_page.dart index 35f5b9a1c..6b591a35e 100644 --- a/lib/pages/common/common_page.dart +++ b/lib/pages/common/common_page.dart @@ -3,7 +3,6 @@ import 'package:PiliPlus/pages/home/controller.dart'; import 'package:PiliPlus/pages/main/controller.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; abstract class CommonPageState< @@ -12,31 +11,33 @@ abstract class CommonPageState< > extends State { R get controller; - RxBool? showBottomBar; - RxBool? showSearchBar; + final _mainController = Get.find(); + RxBool? _showBottomBar; + RxBool? _showSearchBar; + // late double _downScrollCount = 0.0; // 向下滚动计数器 late double _upScrollCount = 0.0; // 向上滚动计数器 double? _lastScrollPosition; // 记录上次滚动位置 final _enableScrollThreshold = Pref.enableScrollThreshold; late final double _scrollThreshold = Pref.scrollThreshold; // 滚动阈值 - late final scrollController = controller.scrollController; + late final _scrollController = controller.scrollController; @override void initState() { super.initState(); + _showBottomBar = _mainController.showBottomBar; try { - showBottomBar = Get.find().bottomBar; - showSearchBar = Get.find().searchBar; + _showSearchBar = Get.find().showSearchBar; } catch (_) {} if (_enableScrollThreshold && - (showBottomBar != null || showSearchBar != null)) { - controller.scrollController.addListener(listener); + (_showBottomBar != null || _showSearchBar != null)) { + _scrollController.addListener(listener); } } Widget onBuild(Widget child) { if (!_enableScrollThreshold && - (showBottomBar != null || showSearchBar != null)) { + (_showBottomBar != null || _showSearchBar != null)) { return NotificationListener( onNotification: onNotification, child: child, @@ -46,22 +47,24 @@ abstract class CommonPageState< } bool onNotification(UserScrollNotification notification) { - if (notification.metrics.axis == Axis.horizontal) return false; + if (notification.metrics.axis == .horizontal) return false; + if (!_mainController.useBottomNav) return false; final direction = notification.direction; - if (direction == ScrollDirection.forward) { - showBottomBar?.value = true; - showSearchBar?.value = true; - } else if (direction == ScrollDirection.reverse) { - showBottomBar?.value = false; - showSearchBar?.value = false; + if (direction == .forward) { + _showBottomBar?.value = true; + _showSearchBar?.value = true; + } else if (direction == .reverse) { + _showBottomBar?.value = false; + _showSearchBar?.value = false; } return false; } void listener() { - final direction = scrollController.position.userScrollDirection; + if (!_mainController.useBottomNav) return; + final direction = _scrollController.position.userScrollDirection; - final double currentPosition = scrollController.position.pixels; + final double currentPosition = _scrollController.position.pixels; // 初始化上次位置 _lastScrollPosition ??= currentPosition; @@ -69,9 +72,9 @@ abstract class CommonPageState< // 计算滚动距离 final double scrollDelta = currentPosition - _lastScrollPosition!; - if (direction == ScrollDirection.reverse) { - showBottomBar?.value = false; - showSearchBar?.value = false; // // 向下滚动,累加向下滚动距离,重置向上滚动计数器 + if (direction == .reverse) { + _showBottomBar?.value = false; + _showSearchBar?.value = false; // // 向下滚动,累加向下滚动距离,重置向上滚动计数器 _upScrollCount = 0.0; // 重置向上滚动计数器 // if (scrollDelta > 0) { // _downScrollCount += scrollDelta; @@ -83,16 +86,16 @@ abstract class CommonPageState< // searchBarStream?.add(false); // } // } - } else if (direction == ScrollDirection.forward) { + } else if (direction == .forward) { // 向上滚动,累加向上滚动距离,重置向下滚动计数器 if (scrollDelta < 0) { - _upScrollCount += (-scrollDelta); // 使用绝对值 + _upScrollCount -= scrollDelta; // 使用绝对值 // _downScrollCount = 0.0; // 重置向下滚动计数器 // 当累计向上滚动距离超过阈值时,显示顶底栏 if (_upScrollCount >= _scrollThreshold) { - showBottomBar?.value = true; - showSearchBar?.value = true; + _showBottomBar?.value = true; + _showSearchBar?.value = true; } } } @@ -103,9 +106,9 @@ abstract class CommonPageState< @override void dispose() { - showSearchBar = null; - showBottomBar = null; - controller.scrollController.removeListener(listener); + _showSearchBar = null; + _showBottomBar = null; + _scrollController.removeListener(listener); super.dispose(); } } diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 2068079bb..ec16ef5b5 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -165,6 +165,10 @@ class _PlDanmakuState extends State { @override Widget build(BuildContext context) { + final option = DanmakuOptions.get( + notFullscreen: widget.notFullscreen, + speed: playerController.playbackSpeed, + ); return Obx( () => AnimatedOpacity( opacity: playerController.enableShowDanmaku.value @@ -175,10 +179,7 @@ class _PlDanmakuState extends State { createdController: (e) { playerController.danmakuController = _controller = e; }, - option: DanmakuOptions.get( - notFullscreen: widget.notFullscreen, - speed: playerController.playbackSpeed, - ), + option: option, size: widget.size, ), ), diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 92c6895f8..08d658db6 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -19,8 +19,7 @@ class HomeController extends GetxController late List tabs; late TabController tabController; - RxBool? searchBar; - final bool useSideBar = Pref.useSideBar; + RxBool? showSearchBar; bool enableSearchWord = Pref.enableSearchWord; late final RxString defaultSearch = ''.obs; @@ -37,8 +36,8 @@ class HomeController extends GetxController void onInit() { super.onInit(); - if (Pref.hideSearchBar) { - searchBar = true.obs; + if (!Pref.useSideBar && Pref.hideTopBar) { + showSearchBar = true.obs; } if (enableSearchWord) { diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index a7923578d..743125b22 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -32,7 +32,7 @@ class _HomePageState extends State final theme = Theme.of(context); return Column( children: [ - if (!_homeController.useSideBar && + if (!_mainController.useSideBar && MediaQuery.sizeOf(context).isPortrait) customAppBar(theme), if (_homeController.tabs.length > 1) @@ -87,7 +87,7 @@ class _HomePageState extends State userAvatar(theme: theme, mainController: _mainController), ], ); - if (_homeController.searchBar case final searchBar?) { + if (_homeController.showSearchBar case final searchBar?) { return Obx(() { final showSearchBar = searchBar.value; return AnimatedOpacity( diff --git a/lib/pages/live_area_detail/child/view.dart b/lib/pages/live_area_detail/child/view.dart index c4d2072b9..e1c989c23 100644 --- a/lib/pages/live_area_detail/child/view.dart +++ b/lib/pages/live_area_detail/child/view.dart @@ -81,40 +81,38 @@ class _LiveAreaChildPageState extends State slivers: [ if (_controller.newTags?.isNotEmpty == true) SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.only(bottom: 12), - child: SelfSizedHorizontalList( - gapSize: 12, - childBuilder: (index) { - late final item = _controller.newTags![index]; - return Obx( - () { - final isCurr = index == _controller.tagIndex.value; - return SearchText( - fontSize: 14, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, - ), - text: '${item.name}', - bgColor: isCurr - ? theme.colorScheme.secondaryContainer - : Colors.transparent, - textColor: isCurr - ? theme.colorScheme.onSecondaryContainer - : null, - onTap: (value) { - _controller.onSelectTag( - index, - item.sortType, - ); - }, - ); - }, - ); - }, - itemCount: _controller.newTags!.length, - ), + child: SelfSizedHorizontalList( + padding: const .only(bottom: 12), + separatorBuilder: (_, _) => const SizedBox(width: 12), + itemBuilder: (context, index) { + late final item = _controller.newTags![index]; + return Obx( + () { + final isCurr = index == _controller.tagIndex.value; + return SearchText( + fontSize: 14, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + text: '${item.name}', + bgColor: isCurr + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + textColor: isCurr + ? theme.colorScheme.onSecondaryContainer + : null, + onTap: (value) { + _controller.onSelectTag( + index, + item.sortType, + ); + }, + ); + }, + ); + }, + itemCount: _controller.newTags!.length, ), ), response != null && response.isNotEmpty diff --git a/lib/pages/live_room/superchat/superchat_panel.dart b/lib/pages/live_room/superchat/superchat_panel.dart index d91ad0883..d08e875b0 100644 --- a/lib/pages/live_room/superchat/superchat_panel.dart +++ b/lib/pages/live_room/superchat/superchat_panel.dart @@ -30,7 +30,7 @@ class _SuperChatPanelState extends DebounceStreamState super.build(context); return Obx( () => ListView.separated( - key: const PageStorageKey('live-sc'), + key: const PageStorageKey(_SuperChatPanelState), padding: const EdgeInsets.symmetric(horizontal: 12), physics: const ClampingScrollPhysics(), itemCount: widget.controller.superChatMsg.length, diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index a93dcbafc..79ad395d0 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -1050,6 +1050,7 @@ class _LiveDanmakuState extends State { @override Widget build(BuildContext context) { + final option = DanmakuOptions.get(notFullscreen: widget.notFullscreen); return Obx( () => AnimatedOpacity( opacity: plPlayerController.enableShowDanmaku.value @@ -1061,7 +1062,7 @@ class _LiveDanmakuState extends State { widget.liveRoomController.danmakuController = plPlayerController.danmakuController = e; }, - option: DanmakuOptions.get(notFullscreen: widget.notFullscreen), + option: option, size: widget.size, ), ), diff --git a/lib/pages/live_room/widgets/chat_panel.dart b/lib/pages/live_room/widgets/chat_panel.dart index 19b69c80c..c4706f373 100644 --- a/lib/pages/live_room/widgets/chat_panel.dart +++ b/lib/pages/live_room/widgets/chat_panel.dart @@ -49,7 +49,7 @@ class LiveRoomChatPanel extends StatelessWidget { children: [ Obx( () => ListView.separated( - key: const PageStorageKey('live-chat'), + key: const PageStorageKey(LiveRoomChatPanel), padding: const EdgeInsets.symmetric(horizontal: 12), controller: liveRoomController.scrollController, separatorBuilder: (_, _) => const SizedBox(height: 8), diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index e39c7ccec..571ad1ed1 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -30,7 +30,8 @@ class MainController extends GetxController List navigationBars = []; - RxBool? bottomBar; + RxBool? showBottomBar; + bool useBottomNav = false; late dynamic controller; final RxInt selectedIndex = 0.obs; @@ -81,8 +82,8 @@ class MainController extends GetxController ) : PageController(initialPage: selectedIndex.value); - if (navigationBars.length > 1 && Pref.hideTabBar) { - bottomBar = true.obs; + if (!useSideBar && navigationBars.length > 1 && Pref.hideBottomBar) { + showBottomBar = true.obs; } dynamicBadgeMode = Pref.dynamicBadgeMode; @@ -314,13 +315,13 @@ class MainController extends GetxController void setSearchBar() { if (hasHome) { - homeController.searchBar?.value = true; + homeController.showSearchBar?.value = true; } } @override void onClose() { - bottomBar?.close(); + showBottomBar?.close(); controller.dispose(); super.onClose(); } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 10bb2201a..c6f66d8dd 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -64,6 +64,9 @@ class _MainAppState extends PopScopeState this, ModalRoute.of(context) as PageRoute, ); + if (!_mainController.useSideBar) { + _mainController.useBottomNav = MediaQuery.sizeOf(context).isPortrait; + } } @override @@ -72,7 +75,7 @@ class _MainAppState extends PopScopeState _mainController ..checkUnreadDynamic() ..checkDefaultSearch(true) - ..checkUnread(useBottomNav); + ..checkUnread(_mainController.useBottomNav); super.didPopNext(); } @@ -88,7 +91,7 @@ class _MainAppState extends PopScopeState _mainController ..checkUnreadDynamic() ..checkDefaultSearch(true) - ..checkUnread(useBottomNav); + ..checkUnread(_mainController.useBottomNav); } } @@ -239,57 +242,6 @@ class _MainAppState extends PopScopeState } } - late bool useBottomNav; - - Widget? get _bottomNav { - return useBottomNav - ? _mainController.navigationBars.length > 1 - ? _mainController.enableMYBar - ? Obx( - () => NavigationBar( - maintainBottomViewPadding: true, - onDestinationSelected: _mainController.setIndex, - selectedIndex: _mainController.selectedIndex.value, - destinations: _mainController.navigationBars - .map( - (e) => NavigationDestination( - label: e.label, - icon: _buildIcon(type: e), - selectedIcon: _buildIcon( - type: e, - selected: true, - ), - ), - ) - .toList(), - ), - ) - : Obx( - () => BottomNavigationBar( - currentIndex: _mainController.selectedIndex.value, - onTap: _mainController.setIndex, - iconSize: 16, - selectedFontSize: 12, - unselectedFontSize: 12, - type: .fixed, - items: _mainController.navigationBars - .map( - (e) => BottomNavigationBarItem( - label: e.label, - icon: _buildIcon(type: e), - activeIcon: _buildIcon( - type: e, - selected: true, - ), - ), - ) - .toList(), - ), - ) - : null - : null; - } - @override void onPopInvokedWithResult(bool didPop, Object? result) { if (_mainController.directExitOnBack) { @@ -298,7 +250,7 @@ class _MainAppState extends PopScopeState if (_mainController.selectedIndex.value != 0) { _mainController ..setIndex(0) - ..bottomBar?.value = true + ..showBottomBar?.value = true ..setSearchBar(); } else { _onBack(); @@ -306,140 +258,47 @@ class _MainAppState extends PopScopeState } } - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final padding = MediaQuery.viewPaddingOf(context); - useBottomNav = - !_mainController.useSideBar && MediaQuery.sizeOf(context).isPortrait; - final bottomNav = _bottomNav; - return AnnotatedRegion( - value: SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - systemNavigationBarIconBrightness: theme.brightness.reverse, - ), - child: Scaffold( - extendBody: true, - resizeToAvoidBottomInset: false, - appBar: AppBar(toolbarHeight: 0), - body: Padding( - padding: EdgeInsets.only( - left: useBottomNav ? padding.left : 0.0, - right: padding.right, - ), - child: Row( - mainAxisAlignment: .center, - children: [ - if (!useBottomNav) ...[ - _mainController.navigationBars.length > 1 - ? context.isTablet && _mainController.optTabletNav - ? Column( - children: [ - const SizedBox(height: 25), - userAndSearchVertical(theme), - const Spacer(flex: 2), - Expanded( - flex: 5, - child: SizedBox( - width: 130, - child: Obx( - () => NavigationDrawer( - backgroundColor: Colors.transparent, - tilePadding: const .symmetric( - vertical: 5, - horizontal: 12, - ), - indicatorShape: - const RoundedRectangleBorder( - borderRadius: .all( - .circular(16), - ), - ), - onDestinationSelected: - _mainController.setIndex, - selectedIndex: - _mainController.selectedIndex.value, - children: _mainController.navigationBars - .map( - (e) => - NavigationDrawerDestination( - label: Text(e.label), - icon: _buildIcon(type: e), - selectedIcon: _buildIcon( - type: e, - selected: true, - ), - ), - ) - .toList(), - ), - ), - ), - ), - ], - ) - : Obx( - () => NavigationRail( - groupAlignment: 0.5, - selectedIndex: - _mainController.selectedIndex.value, - onDestinationSelected: _mainController.setIndex, - labelType: .selected, - leading: userAndSearchVertical(theme), - destinations: _mainController.navigationBars - .map( - (e) => NavigationRailDestination( - label: Text(e.label), - icon: _buildIcon(type: e), - selectedIcon: _buildIcon( - type: e, - selected: true, - ), - ), - ) - .toList(), - ), - ) - : Container( - width: 80, - padding: const .only(top: 10), - child: userAndSearchVertical(theme), - ), - VerticalDivider( - width: 1, - endIndent: padding.bottom, - color: theme.colorScheme.outline.withValues(alpha: 0.06), - ), - ], - Expanded( - child: _mainController.mainTabBarView - ? CustomTabBarView( - scrollDirection: useBottomNav ? .horizontal : .vertical, - physics: const NeverScrollableScrollPhysics(), - controller: _mainController.controller, - children: _mainController.navigationBars - .map((i) => i.page) - .toList(), - ) - : PageView( - physics: const NeverScrollableScrollPhysics(), - controller: _mainController.controller, - children: _mainController.navigationBars - .map((i) => i.page) - .toList(), - ), - ), - ], - ), - ), - bottomNavigationBar: _buildBottom(bottomNav), - ), - ); - } - - Widget? _buildBottom(Widget? bottomNav) { + Widget? get _bottomNav { + Widget? bottomNav = _mainController.navigationBars.length > 1 + ? _mainController.enableMYBar + ? Obx( + () => NavigationBar( + maintainBottomViewPadding: true, + onDestinationSelected: _mainController.setIndex, + selectedIndex: _mainController.selectedIndex.value, + destinations: _mainController.navigationBars + .map( + (e) => NavigationDestination( + label: e.label, + icon: _buildIcon(type: e), + selectedIcon: _buildIcon(type: e, selected: true), + ), + ) + .toList(), + ), + ) + : Obx( + () => BottomNavigationBar( + currentIndex: _mainController.selectedIndex.value, + onTap: _mainController.setIndex, + iconSize: 16, + selectedFontSize: 12, + unselectedFontSize: 12, + type: .fixed, + items: _mainController.navigationBars + .map( + (e) => BottomNavigationBarItem( + label: e.label, + icon: _buildIcon(type: e), + activeIcon: _buildIcon(type: e, selected: true), + ), + ) + .toList(), + ), + ) + : null; if (bottomNav != null) { - if (_mainController.bottomBar case final bottomBar?) { + if (_mainController.showBottomBar case final bottomBar?) { return Obx( () => AnimatedSlide( curve: Curves.easeInOutCubicEmphasized, @@ -453,6 +312,139 @@ class _MainAppState extends PopScopeState return bottomNav; } + Widget _sideBar(ThemeData theme) { + return _mainController.navigationBars.length > 1 + ? context.isTablet && _mainController.optTabletNav + ? Column( + children: [ + const SizedBox(height: 25), + userAndSearchVertical(theme), + const Spacer(flex: 2), + Expanded( + flex: 5, + child: SizedBox( + width: 130, + child: Obx( + () => NavigationDrawer( + backgroundColor: Colors.transparent, + tilePadding: const .symmetric( + vertical: 5, + horizontal: 12, + ), + indicatorShape: const RoundedRectangleBorder( + borderRadius: .all(.circular(16)), + ), + onDestinationSelected: _mainController.setIndex, + selectedIndex: _mainController.selectedIndex.value, + children: _mainController.navigationBars + .map( + (e) => NavigationDrawerDestination( + label: Text(e.label), + icon: _buildIcon(type: e), + selectedIcon: _buildIcon( + type: e, + selected: true, + ), + ), + ) + .toList(), + ), + ), + ), + ), + ], + ) + : Obx( + () => NavigationRail( + groupAlignment: 0.5, + selectedIndex: _mainController.selectedIndex.value, + onDestinationSelected: _mainController.setIndex, + labelType: .selected, + leading: userAndSearchVertical(theme), + destinations: _mainController.navigationBars + .map( + (e) => NavigationRailDestination( + label: Text(e.label), + icon: _buildIcon(type: e), + selectedIcon: _buildIcon(type: e, selected: true), + ), + ) + .toList(), + ), + ) + : Container( + width: 80, + padding: const .only(top: 10), + child: userAndSearchVertical(theme), + ); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final padding = MediaQuery.viewPaddingOf(context); + + Widget child; + if (_mainController.mainTabBarView) { + child = CustomTabBarView( + scrollDirection: _mainController.useBottomNav ? .horizontal : .vertical, + physics: const NeverScrollableScrollPhysics(), + controller: _mainController.controller, + children: _mainController.navigationBars.map((i) => i.page).toList(), + ); + } else { + child = PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _mainController.controller, + children: _mainController.navigationBars.map((i) => i.page).toList(), + ); + } + + Widget? bottomNav; + if (_mainController.useBottomNav) { + bottomNav = _bottomNav; + child = Row(children: [Expanded(child: child)]); + } else { + child = Row( + children: [ + _sideBar(theme), + VerticalDivider( + width: 1, + endIndent: padding.bottom, + color: theme.colorScheme.outline.withValues(alpha: 0.06), + ), + Expanded(child: child), + ], + ); + } + + child = Scaffold( + extendBody: true, + resizeToAvoidBottomInset: false, + appBar: AppBar(toolbarHeight: 0), + body: Padding( + padding: EdgeInsets.only( + left: _mainController.useBottomNav ? padding.left : 0.0, + right: padding.right, + ), + child: child, + ), + bottomNavigationBar: bottomNav, + ); + + if (PlatformUtils.isMobile) { + child = AnnotatedRegion( + value: SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarIconBrightness: theme.brightness.reverse, + ), + child: child, + ); + } + + return child; + } + Widget _buildIcon({required NavigationBarType type, bool selected = false}) { final icon = selected ? type.selectIcon : type.icon; return type == .dynamics diff --git a/lib/pages/pgc_index/view.dart b/lib/pages/pgc_index/view.dart index 1b48e12b6..1348aa6f1 100644 --- a/lib/pages/pgc_index/view.dart +++ b/lib/pages/pgc_index/view.dart @@ -172,31 +172,25 @@ class _PgcIndexPageState extends State : count ~/ 2 : count, (index) { + final isFirst = index == 0; List? item = data.order?.isNotEmpty == true - ? index == 0 + ? isFirst ? data.order : data.filter![index - 1].values : data.filter![index].values; - return item != null && item.isNotEmpty - ? Padding( - padding: index == 0 - ? EdgeInsets.zero - : const EdgeInsets.only(top: 10), - child: SelfSizedHorizontalList( - gapSize: 12, - padding: const EdgeInsets.symmetric(horizontal: 12), - childBuilder: (childIndex) { - return _buildSortWidget( - theme, - index, - data, - item[childIndex], - ); - }, - itemCount: item.length, - ), - ) - : const SizedBox.shrink(); + if (item != null && item.isNotEmpty) { + return SelfSizedHorizontalList( + padding: isFirst + ? const .symmetric(horizontal: 12) + : const .fromLTRB(12, 10, 12, 0), + separatorBuilder: (_, _) => const SizedBox(width: 12), + itemBuilder: (context, childIndex) { + return _buildSortWidget(theme, index, data, item[childIndex]); + }, + itemCount: item.length, + ); + } + return const SizedBox.shrink(); }, ), if (count > 5) ...[ diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 940769332..3443ce304 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -24,71 +24,7 @@ class _RankPageState extends State final theme = Theme.of(context); return Row( children: [ - SizedBox( - width: 64, - child: ListView( - padding: const EdgeInsets.only(bottom: 100), - children: List.generate( - RankType.values.length, - (index) => IntrinsicHeight( - child: Obx( - () { - final isCurr = index == _rankController.tabIndex.value; - return Material( - color: isCurr - ? theme.colorScheme.onInverseSurface - : theme.colorScheme.surface, - child: InkWell( - onTap: () { - if (isCurr) { - _rankController.animateToTop(); - } else { - _rankController - ..tabIndex.value = index - ..tabController.animateTo(index); - } - }, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (isCurr) - Container( - height: double.infinity, - width: 3, - color: theme.colorScheme.primary, - ) - else - const SizedBox(width: 3), - Expanded( - flex: 1, - child: Container( - alignment: Alignment.center, - padding: const EdgeInsets.symmetric( - vertical: 7, - ), - child: Text( - RankType.values[index].label, - style: TextStyle( - color: isCurr - ? theme.colorScheme.primary - : theme.colorScheme.onSurface, - fontSize: 15, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ), - ], - ), - ), - ); - }, - ), - ), - ), - ), - ), + _buildTab(theme), Expanded( child: TabBarView( physics: const NeverScrollableScrollPhysics(), @@ -106,4 +42,69 @@ class _RankPageState extends State ], ); } + + Widget _buildTab(ThemeData theme) { + return SizedBox( + width: 64, + child: Obx(() { + final tabIndex = _rankController.tabIndex.value; + return ListView( + padding: .only(bottom: MediaQuery.paddingOf(context).bottom + 105), + children: RankType.values.map((e) { + final index = e.index; + final isCurr = index == tabIndex; + return IntrinsicHeight( + child: Material( + color: isCurr + ? theme.colorScheme.onInverseSurface + : theme.colorScheme.surface, + child: InkWell( + onTap: () { + if (isCurr) { + _rankController.animateToTop(); + } else { + _rankController + ..tabIndex.value = index + ..tabController.animateTo(index); + } + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (isCurr) + Container( + width: 3, + height: double.infinity, + color: theme.colorScheme.primary, + ) + else + const SizedBox(width: 3), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.center, + padding: const .symmetric(vertical: 7), + child: Text( + RankType.values[index].label, + style: TextStyle( + color: isCurr + ? theme.colorScheme.primary + : theme.colorScheme.onSurface, + fontSize: 15, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ), + ), + ), + ); + }).toList(), + ); + }), + ); + } } diff --git a/lib/pages/setting/models/extra_settings.dart b/lib/pages/setting/models/extra_settings.dart index a91f32632..b10491f8a 100644 --- a/lib/pages/setting/models/extra_settings.dart +++ b/lib/pages/setting/models/extra_settings.dart @@ -274,17 +274,17 @@ List get extraSettings => [ setKey: SettingBoxKey.expandIntroPanelH, defaultVal: false, ), - const SwitchModel( + SwitchModel( title: '横屏分P/合集列表显示在Tab栏', - leading: Icon(Icons.format_list_numbered_rtl_sharp), + leading: const Icon(Icons.format_list_numbered_rtl_sharp), setKey: SettingBoxKey.horizontalSeasonPanel, - defaultVal: false, + defaultVal: PlatformUtils.isDesktop, ), - const SwitchModel( + SwitchModel( title: '横屏播放页在侧栏打开UP主页', - leading: Icon(Icons.account_circle_outlined), + leading: const Icon(Icons.account_circle_outlined), setKey: SettingBoxKey.horizontalMemberPage, - defaultVal: false, + defaultVal: PlatformUtils.isDesktop, ), SwitchModel( title: '横屏在侧栏打开图片预览', diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart index aed333286..b1778f12c 100644 --- a/lib/pages/setting/models/style_settings.dart +++ b/lib/pages/setting/models/style_settings.dart @@ -331,20 +331,20 @@ List get styleSettings => [ getSubtitle: () => '当前消息类型:${Pref.msgUnReadTypeV2.map((item) => item.title).join('、')}', ), - const SwitchModel( + SwitchModel( title: '首页顶栏收起', subtitle: '首页列表滑动时,收起顶栏', - leading: Icon(Icons.vertical_align_top_outlined), - setKey: SettingBoxKey.hideSearchBar, - defaultVal: true, + leading: const Icon(Icons.vertical_align_top_outlined), + setKey: SettingBoxKey.hideTopBar, + defaultVal: PlatformUtils.isMobile, needReboot: true, ), - const SwitchModel( + SwitchModel( title: '首页底栏收起', subtitle: '首页列表滑动时,收起底栏', - leading: Icon(Icons.vertical_align_bottom_outlined), - setKey: SettingBoxKey.hideTabBar, - defaultVal: true, + leading: const Icon(Icons.vertical_align_bottom_outlined), + setKey: SettingBoxKey.hideBottomBar, + defaultVal: PlatformUtils.isMobile, needReboot: true, ), SwitchModel( diff --git a/lib/pages/share/view.dart b/lib/pages/share/view.dart index 001e0151b..c0da274d8 100644 --- a/lib/pages/share/view.dart +++ b/lib/pages/share/view.dart @@ -104,11 +104,11 @@ class _SharePanelState extends State { children: [ Expanded( child: SelfSizedHorizontalList( - gapSize: 10, + padding: .zero, itemCount: _userList.length, controller: _scrollController, - padding: EdgeInsets.zero, - childBuilder: (index) { + separatorBuilder: (_, _) => const SizedBox(width: 10), + itemBuilder: (context, index) { final item = _userList[index]; return Builder( builder: (context) { diff --git a/lib/pages/video/pay_coins/view.dart b/lib/pages/video/pay_coins/view.dart index 9ee33bafe..5317dd123 100644 --- a/lib/pages/video/pay_coins/view.dart +++ b/lib/pages/video/pay_coins/view.dart @@ -334,7 +334,7 @@ class _PayCoinsPageState extends State child: SizedBox( height: 100, child: PageView( - key: const PageStorageKey('PageView'), + key: const PageStorageKey(_PayCoinsPageState), physics: const CustomTabBarViewScrollPhysics( parent: ClampingScrollPhysics(), ), diff --git a/lib/pages/video/reply/view.dart b/lib/pages/video/reply/view.dart index 9c5765238..dabf3d7eb 100644 --- a/lib/pages/video/reply/view.dart +++ b/lib/pages/video/reply/view.dart @@ -85,7 +85,7 @@ class _VideoReplyPanelState extends State parent: ClampingScrollPhysics(), ) : const AlwaysScrollableScrollPhysics(), - key: const PageStorageKey('评论'), + key: const PageStorageKey(_VideoReplyPanelState), slivers: [ SliverPersistentHeader( pinned: false, diff --git a/lib/utils/storage_key.dart b/lib/utils/storage_key.dart index b42f2577b..d365aaaf0 100644 --- a/lib/utils/storage_key.dart +++ b/lib/utils/storage_key.dart @@ -216,8 +216,8 @@ abstract final class SettingBoxKey { dynamicsShowAllFollowedUp = 'dynamicsShowAllFollowedUp', useSideBar = 'useSideBar', enableMYBar = 'enableMYBar', - hideSearchBar = 'hideSearchBar', - hideTabBar = 'hideTabBar', + hideTopBar = 'hideSearchBar', + hideBottomBar = 'hideTabBar', scrollThreshold = 'scrollThreshold', enableScrollThreshold = 'enableScrollThreshold', tabBarSort = 'tabBarSort', diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index 99b73af22..c7a03f129 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -655,8 +655,15 @@ abstract final class Pref { static bool get dynamicsWaterfallFlow => _setting.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); - static bool get hideSearchBar => - _setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); + static bool get hideTopBar => _setting.get( + SettingBoxKey.hideTopBar, + defaultValue: PlatformUtils.isMobile, + ); + + static bool get hideBottomBar => _setting.get( + SettingBoxKey.hideBottomBar, + defaultValue: PlatformUtils.isMobile, + ); static bool get enableScrollThreshold => _setting.get(SettingBoxKey.enableScrollThreshold, defaultValue: false); @@ -709,9 +716,6 @@ abstract final class Pref { defaultValue: ReplySortType.hot.index, )]; - static bool get hideTabBar => - _setting.get(SettingBoxKey.hideTabBar, defaultValue: true); - static DynamicBadgeMode get dynamicBadgeMode => DynamicBadgeMode.values[_setting.get( SettingBoxKey.dynamicBadgeMode,