Skip to content

Commit

Permalink
Merge pull request #295 from enrique-lozano/feat/theme-dropdown
Browse files Browse the repository at this point in the history
New theme dropdown and setting icons animations
  • Loading branch information
enrique-lozano authored Jan 9, 2025
2 parents 4c72b1c + 46a4129 commit d72fcfa
Show file tree
Hide file tree
Showing 11 changed files with 276 additions and 111 deletions.
148 changes: 59 additions & 89 deletions lib/app/settings/appearance_settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import 'package:monekin/app/settings/widgets/monekin_tile_switch.dart';
import 'package:monekin/app/settings/widgets/supported_locales.dart';
import 'package:monekin/core/database/services/user-setting/private_mode_service.dart';
import 'package:monekin/core/database/services/user-setting/user_setting_service.dart';
import 'package:monekin/core/database/services/user-setting/utils/get_theme_from_string.dart';
import 'package:monekin/core/extensions/color.extensions.dart';
import 'package:monekin/core/presentation/animations/scaled_animated_switcher.dart';
import 'package:monekin/core/presentation/theme.dart';
import 'package:monekin/core/presentation/widgets/color_picker/color_picker.dart';
import 'package:monekin/core/presentation/widgets/color_picker/color_picker_modal.dart';
import 'package:monekin/core/presentation/widgets/monekin_dropdown_select.dart';
import 'package:monekin/i18n/translations.g.dart';

import '../../core/presentation/app_colors.dart';
Expand All @@ -33,70 +37,6 @@ class SelectItem<T> {
}

class _AdvancedSettingsPageState extends State<AdvancedSettingsPage> {
Widget buildSelector<T>({
required String title,
String? dialogDescr,
required List<SelectItem<T>> items,
required T selected,
required void Function(T newValue) onChanged,
}) {
SelectItem<T> selectedItem =
items.firstWhere((element) => element.value == selected);

return ListTile(
title: Text(title),
subtitle: Text(selectedItem.label),
leading: Icon(Icons.light_mode),
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title),
if (dialogDescr != null) ...[
const SizedBox(height: 8),
Text(
dialogDescr,
style: Theme.of(context).textTheme.labelMedium,
)
]
],
),
contentPadding: const EdgeInsets.only(top: 12),
content: SingleChildScrollView(
child: StatefulBuilder(builder: (context, alertState) {
return Column(
children: items
.map(
(item) => RadioListTile(
title: Text(item.label),
value: item.value,
groupValue: selected,
onChanged: (newValue) {
if (newValue != null && newValue != selected) {
onChanged(newValue);
selected = newValue;
}
},
),
)
.toList());
})),
actions: [
TextButton(
child: Text(t.general.cancel),
onPressed: () {
Navigator.pop(context);
},
),
],
),
);
});
}

