kdsuneraavinash / theme_provider

Easy to use, customizable and pluggable Theme Provider.
https://pub.dartlang.org/packages/theme_provider
MIT License
152 stars 28 forks source link

Choosing light or dark theme based on ThemeMode #20

Open deinspanjer opened 3 years ago

deinspanjer commented 3 years ago

I just bumped into your library while looking for references on how to properly get MaterialApp to be a consumer of a ChangeNotifyProvider that is providing theme data.

It looks like you've already written a lot of the same code I have, and I'm looking at switching over to your library instead of my custom code.

One thing I've noticed is different with your implementation is that you don't currently make any use of Material's ThemeMode. This enum has 3 values, system, light, and dark. The default for MaterialApp is system. If the system is set to use a dark theme (i.e Android system preferences, or the Browser theme default) then MaterialApp will attempt to use the ThemeData provided in darkTheme. If that property is not set, then it will default to theme.

What this allows is to have an overall theme but apply a light and dark variant of it, and set both of them in the MaterialApp properties theme and darkTheme, then either let the system default choose, or if the user wishes to override, the way that override is implemented is by changing the themeMode property to either ThemeMode.light or ThemeMode.dark.

Here is an example of my AppTheme model that can use this. Would you be interested in changes to your library to try to take ThemeMode into account?

import 'package:flutter/material.dart';

class AppThemeModel extends ChangeNotifier {
  var _appTheme = defaultAppTheme;

  AppTheme get appTheme => _appTheme;

  set appTheme(AppTheme appTheme) {
    _appTheme = appTheme;
    notifyListeners();
  }
}

final defaultAppTheme = AppTheme._('System Default', ThemeMode.system, ThemeData.light(), ThemeData.dark());
final appThemes = <String, AppTheme>{
  'SystemDefault': defaultAppTheme,
  'BasicLight': AppTheme._('Basic Light', ThemeMode.light, ThemeData.light(), null),
  'BasicDark': AppTheme._('Basic Dark', ThemeMode.dark, null, ThemeData.dark()),
  'Fortnightly': AppTheme._('Fortnightly', ThemeMode.light, _buildFortnightlyTheme(), null),
  'Fortnightly2': AppTheme._('Fortnightly2', ThemeMode.light, _buildFortnightlyTheme(), null),
};

class AppTheme {
  final String _name;
  final ThemeMode _mode;
  final ThemeData? _lightData;
  final ThemeData? _darkData;

  const AppTheme._(this._name, this._mode, this._lightData, this._darkData);

  String get name => _name;

  ThemeMode get themeMode => _mode;

  ThemeData? get lightThemeData => _lightData;

  ThemeData? get darkThemeData => _darkData;

  @override
  String toString() {
    return _name;
  }
}

ThemeData _buildFortnightlyTheme() {
  TextTheme _buildTextTheme(TextTheme base) {
    TextTheme theme = base.apply(bodyColor: Colors.black, displayColor: Colors.black);

    theme = theme.copyWith(
      headline2: base.headline2!.copyWith(
        fontFamily: 'LibreFranklin',
        fontSize: 18,
        fontWeight: FontWeight.w500,
        color: Colors.black.withAlpha(153),
      ),
      headline4: base.headline4!.copyWith(
        fontFamily: 'Merriweather',
        fontStyle: FontStyle.italic,
        fontSize: 28,
        fontWeight: FontWeight.w800,
        color: Colors.black,
        height: .88,
      ),
      headline5: base.headline5!.copyWith(fontWeight: FontWeight.w500),
      bodyText1: base.bodyText1!.copyWith(
        fontFamily: 'Merriweather',
        fontSize: 16,
        fontWeight: FontWeight.w300,
        color: const Color(0xFF666666),
        height: 1.4,
        letterSpacing: .25,
      ),
      bodyText2: base.bodyText2!.copyWith(
        fontFamily: 'Merriweather',
        fontSize: 14,
        fontWeight: FontWeight.w300,
        color: const Color(0xFF666666),
        height: 1.11,
      ),
      overline: const TextStyle(
        fontFamily: 'LibreFranklin',
        fontSize: 10,
        fontWeight: FontWeight.w700,
        color: Colors.black,
      ),
    );

    return theme;
  }

  final ThemeData base = ThemeData.light();
  return base.copyWith(
    primaryTextTheme: _buildTextTheme(base.primaryTextTheme),
    scaffoldBackgroundColor: Colors.white,
  );
}
kdsuneraavinash commented 3 years ago

Yeah, that would be fine. I think, in addition to data parameter in AppTheme we can provide an optional darkData parameter or something equivalent. So, if that param is not present we can default to using the theme in data in both light and dark modes.

Would you be fine with sending a PR? If not I guess I'll try to add this within few weeks.

deinspanjer commented 3 years ago

Yes, I am working on a feature branch in my fork that implements support for light, dark and high contrast variants of themes in a subclass of AppTheme called CompositeAppTheme. I should have that wrapped up and available as a PR next week.