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( context: context, maxWidth: 780, maxHeight: 620, compactHeightFactor: 0.92, builder: (_) => SettingsPage(initialTab: initialTab), ); } Future showSettingsSectionSheet( BuildContext context, { required String initialTab, }) { return showAdaptiveModal( 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 createState() => _SettingsPageState(); } class _SettingsPageState extends ConsumerState { 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)), }; } }