Initial commit
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../shared/widgets/adaptive_modal.dart';
|
||||
import '../../shared/widgets/app_inline_alert.dart';
|
||||
import '../../shared/widgets/app_snack_bar.dart';
|
||||
import 'settings_tab_key.dart';
|
||||
import 'tabs/audio_settings_tab.dart';
|
||||
import 'tabs/danmaku_settings_tab.dart';
|
||||
import 'tabs/general_settings_tab.dart';
|
||||
import 'tabs/icon_library_settings_tab.dart';
|
||||
import 'tabs/modules_settings_tab.dart';
|
||||
import 'tabs/playback_settings_tab.dart';
|
||||
import 'tabs/tmdb_settings_tab.dart';
|
||||
import 'tabs/trakt_settings_tab.dart';
|
||||
|
||||
const _kSettingsTabMinWidth = 96.0;
|
||||
|
||||
void showSettingsDialog(BuildContext context, {String? initialTab}) {
|
||||
showAdaptiveModal<void>(
|
||||
context: context,
|
||||
maxWidth: 780,
|
||||
maxHeight: 620,
|
||||
compactHeightFactor: 0.92,
|
||||
builder: (_) => SettingsPage(initialTab: initialTab),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Future<void> showSettingsSectionSheet(
|
||||
BuildContext context, {
|
||||
required String initialTab,
|
||||
}) {
|
||||
return showAdaptiveModal<void>(
|
||||
context: context,
|
||||
maxWidth: 620,
|
||||
compactHeightFactor: 0.9,
|
||||
builder: (sheetContext) => _SettingsSectionSheet(tabKey: initialTab),
|
||||
);
|
||||
}
|
||||
|
||||
class SettingsPage extends ConsumerStatefulWidget {
|
||||
final String? initialTab;
|
||||
|
||||
const SettingsPage({super.key, this.initialTab});
|
||||
|
||||
@override
|
||||
ConsumerState<SettingsPage> createState() => _SettingsPageState();
|
||||
}
|
||||
|
||||
class _SettingsPageState extends ConsumerState<SettingsPage> {
|
||||
int _tabIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabIndex = settingsTabIndexFromKey(widget.initialTab);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant SettingsPage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.initialTab != oldWidget.initialTab) {
|
||||
final nextTabIndex = settingsTabIndexFromKey(widget.initialTab);
|
||||
if (nextTabIndex != _tabIndex) {
|
||||
setState(() {
|
||||
_tabIndex = nextTabIndex;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _showFeedback(String message, {AppAlertTone tone = AppAlertTone.info}) {
|
||||
showAppToast(context, message, tone: tone);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
final settingsBg = isDark
|
||||
? const Color(0xFF1C1C1E)
|
||||
: const Color(0xFFF9FAFB);
|
||||
|
||||
return Container(
|
||||
color: settingsBg,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(32, 20, 32, 12),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'设置',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final scrollable =
|
||||
constraints.maxWidth <
|
||||
settingsTabData.length * _kSettingsTabMinWidth;
|
||||
return FTabs(
|
||||
scrollable: scrollable,
|
||||
expands: true,
|
||||
control: FTabControl.lifted(
|
||||
index: _tabIndex,
|
||||
onChange: (index) {
|
||||
if (index == _tabIndex) return;
|
||||
setState(() => _tabIndex = index);
|
||||
},
|
||||
),
|
||||
children: [
|
||||
FTabEntry(
|
||||
label: _tabLabel(0, scrollable: scrollable),
|
||||
child: GeneralSettingsTab(onFeedback: _showFeedback),
|
||||
),
|
||||
FTabEntry(
|
||||
label: _tabLabel(1, scrollable: scrollable),
|
||||
child: const PlaybackSettingsTab(),
|
||||
),
|
||||
FTabEntry(
|
||||
label: _tabLabel(2, scrollable: scrollable),
|
||||
child: const AudioSettingsTab(),
|
||||
),
|
||||
FTabEntry(
|
||||
label: _tabLabel(3, scrollable: scrollable),
|
||||
child: const DanmakuSettingsTab(),
|
||||
),
|
||||
FTabEntry(
|
||||
label: _tabLabel(4, scrollable: scrollable),
|
||||
child: const ModulesSettingsTab(),
|
||||
),
|
||||
FTabEntry(
|
||||
label: _tabLabel(5, scrollable: scrollable),
|
||||
child: TmdbSettingsTab(onFeedback: _showFeedback),
|
||||
),
|
||||
FTabEntry(
|
||||
label: _tabLabel(6, scrollable: scrollable),
|
||||
child: TraktSettingsTab(onFeedback: _showFeedback),
|
||||
),
|
||||
FTabEntry(
|
||||
label: _tabLabel(7, scrollable: scrollable),
|
||||
child: const IconLibrarySettingsTab(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _tabLabel(int index, {required bool scrollable}) {
|
||||
final tab = settingsTabData[index];
|
||||
final label = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(tab.icon, size: 16),
|
||||
const SizedBox(width: 6),
|
||||
Text(tab.label),
|
||||
],
|
||||
);
|
||||
return scrollable
|
||||
? SizedBox(width: _kSettingsTabMinWidth, child: label)
|
||||
: label;
|
||||
}
|
||||
}
|
||||
|
||||
class _SettingsSectionSheet extends ConsumerWidget {
|
||||
final String tabKey;
|
||||
|
||||
const _SettingsSectionSheet({required this.tabKey});
|
||||
|
||||
void _showFeedback(
|
||||
BuildContext context,
|
||||
String message, {
|
||||
AppAlertTone tone = AppAlertTone.info,
|
||||
}) {
|
||||
showAppToast(context, message, tone: tone);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = Theme.of(context);
|
||||
void feedback(String msg, {AppAlertTone tone = AppAlertTone.info}) =>
|
||||
_showFeedback(context, msg, tone: tone);
|
||||
|
||||
final (title, body) = _resolveSection(context, feedback);
|
||||
|
||||
return Material(
|
||||
color: theme.colorScheme.surface,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 8, 8, 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
tooltip: '关闭',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Flexible(child: body),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
(String, Widget) _resolveSection(
|
||||
BuildContext context,
|
||||
void Function(String message, {AppAlertTone tone}) feedback,
|
||||
) {
|
||||
return switch (tabKey) {
|
||||
kSettingsTabGeneral => ('通用', GeneralSettingsTab(onFeedback: feedback)),
|
||||
kSettingsTabPlayback || kSettingsTabGestures => (
|
||||
'播放',
|
||||
const PlaybackSettingsTab(),
|
||||
),
|
||||
kSettingsTabAudio => ('音频', const AudioSettingsTab()),
|
||||
kSettingsTabDanmaku => ('弹幕', const DanmakuSettingsTab()),
|
||||
kSettingsTabModules => ('模块', const ModulesSettingsTab()),
|
||||
kSettingsTabTmdb => ('TMDB', TmdbSettingsTab(onFeedback: feedback)),
|
||||
kSettingsTabTrakt => ('Trakt', TraktSettingsTab(onFeedback: feedback)),
|
||||
kSettingsTabIconLibrary => ('图标库', const IconLibrarySettingsTab()),
|
||||
_ => ('设置', GeneralSettingsTab(onFeedback: feedback)),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user