Skip to content

Instantly share code, notes, and snippets.

@etherealHero
Created May 18, 2024 15:28
Show Gist options
  • Save etherealHero/919fd006d7e8afc88814e7f4c0b19b84 to your computer and use it in GitHub Desktop.
Save etherealHero/919fd006d7e8afc88814e7f4c0b19b84 to your computer and use it in GitHub Desktop.
Flutter ThemeProvider with persistent storage
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
runApp(App(
themeController: ThemeController(prefs),
));
}
class App extends StatelessWidget {
const App({
super.key,
required ThemeController themeController,
}) : _themeController = themeController;
final ThemeController _themeController;
@override
Widget build(BuildContext context) {
return ThemeProvider(
controller: _themeController,
builder: (context, _) => MaterialApp(
darkTheme: ThemeData(brightness: Brightness.dark),
themeMode: _themeController.currentTheme,
title: "ThemeProvider",
home: const Scaffold(body: Center(child: ChoiceTheme())),
),
);
}
}
class ThemeController extends ChangeNotifier {
static const themePrefKey = 'theme';
ThemeController(this._prefs) {
_currentTheme = switch (_prefs.getString(themePrefKey)) {
'system' => ThemeMode.system,
'light' => ThemeMode.light,
'dark' => ThemeMode.dark,
_ => ThemeMode.system,
};
}
final SharedPreferences _prefs;
late ThemeMode _currentTheme;
ThemeMode get currentTheme => _currentTheme;
void setTheme(ThemeMode theme) {
_currentTheme = theme;
_prefs.setString(themePrefKey, theme.name);
notifyListeners();
}
static ThemeController of(BuildContext context) {
final provider = context.getInheritedWidgetOfExactType<ThemeProvider>();
return (provider as ThemeProvider).controller;
}
}
class ThemeProvider extends InheritedWidget {
ThemeProvider({
super.key,
required this.controller,
required Widget Function(BuildContext, Widget?) builder,
}) : super(child: AnimatedBuilder(animation: controller, builder: builder));
final ThemeController controller;
@override
bool updateShouldNotify(ThemeProvider oldWidget) =>
controller != oldWidget.controller;
}
class ChoiceTheme extends StatefulWidget {
const ChoiceTheme({super.key});
@override
State<ChoiceTheme> createState() => _ChoiceThemeState();
}
class _ChoiceThemeState extends State<ChoiceTheme> {
@override
Widget build(BuildContext context) {
return SegmentedButton(
segments: <ButtonSegment<ThemeMode>>[
ButtonSegment(
value: ThemeMode.light, label: Text(ThemeMode.light.name)),
ButtonSegment(
value: ThemeMode.system, label: Text(ThemeMode.system.name)),
ButtonSegment(value: ThemeMode.dark, label: Text(ThemeMode.dark.name)),
],
selected: {ThemeController.of(context).currentTheme},
onSelectionChanged: (Set<ThemeMode> newSelection) => setState(() {
ThemeController.of(context).setTheme(newSelection.first);
}),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment