fluffychat/lib/widgets/theme_builder.dart
The one with the braid f1a43dfb45 feat: implement scoped dynamic colors
- implement scoped theme builder with seed controller
- apply scoped seed to room view, room details and profile preview
- in case dynamic theme is selected, use user profile picture as global
  color seed
- adjust localication from system color to dynamic color

Further reading :
https://m3.material.io/styles/color/dynamic/content-based-source

Signed-off-by: The one with the braid <info@braid.business>
2023-11-24 12:13:59 +01:00

116 lines
3.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
typedef FluffyThemeBuilder = Widget Function(
BuildContext context,
ThemeMode themeMode,
Color?,
);
class ThemeBuilder extends StatefulWidget {
final FluffyThemeBuilder builder;
final String themeModeSettingsKey;
final String primaryColorSettingsKey;
const ThemeBuilder({
required this.builder,
this.themeModeSettingsKey = 'theme_mode',
this.primaryColorSettingsKey = 'primary_color',
super.key,
});
@override
State<ThemeBuilder> createState() => ThemeController();
}
class ThemeController extends State<ThemeBuilder> {
SharedPreferences? _sharedPreferences;
ThemeMode? _themeMode;
Color? _primaryColor;
/// caching if ever set based on the profile pic
Color? _profileThemeSeed;
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
Color? get primaryColor => _primaryColor;
/// Sets the primaryColor at runtime
/// This won't store it but should rather be used for temporary theme changes
/// E.g. used for the profile picture based theme
///
/// In case a custom theme is selected by the user, this call is ignored
set profileThemeSeed(Color? color) => _profileThemeSeed = color;
static ThemeController of(BuildContext context) =>
Provider.of<ThemeController>(
context,
listen: false,
);
void _loadData(_) async {
final preferences =
_sharedPreferences ??= await SharedPreferences.getInstance();
final rawThemeMode = preferences.getString(widget.themeModeSettingsKey);
final rawColor = preferences.getInt(widget.primaryColorSettingsKey);
setState(() {
_themeMode = ThemeMode.values
.singleWhereOrNull((value) => value.name == rawThemeMode);
_primaryColor = rawColor == null ? null : Color(rawColor);
});
}
Future<void> setThemeMode(ThemeMode newThemeMode) async {
final preferences =
_sharedPreferences ??= await SharedPreferences.getInstance();
await preferences.setString(widget.themeModeSettingsKey, newThemeMode.name);
setState(() {
_themeMode = newThemeMode;
});
}
Future<void> setPrimaryColor(Color? newPrimaryColor) async {
final preferences =
_sharedPreferences ??= await SharedPreferences.getInstance();
if (newPrimaryColor == null) {
await preferences.remove(widget.primaryColorSettingsKey);
} else {
await preferences.setInt(
widget.primaryColorSettingsKey,
newPrimaryColor.value,
);
}
setState(() {
_primaryColor = newPrimaryColor;
});
}
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_loadData);
super.initState();
}
@override
Widget build(BuildContext context) {
return Provider(
create: (_) => this,
child: DynamicColorBuilder(
builder: (light, _) => widget.builder(
context,
themeMode,
primaryColor ?? _profileThemeSeed ?? light?.primary,
),
),
);
}
}