@override
Widget build(BuildContext context) {
final t = Translations.of(context);
Expand Down Expand Up @@ -155,33 +95,32 @@ class _AdvancedSettingsPageState extends State<AdvancedSettingsPage> {
),
createListSeparator(context, t.settings.theme_and_colors),
StreamBuilder(
stream: UserSettingService.instance
.getSettingFromDB(SettingKey.themeMode),
builder: (context, snapshot) {
return buildSelector(
title: t.settings.theme,
items: [
SelectItem(value: 'system', label: t.settings.theme_auto),
SelectItem(value: 'light', label: t.settings.theme_light),
SelectItem(value: 'dark', label: t.settings.theme_dark)
stream: UserSettingService.instance
.getSettingFromDB(SettingKey.themeMode),
builder: (context, snapshot) {
final theme = getThemeFromString(snapshot.data);

return ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(t.settings.theme)),
const SizedBox(width: 12),
Flexible(child: _buildThemeDropdown(theme))
],
selected: snapshot.data ?? 'system',
onChanged: (value) {
UserSettingService.instance
.setItem(
SettingKey.themeMode,
value,
updateGlobalState: true,
)
.then((value) => null);
},
);
}),
),
leading: ScaledAnimatedSwitcher(
keyToWatch: theme.icon(context).toString(),
child: Icon(theme.icon(context)),
),
);
},
),
MonekinTileSwitch(
title: t.settings.amoled_mode,
subtitle: t.settings.amoled_mode_descr,
initialValue: appStateSettings[SettingKey.amoledMode] == '1',
disabled: Theme.of(context).brightness == Brightness.light,
disabled: isAppInLightBrightness(context),
onSwitchDebounceMs: 200,
onSwitch: (bool value) async {
await UserSettingService.instance.setItem(
Expand Down Expand Up @@ -265,7 +204,7 @@ class _AdvancedSettingsPageState extends State<AdvancedSettingsPage> {
MonekinTileSwitch(
title: t.settings.security.private_mode_at_launch,
subtitle: t.settings.security.private_mode_at_launch_descr,
icon: Icons.phonelink_lock_outlined,
icon: const Icon(Icons.phonelink_lock_outlined),
initialValue:
appStateSettings[SettingKey.privateModeAtLaunch] == '1',
onSwitch: (bool value) async {
Expand All @@ -275,11 +214,18 @@ class _AdvancedSettingsPageState extends State<AdvancedSettingsPage> {
StreamBuilder(
stream: PrivateModeService.instance.privateModeStream,
builder: (context, snapshot) {
final initialValue = (snapshot.data ?? false);

return MonekinTileSwitch(
title: t.settings.security.private_mode,
subtitle: t.settings.security.private_mode_descr,
icon: Icons.lock,
initialValue: snapshot.data ?? false,
icon: ScaledAnimatedSwitcher(
keyToWatch: initialValue.toString(),
child: Icon(initialValue
? Icons.lock_outline_rounded
: Icons.lock_open_rounded),
),
initialValue: initialValue,
onSwitch: (bool value) {
setState(() {
PrivateModeService.instance.setPrivateMode(value);
Expand All @@ -292,4 +238,28 @@ class _AdvancedSettingsPageState extends State<AdvancedSettingsPage> {
),
);
}

Widget _buildThemeDropdown(ThemeMode theme) {
return LayoutBuilder(builder: (context, constraints) {
return MonekinDropdownSelect(
initial: theme,
compact: true,
expanded: false,
items: const [
ThemeMode.system,
ThemeMode.light,
ThemeMode.dark,
],
getLabel: (x) => x.displayName(context),
onChanged: (mode) {
UserSettingService.instance
.setItem(
SettingKey.themeMode,
mode.name,
updateGlobalState: true,
)
.then((value) => null);
});
});
}
}
4 changes: 2 additions & 2 deletions lib/app/settings/widgets/monekin_tile_switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class MonekinTileSwitch extends StatefulWidget {
final String title;
final String? subtitle;
final bool initialValue;
final IconData? icon;
final Widget? icon;

/// Callback triggered when the switch value changes.
///
Expand Down Expand Up @@ -98,7 +98,7 @@ class _MonekinTileSwitchState extends State<MonekinTileSwitch> {
return SwitchListTile(
title: Text(widget.title),
subtitle: widget.subtitle == null ? null : Text(widget.subtitle!),
secondary: widget.icon == null ? null : Icon(widget.icon),
secondary: widget.icon,
value: value,
onChanged: widget.disabled
? null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'package:flutter/material.dart';
import 'package:monekin/core/presentation/theme.dart';
import 'package:monekin/i18n/translations.g.dart';

ThemeMode getThemeFromString(String? themeString) {
if (themeString == null) return ThemeMode.system;

ThemeMode getThemeFromString(String themeString) {
Map<String, ThemeMode> themeSetting = {
'system': ThemeMode.system,
'light': ThemeMode.light,
Expand All @@ -9,3 +13,31 @@ ThemeMode getThemeFromString(String themeString) {

return themeSetting[themeString] ?? ThemeMode.system;
}

extension ThemeModeExt on ThemeMode {
String displayName(BuildContext context) {
final t = Translations.of(context);

switch (this) {
case ThemeMode.system:
return t.settings.theme_auto;
case ThemeMode.light:
return t.settings.theme_light;
case ThemeMode.dark:
return t.settings.theme_dark;
}
}

IconData icon(BuildContext context) {
switch (this) {
case ThemeMode.system:
return isAppInDarkBrightness(context)
? Icons.dark_mode
: Icons.light_mode_rounded;
case ThemeMode.light:
return Icons.light_mode_rounded;
case ThemeMode.dark:
return Icons.dark_mode;
}
}
}
Loading

0 comments on commit d72fcfa

Please sign in to comment.