add bar hide type

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-02-18 21:12:34 +08:00
parent a142b15344
commit 3f3d54fd27
12 changed files with 223 additions and 63 deletions

View File

@@ -1653,7 +1653,11 @@ class _VerticalTabBarState extends State<VerticalTabBar> {
tabCenter +
paddingTop -
viewportWidth / 2.0 +
(_mainCtr.useBottomNav && (_mainCtr.barOffset?.value ?? 0) == 0
(_mainCtr.useBottomNav &&
switch (_mainCtr.barHideType) {
.instant => _mainCtr.showBottomBar?.value ?? true,
.sync => (_mainCtr.barOffset?.value ?? 0) == 0,
}
? 80.0
: 0.0),
minExtent,

View File

@@ -0,0 +1,11 @@
import 'package:PiliPlus/models/common/enum_with_label.dart';
enum BarHideType with EnumWithLabel {
instant('即时'),
sync('同步')
;
@override
final String label;
const BarHideType(this.label);
}

View File

@@ -1,5 +1,6 @@
import 'package:PiliPlus/common/constants.dart' show StyleString;
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/home/controller.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/material.dart';
@@ -11,25 +12,52 @@ abstract class CommonPageState<
>
extends State<T> {
R get controller;
final _mainController = Get.find<MainController>();
RxDouble? _barOffset;
RxBool? _showTopBar;
RxBool? _showBottomBar;
final _mainController = Get.find<MainController>();
@override
void initState() {
super.initState();
_barOffset = _mainController.barOffset;
_showBottomBar = _mainController.showBottomBar;
try {
_showTopBar = Get.find<HomeController>().showTopBar;
} catch (_) {}
}
Widget onBuild(Widget child) {
if (_barOffset != null) {
return NotificationListener<ScrollNotification>(
onNotification: onNotification,
onNotification: onNotificationType2,
child: child,
);
}
if (_showTopBar != null || _showBottomBar != null) {
return NotificationListener<UserScrollNotification>(
onNotification: onNotificationType1,
child: child,
);
}
return child;
}
bool onNotificationType1(UserScrollNotification notification) {
if (!_mainController.useBottomNav) return false;
if (notification.metrics.axis == .horizontal) return false;
switch (notification.direction) {
case .forward:
_showTopBar?.value = true;
_showBottomBar?.value = true;
case .reverse:
_showTopBar?.value = false;
_showBottomBar?.value = false;
case _:
}
return false;
}
void _updateOffset(double scrollDelta) {
_barOffset!.value = clampDouble(
_barOffset!.value + scrollDelta,
@@ -38,7 +66,7 @@ abstract class CommonPageState<
);
}
bool onNotification(ScrollNotification notification) {
bool onNotificationType2(ScrollNotification notification) {
if (!_mainController.useBottomNav) return false;
if (notification.metrics.axis == .horizontal) return false;

View File

@@ -46,11 +46,19 @@ class _DynamicsTabPageState
_mainController.selectedIndex.value == 0;
@override
bool onNotification(ScrollNotification notification) {
bool onNotificationType1(UserScrollNotification notification) {
if (checkPage) {
return false;
}
return super.onNotification(notification);
return super.onNotificationType1(notification);
}
@override
bool onNotificationType2(ScrollNotification notification) {
if (checkPage) {
return false;
}
return super.onNotificationType2(notification);
}
@override

View File

@@ -5,6 +5,7 @@ import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/models/common/home_tab_type.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
@@ -19,7 +20,8 @@ class HomeController extends GetxController
late List<HomeTabType> tabs;
late TabController tabController;
final bool hideTopBar = !Pref.useSideBar && Pref.hideTopBar;
RxBool? showTopBar;
late final bool hideTopBar;
bool enableSearchWord = Pref.enableSearchWord;
late final RxString defaultSearch = ''.obs;
@@ -36,6 +38,17 @@ class HomeController extends GetxController
void onInit() {
super.onInit();
hideTopBar = !Pref.useSideBar && Pref.hideTopBar;
if (hideTopBar) {
final mainCtr = Get.find<MainController>();
switch (mainCtr.barHideType) {
case .instant:
showTopBar = RxBool(true);
case .sync:
mainCtr.barOffset ??= RxDouble(0.0);
}
}
if (enableSearchWord) {
lateCheckSearchAt = DateTime.now().millisecondsSinceEpoch;
querySearchDefault();

View File

@@ -31,38 +31,46 @@ class _HomePageState extends State<HomePage>
Widget build(BuildContext context) {
super.build(context);
final theme = Theme.of(context);
Widget tabBar;
if (_homeController.tabs.length > 1) {
tabBar = Padding(
padding: const EdgeInsets.only(top: 4),
child: SizedBox(
height: 42,
width: double.infinity,
child: TabBar(
controller: _homeController.tabController,
tabs: _homeController.tabs.map((e) => Tab(text: e.label)).toList(),
isScrollable: true,
dividerColor: Colors.transparent,
dividerHeight: 0,
splashBorderRadius: StyleString.mdRadius,
tabAlignment: TabAlignment.center,
onTap: (_) {
feedBack();
if (!_homeController.tabController.indexIsChanging) {
_homeController.animateToTop();
}
},
),
),
);
if (_homeController.hideTopBar &&
_mainController.barHideType == .instant) {
tabBar = Material(
color: theme.colorScheme.surface,
child: tabBar,
);
}
} else {
tabBar = const SizedBox(height: 6);
}
return Column(
children: [
if (!_mainController.useSideBar &&
MediaQuery.sizeOf(context).isPortrait)
customAppBar(theme),
if (_homeController.tabs.length > 1)
Padding(
padding: const EdgeInsets.only(top: 4),
child: SizedBox(
height: 42,
width: double.infinity,
child: TabBar(
controller: _homeController.tabController,
tabs: _homeController.tabs
.map((e) => Tab(text: e.label))
.toList(),
isScrollable: true,
dividerColor: Colors.transparent,
dividerHeight: 0,
splashBorderRadius: StyleString.mdRadius,
tabAlignment: TabAlignment.center,
onTap: (_) {
feedBack();
if (!_homeController.tabController.indexIsChanging) {
_homeController.animateToTop();
}
},
),
),
)
else
const SizedBox(height: 6),
tabBar,
Expanded(
child: tabBarView(
controller: _homeController.tabController,
@@ -85,26 +93,43 @@ class _HomePageState extends State<HomePage>
],
);
if (_homeController.hideTopBar) {
return Obx(
() {
final barOffset = _mainController.barOffset!.value;
return CustomHeightWidget(
offset: Offset(0, -barOffset),
height: StyleString.topBarHeight - barOffset,
child: Padding(
if (_mainController.barOffset case final barOffset?) {
return Obx(
() {
final offset = barOffset.value;
return CustomHeightWidget(
offset: Offset(0, -offset),
height: StyleString.topBarHeight - offset,
child: Padding(
padding: padding,
child: child,
),
);
},
);
}
if (_homeController.showTopBar case final showTopBar?) {
return Obx(() {
final showSearchBar = showTopBar.value;
return AnimatedOpacity(
opacity: showSearchBar ? 1 : 0,
duration: const Duration(milliseconds: 300),
child: AnimatedContainer(
curve: Curves.easeInOutCubicEmphasized,
duration: const Duration(milliseconds: 500),
height: showSearchBar ? StyleString.topBarHeight : 0,
padding: padding,
child: child,
),
);
},
);
} else {
return Container(
height: StyleString.topBarHeight,
padding: padding,
child: child,
);
});
}
}
return Container(
height: StyleString.topBarHeight,
padding: padding,
child: child,
);
}
Widget searchBar(ThemeData theme) {

View File

@@ -31,7 +31,9 @@ class MainController extends GetxController
List<NavigationBarType> navigationBars = <NavigationBarType>[];
RxDouble? barOffset;
RxBool? showBottomBar;
late final bool hideBottomBar;
late final barHideType = Pref.barHideType;
late double navHeight = 80.0;
bool useBottomNav = false;
late dynamic controller;
@@ -86,8 +88,13 @@ class MainController extends GetxController
hideBottomBar =
!useSideBar && navigationBars.length > 1 && Pref.hideBottomBar;
if (hideBottomBar || homeController.hideTopBar) {
barOffset = RxDouble(0.0);
if (hideBottomBar) {
switch (barHideType) {
case .instant:
showBottomBar = RxBool(true);
case .sync:
barOffset ??= RxDouble(0.0);
}
}
dynamicBadgeMode = Pref.dynamicBadgeMode;
@@ -318,6 +325,12 @@ class MainController extends GetxController
}
}
void setSearchBar() {
if (hasHome) {
homeController.showTopBar?.value = true;
}
}
@override
void onClose() {
barOffset?.close();

View File

@@ -57,7 +57,9 @@ class _MainAppState extends PopScopeState<MainApp>
void didChangeDependencies() {
super.didChangeDependencies();
_padding = MediaQuery.viewPaddingOf(context);
_mainController.navHeight = 80.0 + _padding.bottom;
if (_mainController.hideBottomBar && _mainController.barHideType == .sync) {
_mainController.navHeight = 80.0 + _padding.bottom;
}
final brightness = Theme.brightnessOf(context);
NetworkImgLayer.reduce =
NetworkImgLayer.reduceLuxColor != null && brightness.isDark;
@@ -253,7 +255,9 @@ class _MainAppState extends PopScopeState<MainApp>
if (_mainController.selectedIndex.value != 0) {
_mainController
..setIndex(0)
..barOffset?.value = 0.0;
..barOffset?.value = 0.0
..showBottomBar?.value = true
..setSearchBar();
} else {
_onBack();
}
@@ -300,14 +304,26 @@ class _MainAppState extends PopScopeState<MainApp>
)
: null;
if (bottomNav != null && _mainController.hideBottomBar) {
return Obx(
() => CustomHeightWidget(
height:
_mainController.navHeight *
(1 - _mainController.barOffset!.value / StyleString.topBarHeight),
child: bottomNav,
),
);
if (_mainController.barOffset case final barOffset?) {
return Obx(
() => CustomHeightWidget(
height:
_mainController.navHeight *
(1 - barOffset.value / StyleString.topBarHeight),
child: bottomNav,
),
);
}
if (_mainController.showBottomBar case final showBottomBar?) {
return Obx(
() => AnimatedSlide(
curve: Curves.easeInOutCubicEmphasized,
duration: const Duration(milliseconds: 500),
offset: Offset(0, showBottomBar.value ? 0 : 1),
child: bottomNav,
),
);
}
}
return bottomNav;
}

View File

@@ -45,11 +45,19 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
_mainController.selectedIndex.value == 0;
@override
bool onNotification(ScrollNotification notification) {
bool onNotificationType1(UserScrollNotification notification) {
if (checkPage) {
return false;
}
return super.onNotification(notification);
return super.onNotificationType1(notification);
}
@override
bool onNotificationType2(ScrollNotification notification) {
if (checkPage) {
return false;
}
return super.onNotificationType2(notification);
}
@override

View File

@@ -8,6 +8,7 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/scale_app.dart';
import 'package:PiliPlus/common/widgets/stateful_builder.dart';
import 'package:PiliPlus/main.dart';
import 'package:PiliPlus/models/common/bar_hide_type.dart';
import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart';
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
import 'package:PiliPlus/models/common/home_tab_type.dart';
@@ -178,6 +179,12 @@ List<SettingsModel> get styleSettings => [
getSubtitle: () =>
'当前消息类型:${Pref.msgUnReadTypeV2.map((item) => item.title).join('')}',
),
NormalModel(
onTap: _showBarHideTypeDialog,
title: '顶/底栏收起类型',
leading: const Icon(MdiIcons.arrowExpandVertical),
getSubtitle: () => '当前:${Pref.barHideType.label}',
),
SwitchModel(
title: '首页顶栏收起',
subtitle: '首页列表滑动时,收起顶栏',
@@ -884,3 +891,22 @@ Future<void> _showDefHomeDialog(
setState();
}
}
Future<void> _showBarHideTypeDialog(
BuildContext context,
VoidCallback setState,
) async {
final res = await showDialog<BarHideType>(
context: context,
builder: (context) => SelectDialog<BarHideType>(
title: '顶/底栏收起类型',
value: Pref.barHideType,
values: BarHideType.values.map((e) => (e, e.label)).toList(),
),
);
if (res != null) {
await GStorage.setting.put(SettingBoxKey.barHideType, res.index);
SmartDialog.showToast('重启生效');
setState();
}
}

View File

@@ -220,6 +220,7 @@ abstract final class SettingBoxKey {
enableMYBar = 'enableMYBar',
hideTopBar = 'hideSearchBar',
hideBottomBar = 'hideTabBar',
barHideType = 'barHideType',
tabBarSort = 'tabBarSort',
dynamicBadgeMode = 'dynamicBadgeMode',
msgBadgeMode = 'msgBadgeMode',

View File

@@ -3,6 +3,7 @@ import 'dart:math' show pow, sqrt;
import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/models/common/bar_hide_type.dart';
import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart';
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
@@ -672,6 +673,12 @@ abstract final class Pref {
defaultValue: PlatformUtils.isMobile,
);
static BarHideType get barHideType =>
BarHideType.values[_setting.get(
SettingBoxKey.barHideType,
defaultValue: BarHideType.sync.index,
)];
static bool get enableSearchWord =>
_setting.get(SettingBoxKey.enableSearchWord, defaultValue: false);