mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-20 03:06:59 +08:00
@@ -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,
|
||||
|
||||
11
lib/models/common/bar_hide_type.dart
Normal file
11
lib/models/common/bar_hide_type.dart
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +220,7 @@ abstract final class SettingBoxKey {
|
||||
enableMYBar = 'enableMYBar',
|
||||
hideTopBar = 'hideSearchBar',
|
||||
hideBottomBar = 'hideTabBar',
|
||||
barHideType = 'barHideType',
|
||||
tabBarSort = 'tabBarSort',
|
||||
dynamicBadgeMode = 'dynamicBadgeMode',
|
||||
msgBadgeMode = 'msgBadgeMode',
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user