mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-28 13:20:16 +08:00
opt hide top/bottom bar
Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
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:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter/foundation.dart' show clampDouble;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -12,33 +12,17 @@ abstract class CommonPageState<
|
||||
extends State<T> {
|
||||
R get controller;
|
||||
final _mainController = Get.find<MainController>();
|
||||
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;
|
||||
RxDouble? _barOffset;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_showBottomBar = _mainController.showBottomBar;
|
||||
try {
|
||||
_showSearchBar = Get.find<HomeController>().showSearchBar;
|
||||
} catch (_) {}
|
||||
if (_enableScrollThreshold &&
|
||||
(_showBottomBar != null || _showSearchBar != null)) {
|
||||
_scrollController.addListener(listener);
|
||||
}
|
||||
_barOffset = _mainController.barOffset;
|
||||
}
|
||||
|
||||
Widget onBuild(Widget child) {
|
||||
if (!_enableScrollThreshold &&
|
||||
(_showBottomBar != null || _showSearchBar != null)) {
|
||||
return NotificationListener<UserScrollNotification>(
|
||||
if (_barOffset != null) {
|
||||
return NotificationListener<ScrollUpdateNotification>(
|
||||
onNotification: onNotification,
|
||||
child: child,
|
||||
);
|
||||
@@ -46,69 +30,29 @@ abstract class CommonPageState<
|
||||
return child;
|
||||
}
|
||||
|
||||
bool onNotification(UserScrollNotification notification) {
|
||||
if (notification.metrics.axis == .horizontal) return false;
|
||||
bool onNotification(ScrollUpdateNotification notification) {
|
||||
if (!_mainController.useBottomNav) return false;
|
||||
final direction = notification.direction;
|
||||
if (direction == .forward) {
|
||||
_showBottomBar?.value = true;
|
||||
_showSearchBar?.value = true;
|
||||
} else if (direction == .reverse) {
|
||||
_showBottomBar?.value = false;
|
||||
_showSearchBar?.value = false;
|
||||
|
||||
final metrics = notification.metrics;
|
||||
if (metrics.axis == .horizontal ||
|
||||
metrics.pixels < 0 ||
|
||||
notification.dragDetails == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final scrollDelta = notification.scrollDelta ?? 0.0;
|
||||
_barOffset!.value = clampDouble(
|
||||
_barOffset!.value + scrollDelta,
|
||||
0.0,
|
||||
StyleString.topBarHeight,
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void listener() {
|
||||
if (!_mainController.useBottomNav) return;
|
||||
final direction = _scrollController.position.userScrollDirection;
|
||||
|
||||
final double currentPosition = _scrollController.position.pixels;
|
||||
|
||||
// 初始化上次位置
|
||||
_lastScrollPosition ??= currentPosition;
|
||||
|
||||
// 计算滚动距离
|
||||
final double scrollDelta = currentPosition - _lastScrollPosition!;
|
||||
|
||||
if (direction == .reverse) {
|
||||
_showBottomBar?.value = false;
|
||||
_showSearchBar?.value = false; // // 向下滚动,累加向下滚动距离,重置向上滚动计数器
|
||||
_upScrollCount = 0.0; // 重置向上滚动计数器
|
||||
// if (scrollDelta > 0) {
|
||||
// _downScrollCount += scrollDelta;
|
||||
// // _upScrollCount = 0.0; // 重置向上滚动计数器
|
||||
|
||||
// // 当累计向下滚动距离超过阈值时,隐藏顶底栏
|
||||
// if (_downScrollCount >= _scrollThreshold) {
|
||||
// mainStream?.add(false);
|
||||
// searchBarStream?.add(false);
|
||||
// }
|
||||
// }
|
||||
} else if (direction == .forward) {
|
||||
// 向上滚动,累加向上滚动距离,重置向下滚动计数器
|
||||
if (scrollDelta < 0) {
|
||||
_upScrollCount -= scrollDelta; // 使用绝对值
|
||||
// _downScrollCount = 0.0; // 重置向下滚动计数器
|
||||
|
||||
// 当累计向上滚动距离超过阈值时,显示顶底栏
|
||||
if (_upScrollCount >= _scrollThreshold) {
|
||||
_showBottomBar?.value = true;
|
||||
_showSearchBar?.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新上次位置
|
||||
_lastScrollPosition = currentPosition;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_showSearchBar = null;
|
||||
_showBottomBar = null;
|
||||
_scrollController.removeListener(listener);
|
||||
_barOffset = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,21 +46,13 @@ class _DynamicsTabPageState
|
||||
_mainController.selectedIndex.value == 0;
|
||||
|
||||
@override
|
||||
bool onNotification(UserScrollNotification notification) {
|
||||
bool onNotification(ScrollUpdateNotification notification) {
|
||||
if (checkPage) {
|
||||
return false;
|
||||
}
|
||||
return super.onNotification(notification);
|
||||
}
|
||||
|
||||
@override
|
||||
void listener() {
|
||||
if (checkPage) {
|
||||
return;
|
||||
}
|
||||
super.listener();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
controller = Get.putOrFind(
|
||||
|
||||
@@ -19,7 +19,7 @@ class HomeController extends GetxController
|
||||
late List<HomeTabType> tabs;
|
||||
late TabController tabController;
|
||||
|
||||
RxBool? showSearchBar;
|
||||
final bool hideTopBar = !Pref.useSideBar && Pref.hideTopBar;
|
||||
|
||||
bool enableSearchWord = Pref.enableSearchWord;
|
||||
late final RxString defaultSearch = ''.obs;
|
||||
@@ -36,10 +36,6 @@ class HomeController extends GetxController
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
||||
if (!Pref.useSideBar && Pref.hideTopBar) {
|
||||
showSearchBar = true.obs;
|
||||
}
|
||||
|
||||
if (enableSearchWord) {
|
||||
lateCheckSearchAt = DateTime.now().millisecondsSinceEpoch;
|
||||
querySearchDefault();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_height_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/pages/home/controller.dart';
|
||||
@@ -36,30 +37,27 @@ class _HomePageState extends State<HomePage>
|
||||
MediaQuery.sizeOf(context).isPortrait)
|
||||
customAppBar(theme),
|
||||
if (_homeController.tabs.length > 1)
|
||||
Material(
|
||||
color: theme.colorScheme.surface,
|
||||
child: 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();
|
||||
}
|
||||
},
|
||||
),
|
||||
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();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -76,7 +74,6 @@ class _HomePageState extends State<HomePage>
|
||||
}
|
||||
|
||||
Widget customAppBar(ThemeData theme) {
|
||||
const height = 52.0;
|
||||
const padding = EdgeInsets.fromLTRB(14, 6, 14, 0);
|
||||
final child = Row(
|
||||
children: [
|
||||
@@ -87,24 +84,23 @@ class _HomePageState extends State<HomePage>
|
||||
userAvatar(theme: theme, mainController: _mainController),
|
||||
],
|
||||
);
|
||||
if (_homeController.showSearchBar case final searchBar?) {
|
||||
return Obx(() {
|
||||
final showSearchBar = searchBar.value;
|
||||
return AnimatedOpacity(
|
||||
opacity: showSearchBar ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: AnimatedContainer(
|
||||
curve: Curves.easeInOutCubicEmphasized,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
height: showSearchBar ? height : 0,
|
||||
padding: padding,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
});
|
||||
if (_homeController.hideTopBar) {
|
||||
return Obx(
|
||||
() {
|
||||
final barOffset = _mainController.barOffset!.value;
|
||||
return CustomHeightWidget(
|
||||
offset: Offset(0, -barOffset),
|
||||
height: StyleString.topBarHeight - barOffset,
|
||||
child: Padding(
|
||||
padding: padding,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
height: height,
|
||||
height: StyleString.topBarHeight,
|
||||
padding: padding,
|
||||
child: child,
|
||||
);
|
||||
|
||||
@@ -30,7 +30,9 @@ class MainController extends GetxController
|
||||
|
||||
List<NavigationBarType> navigationBars = <NavigationBarType>[];
|
||||
|
||||
RxBool? showBottomBar;
|
||||
RxDouble? barOffset;
|
||||
late final bool hideBottomBar;
|
||||
late double navHeight = 80.0;
|
||||
bool useBottomNav = false;
|
||||
late dynamic controller;
|
||||
final RxInt selectedIndex = 0.obs;
|
||||
@@ -82,9 +84,12 @@ class MainController extends GetxController
|
||||
)
|
||||
: PageController(initialPage: selectedIndex.value);
|
||||
|
||||
if (!useSideBar && navigationBars.length > 1 && Pref.hideBottomBar) {
|
||||
showBottomBar = true.obs;
|
||||
hideBottomBar =
|
||||
!useSideBar && navigationBars.length > 1 && Pref.hideBottomBar;
|
||||
if (hideBottomBar || homeController.hideTopBar) {
|
||||
barOffset = RxDouble(0.0);
|
||||
}
|
||||
|
||||
dynamicBadgeMode = Pref.dynamicBadgeMode;
|
||||
|
||||
hasDyn = navigationBars.contains(NavigationBarType.dynamics);
|
||||
@@ -313,15 +318,9 @@ class MainController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
void setSearchBar() {
|
||||
if (hasHome) {
|
||||
homeController.showSearchBar?.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
showBottomBar?.close();
|
||||
barOffset?.close();
|
||||
controller.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_height_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/tabs.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
@@ -35,6 +36,7 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
with RouteAware, WidgetsBindingObserver, WindowListener, TrayListener {
|
||||
final _mainController = Get.put(MainController());
|
||||
late final _setting = GStorage.setting;
|
||||
late EdgeInsets _padding;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -54,6 +56,8 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_padding = MediaQuery.viewPaddingOf(context);
|
||||
_mainController.navHeight = 80.0 + _padding.bottom;
|
||||
final brightness = Theme.brightnessOf(context);
|
||||
NetworkImgLayer.reduce =
|
||||
NetworkImgLayer.reduceLuxColor != null && brightness.isDark;
|
||||
@@ -249,8 +253,7 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
if (_mainController.selectedIndex.value != 0) {
|
||||
_mainController
|
||||
..setIndex(0)
|
||||
..showBottomBar?.value = true
|
||||
..setSearchBar();
|
||||
..barOffset?.value = 0.0;
|
||||
} else {
|
||||
_onBack();
|
||||
}
|
||||
@@ -297,12 +300,12 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
)
|
||||
: null;
|
||||
if (bottomNav != null) {
|
||||
if (_mainController.showBottomBar case final bottomBar?) {
|
||||
if (_mainController.barOffset case final bottomBarOffset?) {
|
||||
return Obx(
|
||||
() => AnimatedSlide(
|
||||
curve: Curves.easeInOutCubicEmphasized,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
offset: Offset(0, bottomBar.value ? 0 : 1),
|
||||
() => CustomHeightWidget(
|
||||
height:
|
||||
_mainController.navHeight *
|
||||
(1 - bottomBarOffset.value / StyleString.topBarHeight),
|
||||
child: bottomNav,
|
||||
),
|
||||
);
|
||||
@@ -381,8 +384,6 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final padding = MediaQuery.viewPaddingOf(context);
|
||||
|
||||
Widget child;
|
||||
if (_mainController.mainTabBarView) {
|
||||
child = CustomTabBarView(
|
||||
@@ -409,7 +410,7 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
_sideBar(theme),
|
||||
VerticalDivider(
|
||||
width: 1,
|
||||
endIndent: padding.bottom,
|
||||
endIndent: _padding.bottom,
|
||||
color: theme.colorScheme.outline.withValues(alpha: 0.06),
|
||||
),
|
||||
Expanded(child: child),
|
||||
@@ -423,8 +424,8 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
appBar: AppBar(toolbarHeight: 0),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: _mainController.useBottomNav ? padding.left : 0.0,
|
||||
right: padding.right,
|
||||
left: _mainController.useBottomNav ? _padding.left : 0.0,
|
||||
right: _padding.right,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
|
||||
@@ -45,21 +45,13 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
_mainController.selectedIndex.value == 0;
|
||||
|
||||
@override
|
||||
bool onNotification(UserScrollNotification notification) {
|
||||
bool onNotification(ScrollUpdateNotification notification) {
|
||||
if (checkPage) {
|
||||
return false;
|
||||
}
|
||||
return super.onNotification(notification);
|
||||
}
|
||||
|
||||
@override
|
||||
void listener() {
|
||||
if (checkPage) {
|
||||
return;
|
||||
}
|
||||
super.listener();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
|
||||
@@ -194,15 +194,6 @@ List<SettingsModel> get styleSettings => [
|
||||
defaultVal: PlatformUtils.isMobile,
|
||||
needReboot: true,
|
||||
),
|
||||
const SwitchModel(
|
||||
title: '顶/底栏滚动阈值',
|
||||
subtitle: '滚动多少像素后收起/展开顶底栏,默认50像素',
|
||||
leading: Icon(Icons.swipe_vertical),
|
||||
defaultVal: false,
|
||||
setKey: SettingBoxKey.enableScrollThreshold,
|
||||
needReboot: true,
|
||||
onTap: _showScrollDialog,
|
||||
),
|
||||
NormalModel(
|
||||
onTap: (context, setState) => _showQualityDialog(
|
||||
context: context,
|
||||
@@ -781,48 +772,6 @@ Future<void> _showMsgUnReadDialog(
|
||||
}
|
||||
}
|
||||
|
||||
void _showScrollDialog(BuildContext context) {
|
||||
String scrollThreshold = Pref.scrollThreshold.toString();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('滚动阈值'),
|
||||
content: TextFormField(
|
||||
autofocus: true,
|
||||
initialValue: scrollThreshold,
|
||||
keyboardType: const .numberWithOptions(decimal: true),
|
||||
onChanged: (value) => scrollThreshold = value,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[\d\.]+')),
|
||||
],
|
||||
decoration: const InputDecoration(suffixText: 'px'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: ColorScheme.of(context).outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
try {
|
||||
final val = double.parse(scrollThreshold);
|
||||
Get.back();
|
||||
GStorage.setting.put(SettingBoxKey.scrollThreshold, val);
|
||||
SmartDialog.showToast('重启生效');
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
},
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showReduceColorDialog(
|
||||
BuildContext context,
|
||||
VoidCallback setState,
|
||||
|
||||
Reference in New Issue
Block a user