ArthurEKing
ArthurEKing

Reputation: 156

Changing iOS App theme automatically between light and dark depending on iOS environmental theme

Okay, So I've done a fair bit of research on this particular topic previously, and I'm aware that it is a feature that the Flutter devs have not yet implemented to function automatically (setting the light and dark themes to change dynamically checking upon opening the app) but I know it IS possible. I don't want my users to need to make this choice, and I know I'm close, but I'm missing something important. I'll explain:

final Brightness brightnessValue = MediaQuery.of(context).platformBrightness;
bool isDark = brightnessValue == Brightness.dark;

Those two lines of code SHOULD pass through a boolean (dark theme true or false) into my code. Those lines of code ARE running, but they throw an error.

MediaQuery.of() called with a context that does not contain a MediaQuery. No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of(). This can happen because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.). The context used was: MyApp see also: https://flutter.dev/docs/testing/errors

Now I've seen some people implement this line of code and it works for them, so I know it's not a syntax thing. And I actually AM placing this line of code underneath a widgets app like they ask, (as far as I understand these things, so I'm stymied.

Pertinent Code:

CupertinoThemeData currentTheme;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    final Brightness brightnessValue = MediaQuery.of(context).platformBrightness;
    bool isDark = brightnessValue == Brightness.dark;
    <CODE TO SELECT THEME DEPENDING ON PHONE'S THEME.  Setting parameters of currentTheme>

    return CupertinoApp(
      <CODE FOR APP>
    ),
  }
}

Now I WOULD put it inside the CupertinoApp Widget, but that doesn't work either, as you can't really make a function-call inside there, and I NEED to pass a theme into there, as that effects the entirety of the programs theming.

In essence, it looks like a chicken and the egg scenario, where I can't set the theme until I have the media-query, and I can't do the media-query until I've already set the theme, which negates the entire purpose of the query.

Any ideas?

Upvotes: 0

Views: 1438

Answers (2)

WSBT
WSBT

Reputation: 36373

Placing a DefaultTextStyle widget somewhere near the top of the widget tree is a good workaround. Just remember it needs to be one level lower than the CupertinoApp, or else it cannot find the inherited theme using its build context.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      home: Builder(
        builder: (BuildContext context) {
          return DefaultTextStyle(
            style: CupertinoTheme.of(context).textTheme.textStyle,
            child: MyHomePage(),
          );
        },
      ),
    );
  }
}

This way, any default Text will automatically resolve to white or black, depending on the platform brightness settings.

Sample result:

Light and dark theme

Upvotes: 0

ArthurEKing
ArthurEKing

Reputation: 156

After banging my head against a brick wall for a few hours, finally figured it out (For anyone else having a similar issue).

The trick is that yes, you do in fact need to place the code that selects the theme inside the line

return CupertinoApp( <Here> )

You can do this through:

builder: (BuildContext context, Widget child) {

   final Brightness brightnessValue = MediaQuery.of(context).platformBrightness;
   bool isDark = brightnessValue == Brightness.dark; 

   // All your code to set your two themes, light and dark can go here.

}

In this way, instead of the chicken&egg scenario I was imagining, you are setting the theme state at the moment you need it.

Hope this helps someone.

Upvotes: 1

Related Questions