{"id":296,"date":"2021-06-28T14:43:41","date_gmt":"2021-06-28T14:43:41","guid":{"rendered":"https:\/\/erdbeerbeet.com\/?p=296"},"modified":"2021-09-06T13:41:22","modified_gmt":"2021-09-06T13:41:22","slug":"my-flutter-flight-welcome-to-the-dark-mode","status":"publish","type":"post","link":"https:\/\/erdbeerbeet.com\/de\/my-flutter-flight-welcome-to-the-dark-mode","title":{"rendered":"Meine Flutter-Fahrt &#8211; Der Dunkel-Modus"},"content":{"rendered":"\n<p>My app recently received a dark mode, followed by the request for a switch to turn it on and off.<br>It was a question of finding the best example and taking care of all the edge cases, so let me share my result.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The problem<\/h2>\n\n\n\n<p>By default, the theme defined in the MyApp class is used. By setting themeMode to ThemeMode.system, one can use the mode selected by the device. But I might want to use the light mode despite my device having dark mode as default. Additionally, I don&#8217;t want to restart the app to see the change.<\/p>\n\n\n\n<p>Another problem that popped up later is the case, that the user wants to save the mode switch and use the app in the selected mode on the next start.<br>I updated this post with my answer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The provider solution<\/h2>\n\n\n\n<p>My main source to success was this <a href=\"https:\/\/www.youtube.com\/watch?v=HSAa9yi0OMA\">YouTube Video by Johannes Milke<\/a> and his git repository. I will include my relevant parts from the app. Note that I inserted the normal and dark theme directly into the app &#8211; although Johannes solution is much smoother.<\/p>\n\n\n\n<p>Here is the relevant part from <strong>main.dart<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class MyApp extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) =&gt; ChangeNotifierProvider(\n      create: (context) =&gt; ThemeProvider(),\n      builder: (context, _){\n    final themeProvider = Provider.of&lt;ThemeProvider&gt;(context);\n\n    return new MaterialApp(\n\n        title: 'My light and dark app',\n        theme: ThemeData(\n          brightness: Brightness.light,\n          primaryColor: Color.fromRGBO(40, 105, 40, 1),\n          accentColor: Colors.black,\n          fontFamily: 'Raleway',\n        ),\n        darkTheme: ThemeData(\n          brightness: Brightness.dark,\n          primaryColor: Color.fromRGBO(100, 255, 218, 1),\n          accentColor: Colors.white,\n          fontFamily: 'Raleway',\n        ),\n        themeMode: themeProvider.themeMode,\n    );\n  });\n}\n\nclass _MyHomePageState extends State&lt;MyHomePage&gt; with WidgetsBindingObserver {\n\n  Preferences prefs;\n\n  @override\n void initState() {\n   super.initState();\n   prefs = new Preferences();\n     await prefs.load();\n     Provider.of&lt;ThemeProvider&gt;(context, listen: false).initThemeProvider();\n   } on Exception catch (e) {\n     \/\/ TODO handle exception\n   }\n }\n}<\/code><\/pre>\n\n\n\n<p>And my <strong>theme_provider.dart<\/strong>. I choose to ask for the light instead the dark theme to be more positive&#8230; or for no particular reason. When the app is initialized, or the theme is toggled, the selected mode should be stored in the sharedPreferences.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import 'package:flutter\/material.dart';\nimport 'package:flutter\/scheduler.dart';\n\nclass ThemeProvider extends ChangeNotifier {\n    Preferences prefs = Preferences();\n    ThemeMode themeMode = ThemeMode.system;\n\n    void initThemeProvider() async {\n      themeMode = getThemeMode();\n      notifyListeners();\n    }\n\n    ThemeMode getThemeMode() {\n      if (prefs == null || prefs.isLightMode == null) return ThemeMode.system;\n      return prefs.isLightMode ? ThemeMode.light : ThemeMode.dark;\n    }\n\n    bool get isLightMode {\n      themeMode = getThemeMode();\n      return themeMode == ThemeMode.light;\n    }\n\n    void toggleTheme(bool isOn) {\n      prefs.setIsLightMode(isOn);\n      themeMode = getThemeMode();\n      notifyListeners();\n    }\n}<\/code><\/pre>\n\n\n\n<p>My switch is inside a ListTile within the settings page. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">final themeProvider = Provider.<em>of<\/em>&lt;ThemeProvider&gt;(context);<\/pre>\n\n\n\n<p>This has to be defined above.<br>Here is the tiles code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>new SwitchListTile(\n                  value: themeProvider.isLightMode,\n                  onChanged: (value) =&gt; Provider.of&lt;ThemeProvider&gt;(context, listen: false).toggleTheme(value),\n                  title: themeProvider.isLightMode ?\n                    Text(DemoLocalizations.of(context).trans('light_mode')) :\n                    Text(DemoLocalizations.of(context).trans('dark_mode')),\n              ),<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The additional coding<\/h2>\n\n\n\n<p>I use a utils class to define a unified background image. To switch it according to the selected mode, all I need is to get the provider from the build context.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static getBackgroundDecoration(BuildContext context) {\n    final themeProvider = Provider.of&lt;ThemeProvider&gt;(context);\n    return BoxDecoration(\n      image: new DecorationImage(\n        image: themeProvider.isLightMode ?\n          new AssetImage(\"resources\/images\/pergament20.png\") :\n          new AssetImage(\"resources\/images\/pergament20_dark.png\"),\n        fit: BoxFit.fill,\n      ),\n    );\n  }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The result<\/h2>\n\n\n\n<p>The mode switch is not immediate, but most of the time the user wont play around with dark and light mode to brag about a delay. It works like a charm, although I might have missed additional places where I have to switch the colors dynamically.<\/p>\n\n\n\n<p>On start, the app will fall back to the systems theme mode and append the saved theme with the provider initialization.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Browser, Desktop-Anwendungen und Apps k\u00f6nnen sich inzwischen allesamt dem DarkMode des Ger\u00e4ts unterwerfen. Und der Nutzer m\u00f6chte mit einem Schalter auch mitreden.<\/p>\n","protected":false},"author":1,"featured_media":384,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42,48,13,12],"tags":[16,30,31,14],"class_list":["post-296","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-app-programming","category-companero","category-flutter","category-programming","tag-app","tag-companero","tag-dark-mode","tag-flutter"],"translation":{"provider":"WPGlobus","version":"3.0.0","language":"de","enabled_languages":["en","de"],"languages":{"en":{"title":true,"content":true,"excerpt":true},"de":{"title":true,"content":true,"excerpt":true}}},"_links":{"self":[{"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/posts\/296","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/comments?post=296"}],"version-history":[{"count":9,"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/posts\/296\/revisions"}],"predecessor-version":[{"id":388,"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/posts\/296\/revisions\/388"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/media\/384"}],"wp:attachment":[{"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/media?parent=296"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/categories?post=296"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/erdbeerbeet.com\/de\/wp-json\/wp\/v2\/tags?post=296"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}