diff --git a/lib/modules/more/settings/appearance/appearance_screen.dart b/lib/modules/more/settings/appearance/appearance_screen.dart index c52d914d..53fdfd23 100644 --- a/lib/modules/more/settings/appearance/appearance_screen.dart +++ b/lib/modules/more/settings/appearance/appearance_screen.dart @@ -27,17 +27,173 @@ final navigationItems = { "/more": "More", }; +class SettingsSection extends StatelessWidget { + final String title; + final List children; + + const SettingsSection({ + super.key, + required this.title, + required this.children, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Text( + title, + style: TextStyle(fontSize: 13, color: context.primaryColor), + ), + ), + ...children, + ], + ), + ); + } +} + class AppearanceScreen extends ConsumerWidget { const AppearanceScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = l10nLocalizations(context); - final dateFormatState = ref.watch(dateFormatStateProvider); - final relativeTimestamps = ref.watch(relativeTimesTampsStateProvider); final pureBlackDarkMode = ref.watch(pureBlackDarkModeStateProvider); final isDarkTheme = ref.watch(themeModeStateProvider); + bool followSystemTheme = ref.watch(followSystemThemeStateProvider); + return Scaffold( + appBar: AppBar(title: Text(l10n!.appearance)), + body: SingleChildScrollView( + child: Column( + children: [ + SettingsSection( + title: l10n.theme, + children: [ + const FollowSystemThemeButton(), + if (!followSystemTheme) const DarkModeButton(), + const ThemeSelector(), + if (isDarkTheme) + SwitchListTile( + title: Text(l10n.pure_black_dark_mode), + value: pureBlackDarkMode, + onChanged: (value) { + ref + .read(pureBlackDarkModeStateProvider.notifier) + .set(value); + }, + ), + if (!pureBlackDarkMode || !isDarkTheme) + const BlendLevelSlider(), + ], + ), + SettingsSection( + title: l10n.appearance, + children: [ + _buildLanguageTile(context, ref, l10n), + _buildFontTile(context, ref, l10n), + ListTile( + title: Text(l10n.reorder_navigation), + subtitle: Text( + l10n.reorder_navigation_description, + style: TextStyle( + fontSize: 11, + color: context.secondaryColor, + ), + ), + onTap: () { + context.push("/customNavigationSettings"); + }, + ), + ], + ), + SettingsSection( + title: l10n.timestamp, + children: [ + _buildRelativeTimestampTile(context, ref, l10n), + _buildDateFormatTile(context, ref, l10n), + ], + ), + ], + ), + ), + ); + } + + Widget _buildLanguageTile( + BuildContext context, + WidgetRef ref, + AppLocalizations l10n, + ) { final l10nLocale = ref.watch(l10nLocaleStateProvider); + return ListTile( + title: Text(l10n.app_language), + subtitle: Text( + completeLanguageName(l10nLocale.toLanguageTag()), + style: TextStyle(fontSize: 11, color: context.secondaryColor), + ), + onTap: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(l10n.app_language), + content: SizedBox( + width: context.width(0.8), + child: SuperListView.builder( + shrinkWrap: true, + itemCount: AppLocalizations.supportedLocales.length, + itemBuilder: (context, index) { + final locale = AppLocalizations.supportedLocales[index]; + return RadioListTile( + dense: true, + contentPadding: const EdgeInsets.all(0), + value: locale, + groupValue: l10nLocale, + onChanged: (value) { + ref + .read(l10nLocaleStateProvider.notifier) + .setLocale(locale); + Navigator.pop(context); + }, + title: Text(completeLanguageName(locale.toLanguageTag())), + ); + }, + ), + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () async { + Navigator.pop(context); + }, + child: Text( + l10n.cancel, + style: TextStyle(color: context.primaryColor), + ), + ), + ], + ), + ], + ); + }, + ); + }, + ); + } + + Widget _buildFontTile( + BuildContext context, + WidgetRef ref, + AppLocalizations l10n, + ) { final appFontFamily = ref.watch(appFontFamilyProvider); final appFontFamilySub = appFontFamily == null @@ -48,486 +204,278 @@ class AppearanceScreen extends ConsumerWidget { (element) => element.value().fontFamily! == appFontFamily, ) .key; - bool followSystemTheme = ref.watch(followSystemThemeStateProvider); - return Scaffold( - appBar: AppBar(title: Text(l10n!.appearance)), - body: SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Row( + return ListTile( + title: Text(context.l10n.font), + subtitle: Text( + appFontFamilySub, + style: TextStyle(fontSize: 11, color: context.secondaryColor), + ), + onTap: () { + String textValue = ""; + final controller = ScrollController(); + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(context.l10n.font), + content: StatefulBuilder( + builder: (context, setState) { + return SizedBox( + width: context.width(0.8), + child: Column( children: [ - Text( - l10n.theme, - style: TextStyle( - fontSize: 13, - color: context.primaryColor, + Padding( + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 8, ), - ), - ], - ), - ), - const FollowSystemThemeButton(), - if (!followSystemTheme) const DarkModeButton(), - const ThemeSelector(), - if (isDarkTheme) - Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: SwitchListTile( - title: Text(l10n.pure_black_dark_mode), - value: pureBlackDarkMode, - onChanged: (value) { - ref - .read(pureBlackDarkModeStateProvider.notifier) - .set(value); - }, - ), - ), - if (!pureBlackDarkMode || !isDarkTheme) - const BlendLevelSlider(), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Row( - children: [ - Text( - l10n.appearance, - style: TextStyle( - fontSize: 13, - color: context.primaryColor, - ), - ), - ], - ), - ), - ListTile( - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(l10n.app_language), - content: SizedBox( - width: context.width(0.8), - child: SuperListView.builder( - shrinkWrap: true, - itemCount: - AppLocalizations.supportedLocales.length, - itemBuilder: (context, index) { - final locale = - AppLocalizations.supportedLocales[index]; - return RadioListTile( - dense: true, - contentPadding: const EdgeInsets.all(0), - value: locale, - groupValue: l10nLocale, - onChanged: (value) { - ref - .read( - l10nLocaleStateProvider.notifier, - ) - .setLocale(locale); - Navigator.pop(context); - }, - title: Text( - completeLanguageName( - locale.toLanguageTag(), - ), - ), - ); - }, + child: TextField( + onChanged: (v) { + setState(() { + textValue = v; + }); + }, + decoration: InputDecoration( + isDense: true, + filled: false, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: context.secondaryColor, + ), ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: context.primaryColor, + ), + ), + border: const OutlineInputBorder( + borderSide: BorderSide(), + ), + hintText: l10n.search, ), - actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () async { - Navigator.pop(context); - }, - child: Text( - l10n.cancel, - style: TextStyle( - color: context.primaryColor, - ), - ), - ), - ], - ), - ], - ); - }, - ); - }, - title: Text(l10n.app_language), - subtitle: Text( - completeLanguageName(l10nLocale.toLanguageTag()), - style: TextStyle( - fontSize: 11, - color: context.secondaryColor, - ), - ), - ), - ListTile( - onTap: () { - String textValue = ""; - final controller = ScrollController(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(context.l10n.font), - content: StatefulBuilder( - builder: (context, setState) { - return SizedBox( - width: context.width(0.8), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 8, - ), - child: TextField( - onChanged: (v) { - setState(() { - textValue = v; - }); - }, - decoration: InputDecoration( - isDense: true, - filled: false, - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.secondaryColor, - ), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor, - ), - ), - border: const OutlineInputBorder( - borderSide: BorderSide(), - ), - hintText: l10n.search, - ), - ), - ), - Builder( - builder: (context) { - List values = - GoogleFonts.asMap().entries - .toList(); - values = - values - .where( - (values) => values.key - .toLowerCase() - .contains( - textValue - .toLowerCase(), - ), + ), + ), + Builder( + builder: (context) { + List values = GoogleFonts.asMap().entries.toList(); + values = + values + .where( + (values) => values.key + .toLowerCase() + .contains(textValue.toLowerCase()), + ) + .toList(); + return Flexible( + child: Scrollbar( + interactive: true, + thickness: 12, + radius: const Radius.circular(10), + controller: controller, + child: CustomScrollView( + controller: controller, + slivers: [ + SliverPadding( + padding: const EdgeInsets.all(0), + sliver: SuperSliverList.builder( + itemCount: values.length, + itemBuilder: (context, index) { + final value = values[index]; + return RadioListTile( + dense: true, + contentPadding: + const EdgeInsets.all(0), + value: value.value().fontFamily, + groupValue: appFontFamily, + onChanged: (value) { + ref + .read( + appFontFamilyProvider + .notifier, ) - .toList(); - return Flexible( - child: Scrollbar( - interactive: true, - thickness: 12, - radius: const Radius.circular(10), - controller: controller, - child: CustomScrollView( - controller: controller, - slivers: [ - SliverPadding( - padding: - const EdgeInsets.all(0), - sliver: SuperSliverList.builder( - itemCount: values.length, - itemBuilder: ( - context, - index, - ) { - final value = - values[index]; - return RadioListTile( - dense: true, - contentPadding: - const EdgeInsets.all( - 0, - ), - value: - value - .value() - .fontFamily, - groupValue: - appFontFamily, - onChanged: (value) { - ref - .read( - appFontFamilyProvider - .notifier, - ) - .set(value); - Navigator.pop( - context, - ); - }, - title: Text( - value.key, - ), - ); - }, - ), - ), - ], - ), - ), + .set(value); + Navigator.pop(context); + }, + title: Text(value.key), ); }, ), - ], - ), - ); - }, - ), - actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () async { - ref - .read(appFontFamilyProvider.notifier) - .set(null); - Navigator.pop(context); - }, - child: Text( - l10n.default0, - style: TextStyle( - color: context.primaryColor, - ), ), - ), - TextButton( - onPressed: () async { - Navigator.pop(context); - }, - child: Text( - l10n.cancel, - style: TextStyle( - color: context.primaryColor, - ), - ), - ), - ], + ], + ), ), - ], - ); - }, - ); - }, - title: Text(context.l10n.font), - subtitle: Text( - appFontFamilySub, - style: TextStyle( - fontSize: 11, - color: context.secondaryColor, - ), - ), - ), - ListTile( - onTap: () { - context.push("/customNavigationSettings"); - }, - title: Text(l10n.reorder_navigation), - subtitle: Text( - l10n.reorder_navigation_description, - style: TextStyle( - fontSize: 11, - color: context.secondaryColor, - ), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Row( - children: [ - Text( - l10n.timestamp, - style: TextStyle( - fontSize: 13, - color: context.primaryColor, - ), + ); + }, ), ], ), - ), - ListTile( - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(l10n.relative_timestamp), - content: SizedBox( - width: context.width(0.8), - child: SuperListView.builder( - shrinkWrap: true, - itemCount: - relativeTimestampsList(context).length, - itemBuilder: (context, index) { - return RadioListTile( - dense: true, - contentPadding: const EdgeInsets.all(0), - value: index, - groupValue: relativeTimestamps, - onChanged: (value) { - ref - .read( - relativeTimesTampsStateProvider - .notifier, - ) - .set(value!); - Navigator.pop(context); - }, - title: Row( - children: [ - Text( - relativeTimestampsList( - context, - )[index], - ), - ], - ), - ); - }, - ), - ), - actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () async { - Navigator.pop(context); - }, - child: Text( - l10n.cancel, - style: TextStyle( - color: context.primaryColor, - ), - ), - ), - ], - ), - ], - ); - }, - ); - }, - title: Text(l10n.relative_timestamp), - subtitle: Text( - relativeTimestampsList(context)[relativeTimestamps], - style: TextStyle( - fontSize: 11, - color: context.secondaryColor, - ), - ), - ), - ListTile( - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(l10n.date_format), - content: SizedBox( - width: context.width(0.8), - child: SuperListView.builder( - shrinkWrap: true, - itemCount: dateFormatsList.length, - itemBuilder: (context, index) { - return RadioListTile( - dense: true, - contentPadding: const EdgeInsets.all(0), - value: dateFormatsList[index], - groupValue: dateFormatState, - onChanged: (value) { - ref - .read( - dateFormatStateProvider.notifier, - ) - .set(value!); - Navigator.pop(context); - }, - title: Row( - children: [ - Text( - "${dateFormatsList[index]} (${dateFormat(context: context, DateTime.now().millisecondsSinceEpoch.toString(), useRelativeTimesTamps: false, dateFormat: dateFormatsList[index], ref: ref)})", - ), - ], - ), - ); - }, - ), - ), - actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () async { - Navigator.pop(context); - }, - child: Text( - l10n.cancel, - style: TextStyle( - color: context.primaryColor, - ), - ), - ), - ], - ), - ], - ); - }, - ); - }, - title: Text(l10n.date_format), - subtitle: Text( - "$dateFormatState (${dateFormat(context: context, DateTime.now().millisecondsSinceEpoch.toString(), useRelativeTimesTamps: false, dateFormat: dateFormatState, ref: ref)})", - style: TextStyle( - fontSize: 11, - color: context.secondaryColor, - ), - ), - ), - ], + ); + }, ), - ), - ], - ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () async { + ref.read(appFontFamilyProvider.notifier).set(null); + Navigator.pop(context); + }, + child: Text( + l10n.default0, + style: TextStyle(color: context.primaryColor), + ), + ), + TextButton( + onPressed: () async { + Navigator.pop(context); + }, + child: Text( + l10n.cancel, + style: TextStyle(color: context.primaryColor), + ), + ), + ], + ), + ], + ); + }, + ); + }, + ); + } + + Widget _buildRelativeTimestampTile( + BuildContext context, + WidgetRef ref, + AppLocalizations l10n, + ) { + final relativeTimestamps = ref.watch(relativeTimesTampsStateProvider); + return ListTile( + title: Text(l10n.relative_timestamp), + subtitle: Text( + relativeTimestampsList(context)[relativeTimestamps], + style: TextStyle(fontSize: 11, color: context.secondaryColor), ), + onTap: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(l10n.relative_timestamp), + content: SizedBox( + width: context.width(0.8), + child: SuperListView.builder( + shrinkWrap: true, + itemCount: relativeTimestampsList(context).length, + itemBuilder: (context, index) { + return RadioListTile( + dense: true, + contentPadding: const EdgeInsets.all(0), + value: index, + groupValue: relativeTimestamps, + onChanged: (value) { + ref + .read(relativeTimesTampsStateProvider.notifier) + .set(value!); + Navigator.pop(context); + }, + title: Row( + children: [ + Text(relativeTimestampsList(context)[index]), + ], + ), + ); + }, + ), + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () async { + Navigator.pop(context); + }, + child: Text( + l10n.cancel, + style: TextStyle(color: context.primaryColor), + ), + ), + ], + ), + ], + ); + }, + ); + }, + ); + } + + Widget _buildDateFormatTile( + BuildContext context, + WidgetRef ref, + AppLocalizations l10n, + ) { + final dateFormatState = ref.watch(dateFormatStateProvider); + return ListTile( + title: Text(l10n.date_format), + subtitle: Text( + "$dateFormatState (${dateFormat(context: context, DateTime.now().millisecondsSinceEpoch.toString(), useRelativeTimesTamps: false, dateFormat: dateFormatState, ref: ref)})", + style: TextStyle(fontSize: 11, color: context.secondaryColor), + ), + onTap: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(l10n.date_format), + content: SizedBox( + width: context.width(0.8), + child: SuperListView.builder( + shrinkWrap: true, + itemCount: dateFormatsList.length, + itemBuilder: (context, index) { + return RadioListTile( + dense: true, + contentPadding: const EdgeInsets.all(0), + value: dateFormatsList[index], + groupValue: dateFormatState, + onChanged: (value) { + ref.read(dateFormatStateProvider.notifier).set(value!); + Navigator.pop(context); + }, + title: Row( + children: [ + Text( + "${dateFormatsList[index]} (${dateFormat(context: context, DateTime.now().millisecondsSinceEpoch.toString(), useRelativeTimesTamps: false, dateFormat: dateFormatsList[index], ref: ref)})", + ), + ], + ), + ); + }, + ), + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () async { + Navigator.pop(context); + }, + child: Text( + l10n.cancel, + style: TextStyle(color: context.primaryColor), + ), + ), + ], + ), + ], + ); + }, + ); + }, ); } }