Skip to content

Commit

Permalink
Refactored theme & structures (#248)
Browse files Browse the repository at this point in the history
- Removed [AppThemeMode], replaced with [ThemeMode]
- Renamed setOrRemove to setIfNullRemove
- Removed theme.dart file
- Updated home_screen.dart
  • Loading branch information
hawkkiller authored Sep 7, 2023
1 parent 6578bd1 commit 5fa7d98
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 168 deletions.
5 changes: 2 additions & 3 deletions lib/src/core/localization/translations/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"@appTitle": {
"description": "The title of the application"
},
"light_themes": "Light Themes",
"dark_themes": "Dark Themes",
"system_theme": "System Theme",
"custom_colors": "Custom Colors",
"default_themes": "Default Themes",
"locales": "Locales"
}
5 changes: 2 additions & 3 deletions lib/src/core/localization/translations/intl_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"@appTitle": {
"description": "El título de la aplicación"
},
"light_themes": "Tema claro",
"dark_themes": "Tema oscuro",
"system_theme": "Tema del sistema",
"custom_colors": "Colores personalizados",
"default_themes": "Temas predeterminados",
"locales": "Idioma"
}
20 changes: 0 additions & 20 deletions lib/src/core/theme/theme.dart

This file was deleted.

3 changes: 2 additions & 1 deletion lib/src/core/utils/preferences_dao.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ abstract base class PreferencesEntry<T extends Object> {
Future<void> remove();

/// Set the value of the entry in the preferences if the value is not null.
Future<void> setOrRemove(T? value) => value == null ? remove() : set(value);
Future<void> setIfNullRemove(T? value) =>
value == null ? remove() : set(value);
}

final class _PreferencesEntry<T extends Object> extends PreferencesEntry<T> {
Expand Down
65 changes: 58 additions & 7 deletions lib/src/feature/app/data/theme_datasource.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:ui';
import 'dart:convert';

import 'package:flutter/material.dart' show ThemeMode, Color;
import 'package:sizzle_starter/src/core/utils/preferences_dao.dart';
import 'package:sizzle_starter/src/feature/app/model/app_theme.dart';

Expand All @@ -25,14 +26,14 @@ final class ThemeDataSourceImpl extends PreferencesDao

PreferencesEntry<int> get _seedColor => intEntry('theme.seed_color');

PreferencesEntry<String> get _colorSchemeType => stringEntry(
'theme.color_scheme_type',
PreferencesEntry<String> get _themeMode => stringEntry(
'theme.mode',
);

@override
Future<void> setTheme(AppTheme theme) async {
await _seedColor.setOrRemove(theme.seed?.value);
await _colorSchemeType.setOrRemove(theme.type.toString());
await _seedColor.setIfNullRemove(theme.seed?.value);
await _themeMode.setIfNullRemove(_themeModeCodec.encode(theme.mode));

return;
}
Expand All @@ -41,13 +42,63 @@ final class ThemeDataSourceImpl extends PreferencesDao
AppTheme? loadThemeFromCache() {
final seedColor = _seedColor.read();

final type = _colorSchemeType.read();
final type = _themeMode.read();

if (type == null) return null;

return AppTheme(
seed: seedColor != null ? Color(seedColor) : null,
type: AppThemeMode.fromString(type),
mode: _themeModeCodec.decode(type),
);
}
}

const _themeModeCodec = _ThemeModeCodec();

final class _ThemeModeCodec extends Codec<ThemeMode, String> {
const _ThemeModeCodec();

@override
Converter<String, ThemeMode> get decoder => const _ThemeModeDecoder();

@override
Converter<ThemeMode, String> get encoder => const _ThemeModeEncoder();
}

final class _ThemeModeDecoder extends Converter<String, ThemeMode> {
const _ThemeModeDecoder();

@override
ThemeMode convert(String input) {
switch (input) {
case 'ThemeMode.dark':
return ThemeMode.dark;
case 'ThemeMode.light':
return ThemeMode.light;
case 'ThemeMode.system':
return ThemeMode.system;
default:
throw ArgumentError.value(
input,
'input',
'Cannot convert $input to ThemeMode',
);
}
}
}

final class _ThemeModeEncoder extends Converter<ThemeMode, String> {
const _ThemeModeEncoder();

@override
String convert(ThemeMode input) {
switch (input) {
case ThemeMode.dark:
return 'ThemeMode.dark';
case ThemeMode.light:
return 'ThemeMode.light';
case ThemeMode.system:
return 'ThemeMode.system';
}
}
}
155 changes: 47 additions & 108 deletions lib/src/feature/app/model/app_theme.dart
Original file line number Diff line number Diff line change
@@ -1,142 +1,81 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

/// {@template app_theme_type}
/// The type of theme to use.
/// {@endtemplate}
enum AppThemeMode {
/// Light theme.
light,

/// Dark theme.
dark,

/// System theme.
system;

/// Whether this is a system theme.
bool get isSystem => switch (this) {
AppThemeMode.system => true,
_ => false,
};

@override
String toString() => switch (this) {
AppThemeMode.light => 'light',
AppThemeMode.dark => 'dark',
AppThemeMode.system => 'system',
};

/// Creates a [AppThemeMode] from a [String].
static AppThemeMode fromString(String value) => switch (value) {
'light' => AppThemeMode.light,
'dark' => AppThemeMode.dark,
'system' => AppThemeMode.system,
_ => throw Exception('Unknown AppThemeMode: $value'),
};
}

/// {@template app_theme}
/// An immutable class that holds properties needed
/// to build a [ThemeData] for the app.
/// {@endtemplate}
@immutable
final class AppTheme with Diagnosticable {
/// The type of theme to use.
final AppThemeMode type;
final ThemeMode mode;

/// The seed color to generate the [ColorScheme] from.
final Color? seed;

/// {@macro app_theme}
const AppTheme({
required this.type,
AppTheme({
required this.mode,
this.seed,
});

/// Dark theme
static const dark = AppTheme(type: AppThemeMode.dark);

/// Light theme
static const light = AppTheme(type: AppThemeMode.light);

/// System theme
static const system = AppTheme(type: AppThemeMode.system);
}) : darkTheme = ThemeData(
colorSchemeSeed: seed ?? Colors.pink,
brightness: Brightness.dark,
useMaterial3: true,
),
lightTheme = ThemeData(
colorSchemeSeed: seed ?? Colors.pink,
brightness: Brightness.light,
useMaterial3: true,
);

/// Light mode [AppTheme].
static final light = AppTheme(mode: ThemeMode.light);

/// Dark mode [AppTheme].
static final dark = AppTheme(mode: ThemeMode.dark);

/// System mode [AppTheme].
static final system = AppTheme(mode: ThemeMode.system);

/// All the light [AppTheme]s.
static final lightValues = [
...List.generate(
Colors.primaries.length,
(index) => AppTheme(
seed: Colors.primaries[index],
type: AppThemeMode.light,
),
),
];

/// All the dark [AppTheme]s.
static final darkValues = [
static final values = [
...List.generate(
Colors.primaries.length,
(index) => AppTheme(
seed: Colors.primaries[index],
type: AppThemeMode.dark,
mode: ThemeMode.system,
),
),
];

/// Get the dark [ThemeData] for this [AppTheme].
ThemeData get darkTheme {
if (seed != null) {
return ThemeData(
colorSchemeSeed: seed,
brightness: Brightness.dark,
useMaterial3: true,
);
}

// Define there your default dark theme.
return ThemeData(
brightness: Brightness.dark,
useMaterial3: true,
colorSchemeSeed: Colors.pink,
);
}

/// Get the light [ThemeData] for this [AppTheme].
ThemeData get lightTheme {
if (seed != null) {
return ThemeData(
colorSchemeSeed: seed,
brightness: Brightness.light,
useMaterial3: true,
);
}

// Define there your default light theme.
return ThemeData(
brightness: Brightness.light,
useMaterial3: true,
colorSchemeSeed: Colors.pink,
);
}

/// Get the [ThemeMode] for this [AppTheme].
ThemeMode get themeMode {
if (type == AppThemeMode.system) {
return ThemeMode.system;
} else if (type == AppThemeMode.light) {
return ThemeMode.light;
} else {
return ThemeMode.dark;
/// The dark [ThemeData] for this [AppTheme].
final ThemeData darkTheme;

/// The light [ThemeData] for this [AppTheme].
final ThemeData lightTheme;

/// The [ThemeData] for this [AppTheme].
/// This is computed based on the [mode].
///
/// Could be useful for theme showcase.
ThemeData computeTheme(BuildContext context) {
switch (mode) {
case ThemeMode.light:
return lightTheme;
case ThemeMode.dark:
return darkTheme;
case ThemeMode.system:
return MediaQuery.platformBrightnessOf(context) == Brightness.dark
? darkTheme
: lightTheme;
}
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ColorProperty('seed', seed));
properties.add(EnumProperty<AppThemeMode>('type', type));
properties.add(EnumProperty<ThemeMode>('type', mode));
properties.add(DiagnosticsProperty<ThemeData>('lightTheme', lightTheme));
properties.add(DiagnosticsProperty<ThemeData>('darkTheme', darkTheme));
}
Expand All @@ -147,8 +86,8 @@ final class AppTheme with Diagnosticable {
other is AppTheme &&
runtimeType == other.runtimeType &&
seed == other.seed &&
type == other.type;
mode == other.mode;

@override
int get hashCode => type.hashCode ^ seed.hashCode;
int get hashCode => mode.hashCode ^ seed.hashCode;
}
2 changes: 1 addition & 1 deletion lib/src/feature/app/widget/material_context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class _MaterialContextState extends State<MaterialContext> {
debugShowCheckedModeBanner: false,
theme: theme.lightTheme,
darkTheme: theme.darkTheme,
themeMode: theme.themeMode,
themeMode: theme.mode,
localizationsDelegates: Localization.localizationDelegates,
supportedLocales: Localization.supportedLocales,
locale: LocaleScope.of(context).locale,
Expand Down
Loading

0 comments on commit 5fa7d98

Please sign in to comment.