Skip to content

Instantly share code, notes, and snippets.

@RodBr
Last active April 9, 2023 06:09
Show Gist options
  • Save RodBr/37310335c6639f486bb3c8a628052405 to your computer and use it in GitHub Desktop.
Save RodBr/37310335c6639f486bb3c8a628052405 to your computer and use it in GitHub Desktop.
Themes plus persistence
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart'; //STEP 4 - Import shared_preferences
void main() {
Get.lazyPut<ThemeController>(
() => ThemeController()); //STEP 5 - lazy create ThemeController
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
ThemeController.to
.getThemeModeFromPreferences(); //STEP 6 - Get saved theme at startup
return GetMaterialApp(
title: 'Theme change using Get',
theme: ThemeData.light().copyWith(primaryColor: Colors.green),
darkTheme: ThemeData.dark().copyWith(primaryColor: Colors.purple),
themeMode: ThemeMode.system,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ThemeMode _themeMode;
@override
Widget build(BuildContext context) {
_themeMode = ThemeController
.to.themeMode; //STEP 7 - get the theme from ThemeController
print('${MediaQuery.of(context).platformBrightness}');
print('${Theme.of(context).brightness}');
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: Text(
'System Brightness: ${Get.mediaQuery.platformBrightness.toString()}',
style: TextStyle(fontSize: 20),
),
),
SizedBox(height: 24),
Center(
child: Text(
'Theme Brightness: ${Get.theme.brightness.toString()}',
style: TextStyle(fontSize: 20),
),
),
SizedBox(height: 24),
Text(
'ThemeMode',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.left,
),
RadioListTile(
title: Text('system'),
value: ThemeMode.system,
groupValue: _themeMode,
onChanged: (value) {
setState(() {
_themeMode = value;
ThemeController.to
.setThemeMode(_themeMode); //STEP 8 - change this line
});
},
),
RadioListTile(
title: Text('dark'),
value: ThemeMode.dark,
groupValue: _themeMode,
onChanged: (value) {
setState(() {
_themeMode = value;
ThemeController.to.setThemeMode(_themeMode);
});
},
),
RadioListTile(
title: Text('light'),
value: ThemeMode.light,
groupValue: _themeMode,
onChanged: (value) {
setState(() {
_themeMode = value;
ThemeController.to.setThemeMode(_themeMode);
});
},
),
],
),
);
}
}
//STEP 9 - add our ThemeController
class ThemeController extends GetController {
static ThemeController get to => Get.find();
SharedPreferences prefs;
ThemeMode _themeMode;
ThemeMode get themeMode => _themeMode;
Future<void> setThemeMode(ThemeMode themeMode) async {
Get.changeThemeMode(themeMode);
_themeMode = themeMode;
update();
prefs = await SharedPreferences.getInstance();
await prefs.setString('theme', themeMode.toString().split('.')[1]);
}
getThemeModeFromPreferences() async {
ThemeMode themeMode;
prefs = await SharedPreferences.getInstance();
String themeText = prefs.getString('theme') ?? 'system';
try {
themeMode =
ThemeMode.values.firstWhere((e) => describeEnum(e) == themeText);
} catch (e) {
themeMode = ThemeMode.system;
}
setThemeMode(themeMode);
}
}
@justkawal
Copy link

Line Number 21 should be changed to:

themeMode: ThemeController.to.themeMode,

@Babsky
Copy link

Babsky commented Apr 1, 2021

That's awesome and so simple!
I do have one issue though - I have a settings route, changing the theme here does change the theme for the app and the option (radio) for the chosen theme is selected when you open the settings again. The problem occurs if the app is stopped and restarted (emulator), or after a restart (device). The _themeMode = ThemeController.to.themeMode; in the settings route returns null so no theme is selected in the settings route, although the app does use the theme that was last selected.

Main.dart:
`void main() {
Get.lazyPut(() => ThemeController()); //STEP 5 - lazy create ThemeController

ThemeController.to.getThemeModeFromPreferences(); //STEP 6 - Get saved theme at startup

runApp(GetMaterialApp(
translations: ClsText(),
locale: Get.deviceLocale,
debugShowCheckedModeBanner: false,
title: 'app name',
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeController.to.themeMode,`

Settingsroute:
`class _SettingsRouteState extends State {

ThemeMode _themeMode;

@OverRide
Widget build(BuildContext context) {
// get current theme mode
_themeMode = ThemeController.to.themeMode; // initially returns null so no radio is selected

1 of the 3 Radios on settings route (all 3 the same except the value): new Radio(
value: ThemeMode.system,
groupValue: _themeMode,
onChanged: (value) {
setState(() {
_themeMode = value;
ThemeController.to
.setThemeMode(_themeMode); //STEP 8 - change this line
});
},
),`

What am I missing? Please help

UPDATE: I didn't resolve this but I did simplify the settings option. I changed it to a Dark Mode on/off switch;
var _brightness = Theme.of(context).brightness; bool _darkModeOn = _brightness == Brightness.dark;

would be nice to have 3 options though

@cbotelho80
Copy link

The following LateError was thrown building MyApp(dirty):
LateInitializationError: Field '_themeMode' has not been initialized.
The relevant error-causing widget was:
MyApp
lib\main.dart:9
When the exception was thrown, this was the stack:
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49 throw
packages/getx_theme/main.dart 115:18 get [_themeMode]
Launching lib\main.dart on Chrome in debug mode...
lib\main.dart:1
This app is linked to the debug service: ws://127.0.0.1:63230/_nWvV3Z8uCw=/ws
Debug service listening on ws://127.0.0.1:63230/_nWvV3Z8uCw=/ws
Running with sound null safety
Connecting to VM Service at ws://127.0.0.1:63230/_nWvV3Z8uCw=/ws
Flutter Web Bootstrap: Programmatic
packages/getx_theme/main.dart 116:30 get themeMode
packages/getx_theme/main.dart 23:37 build
packages/flutter/src/widgets/framework.dart 4949:22 build
packages/flutter/src/widgets/framework.dart 4878:15 performRebuild
packages/flutter/src/widgets/framework.dart 4604:5 rebuild
packages/flutter/src/widgets/framework.dart 4859:5 [_firstBuild]
packages/flutter/src/widgets/framework.dart 4853:5 mount
packages/flutter/src/widgets/framework.dart 3863:15 inflateWidget
packages/flutter/src/widgets/framework.dart 3592:18 updateChild
packages/flutter/src/widgets/binding.dart 1195:16 [_rebuild]
packages/flutter/src/widgets/binding.dart 1164:5 mount
packages/flutter/src/widgets/binding.dart 1111:16
packages/flutter/src/widgets/framework.dart 2605:19 buildScope
packages/flutter/src/widgets/binding.dart 1110:12 attachToRenderTree
packages/flutter/src/widgets/binding.dart 944:24 attachRootWidget
packages/flutter/src/widgets/binding.dart 925:7
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/isolate_helper.dart 48:19 internalCallback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment