new: custom tabs (#889)

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-09-03 01:05:03 +08:00
committed by GitHub
parent 2466341999
commit e51804fa70
36 changed files with 601 additions and 67 deletions

View File

@@ -8,6 +8,7 @@ extension _App on _AppSettingsPageState {
_buildThemeMode(),
_buildAppColor(),
_buildCheckUpdate(),
_buildHomeTabs(),
PlatformPublicSettings.buildBioAuth,
if (specific != null) specific,
_buildAppMore(),
@@ -274,4 +275,15 @@ extension _App on _AppSettingsPageState {
trailing: StoreSwitch(prop: _setting.hideTitleBar),
);
}
Widget _buildHomeTabs() {
return ListTile(
leading: const Icon(Icons.tab),
title: Text(l10n.homeTabs),
trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () {
HomeTabsConfigPage.route.go(context);
},
);
}
}

View File

@@ -0,0 +1,130 @@
import 'package:fl_lib/fl_lib.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:server_box/core/extension/context/locale.dart';
import 'package:server_box/data/model/app/tab.dart';
import 'package:server_box/data/res/store.dart';
class HomeTabsConfigPage extends ConsumerStatefulWidget {
const HomeTabsConfigPage({super.key});
static final route = AppRouteNoArg(page: HomeTabsConfigPage.new, path: '/settings/home-tabs');
@override
ConsumerState<HomeTabsConfigPage> createState() => _HomeTabsConfigPageState();
}
class _HomeTabsConfigPageState extends ConsumerState<HomeTabsConfigPage> {
final _availableTabs = AppTab.values;
var _selectedTabs = List<AppTab>.from(Stores.setting.homeTabs.fetch());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: Text(l10n.homeTabs),
actions: [
TextButton(onPressed: _resetToDefault, child: Text(libL10n.reset)),
TextButton(onPressed: _saveAndExit, child: Text(libL10n.save)),
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text(l10n.homeTabsCustomizeDesc, style: context.theme.textTheme.bodyMedium),
),
Expanded(
child: ReorderableListView.builder(
itemCount: _selectedTabs.length,
onReorder: _onReorder,
buildDefaultDragHandles: false,
itemBuilder: (context, index) {
final tab = _selectedTabs[index];
return _buildTabItem(tab, index, true);
},
),
),
const Divider(),
Padding(
padding: const EdgeInsets.all(16),
child: Text(l10n.availableTabs, style: context.theme.textTheme.titleMedium),
),
Expanded(
child: ListView.builder(
itemCount: _availableTabs.length,
itemBuilder: (context, index) {
final tab = _availableTabs[index];
if (_selectedTabs.contains(tab)) {
return const SizedBox.shrink();
}
return _buildTabItem(tab, index, false);
},
),
),
],
),
);
}
Widget _buildTabItem(AppTab tab, int index, bool isSelected) {
final canRemove = _selectedTabs.length > 1;
final child = ListTile(
leading: tab.navDestination.icon,
title: Text(tab.navDestination.label),
trailing: isSelected
? IconButton(
icon: const Icon(Icons.delete),
onPressed: canRemove ? () => _removeTab(tab) : null,
color: canRemove ? null : Theme.of(context).disabledColor,
tooltip: canRemove ? libL10n.delete : l10n.atLeastOneTab,
)
: IconButton(icon: const Icon(Icons.add), onPressed: () => _addTab(tab)),
onTap: isSelected && canRemove ? () => _removeTab(tab) : null,
);
return Card(
key: ValueKey(tab.name),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: isSelected ? ReorderableDragStartListener(index: index, child: child) : child,
);
}
void _onReorder(int oldIndex, int newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final tab = _selectedTabs.removeAt(oldIndex);
_selectedTabs.insert(newIndex, tab);
});
}
void _addTab(AppTab tab) {
setState(() {
_selectedTabs.add(tab);
});
}
void _removeTab(AppTab tab) {
if (_selectedTabs.length <= 1) {
context.showSnackBar(l10n.atLeastOneTab);
return;
}
setState(() {
_selectedTabs.remove(tab);
});
}
void _saveAndExit() {
Stores.setting.homeTabs.put(_selectedTabs);
context.pop();
}
void _resetToDefault() {
setState(() {
_selectedTabs = List<AppTab>.from(AppTab.values);
});
Stores.setting.homeTabs.put(_selectedTabs);
}
}

View File

@@ -18,6 +18,7 @@ import 'package:server_box/generated/l10n/l10n.dart';
import 'package:server_box/view/page/backup.dart';
import 'package:server_box/view/page/private_key/list.dart';
import 'package:server_box/view/page/server/connection_stats.dart';
import 'package:server_box/view/page/setting/entries/home_tabs.dart';
import 'package:server_box/view/page/setting/platform/android.dart';
import 'package:server_box/view/page/setting/platform/ios.dart';
import 'package:server_box/view/page/setting/platform/platform_pub.dart';