part of '../settings_page.dart'; /// System / Light / Dark, mirroring desktop's appearance-mode selector. const _modeOptions = <({ThemeMode mode, IconData icon})>[ (mode: ThemeMode.system, icon: LucideIcons.sunMoon), (mode: ThemeMode.light, icon: LucideIcons.sun), (mode: ThemeMode.dark, icon: LucideIcons.moon), ]; String _modeLabel(BuildContext context, ThemeMode mode) => switch (mode) { ThemeMode.system => context.l10n.modeSystem, ThemeMode.light => context.l10n.modeLight, ThemeMode.dark => context.l10n.modeDark, }; class _AppearanceSection extends ConsumerWidget { const _AppearanceSection(); @override Widget build(BuildContext context, WidgetRef ref) { final preference = ref.watch(communityThemeProvider); final mode = preference.mode; final schemeName = preference.theme; final accentIndex = effectiveAccentIndex( preference.theme, preference.accent, ); return AppListCard( label: context.l10n.appearanceStyle, children: [ AppListRow( icon: LucideIcons.sunMoon, title: context.l10n.appearance, value: _modeLabel(context, mode), trailing: const _RowChevron(), onTap: () => _showAppearanceModeSheet(context), ), AppListRow( icon: LucideIcons.palette, title: context.l10n.theme, value: themeSelectionLabel(schemeName, mode), trailing: const _RowChevron(), onTap: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ThemePickerPage()), ), ), if (!isBuzzTheme(effectiveTheme(schemeName, mode)?.name ?? schemeName)) AppListRow( icon: LucideIcons.droplet, title: context.l10n.accentColor, // The swatch *is* the value — naming the color as well would say the // same thing twice, so it takes the chevron's place. trailing: _AccentSwatch(accentIndex: accentIndex), onTap: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => const AccentPickerPage()), ), ), AppListRow( icon: LucideIcons.languages, title: context.l10n.language, value: _languageLabel(context, ref.watch(appLocaleProvider)), trailing: const _RowChevron(), onTap: () => _showLanguageSheet(context), ), ], ); } } void _showAppearanceModeSheet(BuildContext context) { showBuzzModalBottomSheet( context: context, showDragHandle: true, builder: (_) => const _AppearanceModeSheet(), ); } /// Three choices is too few to warrant a page, so the appearance row opens a /// sheet rather than pushing a route. class _AppearanceModeSheet extends ConsumerWidget { const _AppearanceModeSheet(); @override Widget build(BuildContext context, WidgetRef ref) { final mode = ref.watch(communityThemeProvider).mode; return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB( Grid.gutter, 0, Grid.gutter, Grid.xxs, ), child: Text( context.l10n.appearance, style: context.textTheme.titleMedium, ), ), for (final option in _modeOptions) AppListRow( icon: option.icon, title: _modeLabel(context, option.mode), trailing: option.mode == mode ? Icon( LucideIcons.check, size: 18, color: context.colors.primary, ) : null, onTap: () { ref.read(communityThemeProvider.notifier).setMode(option.mode); Navigator.of(context).pop(); }, ), const SizedBox(height: Grid.xxs), ], ), ); } } String _languageLabel(BuildContext context, Locale? locale) { if (locale == null) return context.l10n.languageSystem; return locale.languageCode == 'zh' ? context.l10n.languageChinese : context.l10n.languageEnglish; } void _showLanguageSheet(BuildContext context) { showBuzzModalBottomSheet( context: context, showDragHandle: true, builder: (_) => const _LanguageSheet(), ); } class _LanguageSheet extends ConsumerWidget { const _LanguageSheet(); @override Widget build(BuildContext context, WidgetRef ref) { final selected = ref.watch(appLocaleProvider); final options = <({Locale? locale, String label})>[ (locale: null, label: context.l10n.languageSystem), (locale: const Locale('zh', 'CN'), label: context.l10n.languageChinese), (locale: const Locale('en'), label: context.l10n.languageEnglish), ]; return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB( Grid.gutter, 0, Grid.gutter, Grid.xxs, ), child: Text( context.l10n.language, style: context.textTheme.titleMedium, ), ), for (final option in options) AppListRow( icon: LucideIcons.languages, title: option.label, trailing: option.locale == selected ? Icon( LucideIcons.check, size: 18, color: context.colors.primary, ) : null, onTap: () { ref.read(appLocaleProvider.notifier).setLocale(option.locale); Navigator.of(context).pop(); }, ), const SizedBox(height: Grid.xxs), ], ), ); } } /// The selected accent, shown where a row would otherwise carry a chevron. class _AccentSwatch extends StatelessWidget { const _AccentSwatch({required this.accentIndex}); static const _size = 22.0; final int accentIndex; @override Widget build(BuildContext context) { return Container( width: _size, height: _size, decoration: BoxDecoration( color: accentColorForScheme(context.colors, accentIndex), shape: BoxShape.circle, border: Border.all(color: context.colors.outlineVariant), ), ); } }