Locally overriding CupertinoTheme with Flutter

Issue

I’m working with Cupertino widgets, and need to locally override my global CupertinoTheme, and use CupertinoTheme widget for this purpose. My use case is to force some ‘dark’ theme when displaying text on top of images, but the issue is general.

In the following sample, I try to change the font size for one text style (from 42px to 21px), but it is not applied: the two texts have the same size (second should be 21px high).

It seems that CupertinoTheme.of(context) does not read the overriden style, contrary to the documentation

Descendant widgets can retrieve the current CupertinoThemeData by calling CupertinoTheme.of

Here is a sample (that can be tested on DartPad):

import 'package:flutter/cupertino.dart';
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      debugShowCheckedModeBanner: true,
      theme: CupertinoThemeData(
        brightness: Brightness.dark,
        textTheme: CupertinoTextThemeData(
          navLargeTitleTextStyle: TextStyle(fontSize: 42)
        )
      ),
      home: Home()
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: Column(
        children: [
           Text(
             'Hello, World #1!',
             style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle
            ),
            CupertinoTheme(
              data: CupertinoThemeData(
                textTheme: CupertinoTextThemeData(
                  navLargeTitleTextStyle: TextStyle(fontSize: 21)
                )
              ),
              child: Text(
                'Hello, World #2!',
                style:
                CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle
              ),
            ),
        ]
      )
    );
  }
}

Solution

You’re getting the theme from the wrong context. The context must be a descendant of the CupertinoTheme widget (or rather the element that will be created from it). Try:

CupertinoTheme(
  data: ...,
  child: Builder(
    builder: (context) => ... CupertinoTheme.of(contex)...
  )
)

With the content parameter of the build method you can access anything done by ancestors of the build-method’s widget. Whatever you do in the build method has no effect on it.

Widgets are recipes for creating a tree of Elements. The context parameter that you get in build(er) method is (a reduced interface of) the element created for that widget. The Foo.of(context) methods typically search through the ancestor elements of context to find a Foo. (In some cases there is caching, so it isn’t a slow search.) When you create a tree of widgets in a build method, you’re just creating widgets; the elements will be created after that build method competes. Using a Builder widget, like I did above, delays creation of the widgets in Builder’s builder parameter until after an elements have been created for the Builder (and the widgets above it). So that is a way to get around your problem. Another way would be to create a new StatelessWidget with the widgets that are children of CupertinoTheme in your code, because it will similarly delay the creation of those widgets until after the element for that stateless widget (and its parents) is created.

Answered By – spkersten

Answer Checked By – Marilyn (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.