Home > Software engineering >  Better way to setup theme classes in Flutter
Better way to setup theme classes in Flutter

Time:12-24

Right now for setting up color themes and schemes for my various pages, I've created several classes which hold all my data.

These classes work just fine, but because I have to declare all the possible colors in my first abstract class, all my themes HAVE to set all the colors up, even if that particular theme doesn't use the color.

This is becoming a headache as some theme have one off colors and that forces we to setup blanks for all the other themes, you can see my setup here:

abstract class MyColorScheme {
  static const MyColorScheme Theme1 = Theme1();
  static const MyColorScheme Theme2 = Theme2();
  static const MyColorScheme Theme3 = Theme3();
  static const MyColorScheme Theme4 = Theme4();
  static const MyColorScheme Theme5 = Theme5();
  static const MyColorScheme Theme6 = Theme6();
  static const MyColorScheme Theme7 = Theme7();

  const MyColorScheme();

  Color color1({required brightness});
  Color get color2;
  Color get color3;
  Color get color4;
  Color get color5;

  IconThemeData get icon1;
  IconThemeData get icon2;

  TextStyle get text1;
  TextStyle get text2;
  TextStyle get text3;
  TextStyle get text4;
  TextStyle get text5;
}
class Theme1 extends MyColorScheme {
  const Theme1() : super();

  @override
  Color color1({brightness}) {
    return (brightness == Brightness.light) ? Colors.red : Colors.blue;
  }

  @override
  Color get color2 => Colors.red;

  @override
  Color get color3 => Colors.red;

  @override
  Color get color4 => Colors.red;

   ...  ///Every one of the above things from the abstract class has to be listed here, even though I don't use any of them
}

Can anyone think of a way to modify this so that I wouldn't need to declare every possible color upfront but only declare the ones I would need to use in a particular instance?

CodePudding user response:

You could change all of the getters in MyColorScheme to final properties and accept the properties as optional named parameters in the constructor.

In order to make the parameters optional you can either make the properties nullable or provide default values.

I have implemented it below using default values.

Color _color1Default({required Brightness brightness}) => Colors.transparent;

abstract class MyColorScheme {
  static const MyColorScheme theme1 = Theme1();
  static const MyColorScheme theme2 = Theme2();
  static const MyColorScheme theme3 = Theme3();
  static const MyColorScheme theme4 = Theme4();
  static const MyColorScheme theme5 = Theme5();
  static const MyColorScheme theme6 = Theme6();
  static const MyColorScheme theme7 = Theme7();

  const MyColorScheme({
    this.color1 = _color1Default,
    this.color2 = Colors.transparent,
    this.color3 = Colors.transparent,
    this.color4 = Colors.transparent,
    this.color5 = Colors.transparent,
    this.icon1 = const IconThemeData(),
    this.icon2 = const IconThemeData(),
    this.text1 = const TextStyle(),
    this.text2 = const TextStyle(),
    this.text3 = const TextStyle(),
    this.text4 = const TextStyle(),
    this.text5 = const TextStyle(),
  });

  final Color Function({required Brightness brightness}) color1;
  final Color color2;
  final Color color3;
  final Color color4;
  final Color color5;

  final IconThemeData icon1;
  final IconThemeData icon2;

  final TextStyle text1;
  final TextStyle text2;
  final TextStyle text3;
  final TextStyle text4;
  final TextStyle text5;
}

Then in your individual theme classes you can pass in only the values you need to the super constructor.

class Theme1 extends MyColorScheme {
  static Color _color1({required Brightness brightness}) {
    return (brightness == Brightness.light) ? Colors.red : Colors.blue;
  }

  const Theme1()
      : super(
          color1: _color1,
          color2: Colors.red,
          color3: Colors.red,
          color4: Colors.red,
        );
}

Additionally, if you don't define MyColorScheme as abstract, you could shorten the code slightly by just creating instances of MyColorScheme directly instead of creating sub classes.

Color _color1({required Brightness brightness}) {
  return (brightness == Brightness.light) ? Colors.red : Colors.blue;
}

const myTheme1 = MyColorScheme(
  color1: _color1,
  color2: Colors.red,
  color3: Colors.red,
  color4: Colors.red,
);

EDIT: Another approach would be to update your MyColorScheme to have default implementations for each of the get methods. You won't be required to override the functions if they have a default implementation in the base class.

abstract class MyColorScheme {
  static const MyColorScheme theme1 = Theme1();
  static const MyColorScheme theme2 = Theme2();
  static const MyColorScheme theme3 = Theme3();
  static const MyColorScheme theme4 = Theme4();
  static const MyColorScheme theme5 = Theme5();
  static const MyColorScheme theme6 = Theme6();
  static const MyColorScheme theme7 = Theme7();

  const MyColorScheme();

  Color color1({required brightness}) => Colors.transparent;
  Color get color2 => Colors.transparent;
  Color get color3 => Colors.transparent;
  Color get color4 => Colors.transparent;
  Color get color5 => Colors.transparent;

  IconThemeData get icon1 => const IconThemeData();
  IconThemeData get icon2 => const IconThemeData();

  TextStyle get text1 => const TextStyle();
  TextStyle get text2 => const TextStyle();
  TextStyle get text3 => const TextStyle();
  TextStyle get text4 => const TextStyle();
  TextStyle get text5 => const TextStyle();
}

And then Theme1 can be implemented by only overriding the desired methods.

class Theme1 extends MyColorScheme {
  const Theme1();
  
  @override
  Color color1({brightness}) {
    return (brightness == Brightness.light) ? Colors.red : Colors.blue;
  }

  @override
  Color get color2 => Colors.red;

  @override
  Color get color3 => Colors.red;

  @override
  Color get color4 => Colors.red;
}
  • Related