Reputation:
i'm trying to change theme from a switch widget.
but nothing works i have no errors, but there is no expected result
.
it works perfectly as expected when doing hot reload
when switch is on and do hot reload it switch to dark theme
when switch is off and do hot reload it switch to light theme
this is the code to look at
import 'package:flutter/material.dart';
mixin Th {
static ThemeMode themeMode = ThemeMode.dark;
static ThemeData themeData = ThemeData.dark();
static bool isDark = false;
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
static String title = 'Hello Flutter';
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(),
title: MyApp.title,
themeMode: Th.themeMode,
darkTheme: Th.themeData,
);
}
}
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(MyApp.title),
actions: [
Switch(
value: Th.isDark,
onChanged: (newValue) {
return setState(
() {
if (!newValue) {
Th.themeMode = ThemeMode.light;
Th.themeData = ThemeData.light();
Th.isDark = newValue;
print('Is Dark = ${Th.isDark}');
} else if (newValue) {
Th.themeMode = ThemeMode.dark;
Th.themeData = ThemeData.dark();
Th.isDark = newValue;
print('Is Dark = ${Th.isDark}');
}
},
);
},
),
],
),
);
}
}
print run in consle with the expected result
what is issue here? and thanks for reading.
tested on flutter for desktop 'Windows App' and flutter for mobile 'android app'
Upvotes: 0
Views: 642
Reputation: 2272
The reason is that setState only changes the UI of that page only,
since you have defined themes in material app you need to use provider package to make changes in the whole app UI.
First of all create a dart file to notify listeners about theme and put the following code in it:-
import 'package:flutter/material.dart';
class ThemeNotifier with ChangeNotifier {
ThemeData _themeData;
ThemeNotifier(this._themeData);
getTheme() => _themeData; // to get current theme of the app
setTheme(ThemeData themeData) async {
_themeData = themeData;
notifyListeners(); // to update the theme of the app
}
}
Add Shared prefs plugin in your pubspec.yaml file
Now, lets work on the void main. So, lets create a function to get the current theme of the device, if you don't have any value of the current theme/mode in database. For example, if the user have just installed the app then you don't know the theme choice of the user then it would be great if you provide theme based on the users mobile theme.
getSystemTheme()
{
var brightness = SchedulerBinding.instance.window.platformBrightness;
bool darkModeOn = brightness == Brightness.dark;
return darkModeOn;
}
Above function will return boolean value whether dark theme is on or
Now lets fetch the value from the local database and provide theme accordingly.
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values).then((_) {
SharedPreferences.getInstance().then((prefs) {
var darkModeOn = prefs.getBool('isDarkTheme') ?? getSystemTheme(); //if have value in database then that theme else the system theme will be used
runApp(
ChangeNotifierProvider<ThemeNotifier>(
create: (_) => ThemeNotifier(darkModeOn ? MyThemes.darktheme : MyThemes.lightTheme),
child: MyApp(),
),
);
});
});
Now the code for MyApp() widget:-
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final themeNotifier = Provider.of<ThemeNotifier>(context);
return Consumer<ThemeNotifier>(
builder: (context, appState, child) {
return MaterialApp(
theme: themeNotifier.getTheme(),
home: HomeScreen(),
);
},
);
}
}
If you want to give your user option to change theme manually then you can create a switch for it and do following updates to change theme dynamically(without restarting the app).:-
class Settings extends StatefulWidget {
@override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
bool _darkTheme=false;
void toggleSwitch(bool value, ThemeNotifier themeNotifier) async {
(value)
? themeNotifier.setTheme(AppTheme.darktheme)
: themeNotifier.setTheme(AppTheme.lighttheme);//here change of theme will take place
var prefs = await SharedPreferences.getInstance();
prefs.setBool('isDarkTheme', value); // and this code will save the boolean value whether darkmode is on or off to the local storage.
}
@override
Widget build(BuildContext context) {
final themeNotifier = Provider.of<ThemeNotifier>(context);
_darkTheme = (themeNotifier.getTheme() == MyThemes.darktheme);
return Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(child: Text("Dark Mode",style: TextStyle(fontSize: 18),)),
Switch(
value: _darkTheme,
onChanged: (val) {
setState(() {
_darkTheme = val;
});
toggleSwitch(val, themeNotifier);
},
),
],
)
);
}
}
P.S.:- You can remove shared preferences but to store the value of theme chosen by the user so that whenever he/she restarts the app then theme is maintained, you should add it..
Upvotes: 0