Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cleanly overriding parts of a theme locally in Flutter

Tags:

flutter

I have a widget which has two TextFields as descendants. I would like to apply the same styling to these TextFields. My understanding is that the right way to do this is to apply a localized theme to my widget tree. The following is my attempt. This is a code snippet from my root widget's build function. Is there not a cleaner way to do this?

final ThemeData _themeData = Theme.of(context);
return Theme( // HACK
  data: _themeData.copyWith(
    inputDecorationTheme: InputDecorationTheme(
      border: OutlineInputBorder(),
    ),
    textTheme: _themeData.textTheme.copyWith(
      subhead: _themeData.textTheme.subhead.copyWith(
        fontSize: 30.0,
      ),
    ),
  ),
  child: _buildTheRestOfMyWidgetTree(context),
);

The thing that I am annoyed by is that to override a single property (_themeData.textTheme.subhead.fontSize), I have to explicitly and manually make copies of three intermediate data structures (_themeData, then _themeData.textTheme, then _themeData.textTheme.subhead).

like image 208
Andrey Mishchenko Avatar asked Sep 15 '18 13:09

Andrey Mishchenko


People also ask

How do you override a theme in flutter?

Using copywith to override the theme data in flutter app developing. Sample code of flutter theme data with copywith properties. // This widget is the root of your application.

What is the use of ThemeData in flutter?

ThemeData class Null safety. Defines the configuration of the overall visual Theme for a MaterialApp or a widget subtree within the app. The MaterialApp theme property can be used to configure the appearance of the entire app.


2 Answers

While I can understand the frustration of having to "copy" everything, this is how you should do it.

Data are immutable in Flutter. You cannot mutate them, you are forced to clone them with different properties.

Therefore your assumption is correct: If you want to modify a nested property, you have to clone all of its parents too. Which leads to:

final ThemeData theme = Theme.of(context);
theme.copyWith(
  textTheme: theme.textTheme.copyWith(
    subhead: theme.textTheme.subhead.copyWith(
      fontSize: 30.0,
    ),
  ),
);

Again: you cannot avoid it.

like image 145
Rémi Rousselet Avatar answered Sep 26 '22 01:09

Rémi Rousselet


It would help if you packaged that part of the code up and made it a widget so that your tree is cleaner. That's how it's done in this example.

class TextFieldOverride extends StatelessWidget {
  const TextFieldOverride({this.child});
  final Widget child;
  @override
  Widget build(BuildContext context) {
    final themeData = Theme.of(context);
    return Theme(
      child: child,
      data: themeData.copyWith(
        inputDecorationTheme: InputDecorationTheme(
          border: OutlineInputBorder()),
      textTheme: themeData.textTheme.copyWith(
        subhead: themeData.textTheme.subhead.copyWith(
          fontSize: 30.0))));
  }
}

...

TextFieldOverride(
  child: TextField(...)
)

Or if there are few places the code will be duplicated, you can just make the changes directly:

...
child: TextField(
  style: Theme.of(context).textTheme.subhead.copyWith(fontSize: 30.0),
  decoration: InputDecoration(border: OutlineInputBorder(),
    ...
  )
)

Or perhaps the best choice is to create a function that does the above for you.

TextField buildTextField(BuildContext context) => TextField(
  style: Theme.of(context).textTheme.subhead.copyWith(fontSize: 30.0),
  decoration: InputDecoration(border: OutlineInputBorder(),
    ...
  )
)
like image 33
Jacob Phillips Avatar answered Sep 26 '22 01:09

Jacob Phillips