generated from dellevin/template
1
This commit is contained in:
@@ -86,27 +86,34 @@ class _MainContentPageState extends State<MainContentPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Consumer<AppProvider>(
|
||||||
children: [
|
builder: (context, provider, child) {
|
||||||
if (!Breakpoint.isDesktop(context)) ...[
|
final isDropdown = provider.homeModuleSwitchMode == 1;
|
||||||
_buildAppBar(context),
|
return Column(
|
||||||
_buildTabBar(context),
|
children: [
|
||||||
],
|
if (!Breakpoint.isDesktop(context)) ...[
|
||||||
Expanded(child: _buildTabContent()),
|
_buildAppBar(context, isDropdown),
|
||||||
],
|
if (!isDropdown) _buildTabBar(context),
|
||||||
|
],
|
||||||
|
Expanded(child: _buildTabContent()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── AppBar ──────────────────────────────────────────
|
// ─── AppBar ──────────────────────────────────────────
|
||||||
|
|
||||||
Widget _buildAppBar(BuildContext context) {
|
Widget _buildAppBar(BuildContext context, bool isDropdown) {
|
||||||
return Consumer<AppProvider>(
|
return Consumer<AppProvider>(
|
||||||
builder: (context, provider, child) {
|
builder: (context, provider, child) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
return AppBar(
|
return AppBar(
|
||||||
titleSpacing: 8,
|
titleSpacing: 8,
|
||||||
leadingWidth: 44,
|
leadingWidth: 44,
|
||||||
title: Text(_getAppBarTitle(provider)),
|
title: isDropdown
|
||||||
|
? _buildDropdownTitle(context, provider, colors)
|
||||||
|
: Text(_getAppBarTitle(provider)),
|
||||||
actionsPadding: const EdgeInsets.only(right: 4),
|
actionsPadding: const EdgeInsets.only(right: 4),
|
||||||
actions: [
|
actions: [
|
||||||
_buildCloudSyncButton(context),
|
_buildCloudSyncButton(context),
|
||||||
@@ -143,6 +150,65 @@ class _MainContentPageState extends State<MainContentPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildDropdownTitle(BuildContext context, AppProvider provider, ColorScheme colors) {
|
||||||
|
final tabs = _enabledTabs;
|
||||||
|
final safeIndex = _mapToEnabledTabIndex(provider.mainTabIndex).clamp(0, tabs.length - 1);
|
||||||
|
final currentLabel = tabs.isEmpty ? '主页' : tabs[safeIndex].label;
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () => _showModulePicker(context, tabs, safeIndex),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(_tabIcon(currentLabel), size: 20, color: colors.primary),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(currentLabel, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
Icon(Icons.arrow_drop_down, size: 22, color: colors.onSurface.withValues(alpha: 0.5)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showModulePicker(BuildContext context, List<_TabItem> tabs, int currentIndex) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final provider = context.read<AppProvider>();
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
backgroundColor: colors.surface,
|
||||||
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
builder: (ctx) => SafeArea(
|
||||||
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
|
Container(width: 36, height: 4, margin: const EdgeInsets.only(top: 12, bottom: 16),
|
||||||
|
decoration: BoxDecoration(color: colors.onSurface.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(2))),
|
||||||
|
for (int i = 0; i < tabs.length; i++) ...[
|
||||||
|
if (i > 0) Divider(height: 0.5, indent: 20, endIndent: 20, color: colors.outlineVariant),
|
||||||
|
_modulePickerItem(ctx, tabs[i], i, i == currentIndex, colors, provider),
|
||||||
|
],
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _modulePickerItem(BuildContext ctx, _TabItem tab, int idx, bool selected, ColorScheme colors, AppProvider provider) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
leading: Container(width: 36, height: 36,
|
||||||
|
decoration: BoxDecoration(color: colors.surfaceContainerHighest, borderRadius: BorderRadius.circular(10)),
|
||||||
|
child: Icon(_tabIcon(tab.label), size: 20, color: selected ? colors.primary : colors.onSurface.withValues(alpha: 0.6))),
|
||||||
|
title: Text(tab.label, style: TextStyle(fontSize: 14, fontWeight: selected ? FontWeight.w600 : FontWeight.w400, color: colors.onSurface)),
|
||||||
|
trailing: selected ? Icon(Icons.check, size: 20, color: colors.primary) : null,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(ctx);
|
||||||
|
_isTabTap = true;
|
||||||
|
_pageController.jumpToPage(idx);
|
||||||
|
provider.setMainTabIndex(tab.originalIndex);
|
||||||
|
Future.delayed(const Duration(milliseconds: 50), () => _isTabTap = false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
String _getAppBarTitle(AppProvider provider) {
|
String _getAppBarTitle(AppProvider provider) {
|
||||||
switch (provider.mainTabIndex) {
|
switch (provider.mainTabIndex) {
|
||||||
case -1: return '主页';
|
case -1: return '主页';
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class LayoutSettingsPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
||||||
final UserPrefs _userPrefs = UserPrefs();
|
final UserPrefs _userPrefs = UserPrefs();
|
||||||
|
int _homeModuleSwitchMode = 0;
|
||||||
int _noteLayout = 0;
|
int _noteLayout = 0;
|
||||||
int _movieLayout = 0;
|
int _movieLayout = 0;
|
||||||
int _bookLayout = 0;
|
int _bookLayout = 0;
|
||||||
@@ -28,6 +29,7 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_homeModuleSwitchMode = _userPrefs.homeModuleSwitchMode;
|
||||||
_noteLayout = _userPrefs.noteLayoutStyle;
|
_noteLayout = _userPrefs.noteLayoutStyle;
|
||||||
_movieLayout = _userPrefs.movieLayoutStyle;
|
_movieLayout = _userPrefs.movieLayoutStyle;
|
||||||
_bookLayout = _userPrefs.bookLayoutStyle;
|
_bookLayout = _userPrefs.bookLayoutStyle;
|
||||||
@@ -51,6 +53,13 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
body: ListView(
|
body: ListView(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||||
children: [
|
children: [
|
||||||
|
_buildCategoryTile(
|
||||||
|
icon: Icons.dashboard_outlined,
|
||||||
|
title: '主页模块显示方式',
|
||||||
|
color: colors.primary,
|
||||||
|
subtitle: _homeModuleSubtitle,
|
||||||
|
onTap: _showHomeModuleSheet,
|
||||||
|
),
|
||||||
_buildCategoryTile(
|
_buildCategoryTile(
|
||||||
icon: Icons.movie_outlined,
|
icon: Icons.movie_outlined,
|
||||||
title: '影视',
|
title: '影视',
|
||||||
@@ -84,6 +93,10 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get _homeModuleSubtitle {
|
||||||
|
return _homeModuleSwitchMode == 1 ? '顶部下拉切换' : '模块标签切换';
|
||||||
|
}
|
||||||
|
|
||||||
String get _movieSubtitle {
|
String get _movieSubtitle {
|
||||||
final parts = <String>[];
|
final parts = <String>[];
|
||||||
if (_movieWallMode) {
|
if (_movieWallMode) {
|
||||||
@@ -305,6 +318,49 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
|
|
||||||
// ─── 弹窗 ────────────────────────────────────────────────────────
|
// ─── 弹窗 ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void _showHomeModuleSheet() {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
backgroundColor: colors.surface,
|
||||||
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
builder: (ctx) => StatefulBuilder(
|
||||||
|
builder: (ctx, setSheetState) => SafeArea(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
|
_sheetHandle(colors),
|
||||||
|
_sheetTitle('主页模块显示方式', colors),
|
||||||
|
_sheetOptionRow(
|
||||||
|
label: '切换方式',
|
||||||
|
selected: _homeModuleSwitchMode,
|
||||||
|
options: const [
|
||||||
|
(0, Icons.tab_outlined, '模块标签'),
|
||||||
|
(1, Icons.arrow_drop_down_outlined, '顶部下拉'),
|
||||||
|
],
|
||||||
|
onChanged: (v) {
|
||||||
|
_userPrefs.setHomeModuleSwitchMode(v);
|
||||||
|
setState(() => _homeModuleSwitchMode = v);
|
||||||
|
if (mounted) context.read<AppProvider>().setHomeModuleSwitchMode(v);
|
||||||
|
setSheetState(() {});
|
||||||
|
},
|
||||||
|
colors: colors,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 4, 20, 16),
|
||||||
|
child: Text(
|
||||||
|
_homeModuleSwitchMode == 1
|
||||||
|
? 'AppBar 标题显示当前模块,点击弹出选择菜单。'
|
||||||
|
: '底部显示模块标签栏,点击切换。',
|
||||||
|
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.45)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _showMovieSheet() {
|
void _showMovieSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ class AppProvider extends ChangeNotifier {
|
|||||||
// 当前主界面选中的标签 (0: 观影,1: 阅读,2: 笔记)
|
// 当前主界面选中的标签 (0: 观影,1: 阅读,2: 笔记)
|
||||||
int _mainTabIndex = 0;
|
int _mainTabIndex = 0;
|
||||||
|
|
||||||
|
// 主页模块显示方式 (0=底部tab切换, 1=顶部下拉切换)
|
||||||
|
int _homeModuleSwitchMode = 0;
|
||||||
|
|
||||||
// 当前底部导航选中的索引 (0: 主页,1: 新增,2: 我的)
|
// 当前底部导航选中的索引 (0: 主页,1: 新增,2: 我的)
|
||||||
int _bottomNavIndex = 0;
|
int _bottomNavIndex = 0;
|
||||||
|
|
||||||
@@ -199,6 +202,7 @@ class AppProvider extends ChangeNotifier {
|
|||||||
_bookshelfMode = userPrefs.bookshelfMode;
|
_bookshelfMode = userPrefs.bookshelfMode;
|
||||||
_gameLayoutStyle = userPrefs.gameLayoutStyle;
|
_gameLayoutStyle = userPrefs.gameLayoutStyle;
|
||||||
_gameWallMode = userPrefs.gameWallMode;
|
_gameWallMode = userPrefs.gameWallMode;
|
||||||
|
_homeModuleSwitchMode = userPrefs.homeModuleSwitchMode;
|
||||||
final defaultIndex = userPrefs.defaultMainTabIndex;
|
final defaultIndex = userPrefs.defaultMainTabIndex;
|
||||||
// Windows 桌面端且主页开启时,强制默认为主页
|
// Windows 桌面端且主页开启时,强制默认为主页
|
||||||
if (Platform.isWindows && userPrefs.showDesktopHomeTab) {
|
if (Platform.isWindows && userPrefs.showDesktopHomeTab) {
|
||||||
@@ -297,6 +301,7 @@ class AppProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
int get mainTabIndex => _mainTabIndex;
|
int get mainTabIndex => _mainTabIndex;
|
||||||
|
int get homeModuleSwitchMode => _homeModuleSwitchMode;
|
||||||
int get bottomNavIndex => _bottomNavIndex;
|
int get bottomNavIndex => _bottomNavIndex;
|
||||||
Movie? get selectedMovie => _selectedMovie;
|
Movie? get selectedMovie => _selectedMovie;
|
||||||
Book? get selectedBook => _selectedBook;
|
Book? get selectedBook => _selectedBook;
|
||||||
@@ -340,6 +345,12 @@ class AppProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setHomeModuleSwitchMode(int mode) {
|
||||||
|
_homeModuleSwitchMode = mode;
|
||||||
|
UserPrefs().setHomeModuleSwitchMode(mode);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
void setBottomNavIndex(int index) {
|
void setBottomNavIndex(int index) {
|
||||||
if (index == 0 && _bottomNavIndex == 0) {
|
if (index == 0 && _bottomNavIndex == 0) {
|
||||||
// 已在首页,再次点击 → 回到顶部
|
// 已在首页,再次点击 → 回到顶部
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ class UserPrefs {
|
|||||||
bool get showGameTab => prefs.getBool('showGameTab') ?? false;
|
bool get showGameTab => prefs.getBool('showGameTab') ?? false;
|
||||||
Future<bool> setShowGameTab(bool value) => prefs.setBool('showGameTab', value);
|
Future<bool> setShowGameTab(bool value) => prefs.setBool('showGameTab', value);
|
||||||
|
|
||||||
|
/// 主页模块显示方式 (0=底部tab切换, 1=顶部下拉切换)
|
||||||
|
int get homeModuleSwitchMode => prefs.getInt('homeModuleSwitchMode') ?? 0;
|
||||||
|
Future<bool> setHomeModuleSwitchMode(int value) => prefs.setInt('homeModuleSwitchMode', value);
|
||||||
|
|
||||||
/// 默认启动标签 (-1: 主页, 0: 影视, 1: 阅读, 2: 笔记, 3: 游戏)
|
/// 默认启动标签 (-1: 主页, 0: 影视, 1: 阅读, 2: 笔记, 3: 游戏)
|
||||||
int get defaultMainTabIndex => prefs.getInt('defaultMainTabIndex') ?? -1;
|
int get defaultMainTabIndex => prefs.getInt('defaultMainTabIndex') ?? -1;
|
||||||
Future<bool> setDefaultMainTabIndex(int value) => prefs.setInt('defaultMainTabIndex', value);
|
Future<bool> setDefaultMainTabIndex(int value) => prefs.setInt('defaultMainTabIndex', value);
|
||||||
|
|||||||
Reference in New Issue
Block a user