是否可以在Flutter中“扩展”ThemeData

时间:2018-03-08 12:09:29

标签: dart flutter

我很可能会错过一些东西,因为我很吵,但我发现ThemeData的选项非常有限(至少我对如何实现它的理解)。

如果你从MaterialUp看下面这个随机设计,我想要建立一些大致的东西:

Themedata.cyclingColor = Color.pink; ThemeData.runningColor = Color.green;

在我的应用程序中,我可以参考骑行,跑步,游泳,健身房颜色(或者在我的应用程序/设计环境中有意义的任何颜色),并保持一致。

Random design from MaterialUp

目前在Flutter中有推荐的方法吗?我有什么选择?

9 个答案:

答案 0 :(得分:8)

我推荐这种方法,该方法很简单,可与热重载一起使用,并且可以轻松扩展以支持在黑暗和明亮主题之间切换。

首先创建您自己的类似于ThemeData的模拟,我们将其称为AppThemeData

class AppThemeData {
  final BorderRadius borderRadius = BorderRadius.circular(8);

  final Color colorYellow = Color(0xffffff00);
  final Color colorPrimary = Color(0xffabcdef);

  ThemeData get materialTheme {
    return ThemeData(
        primaryColor: colorPrimary
    );
  }
}

只要需要标准materialTheme,就可以使用ThemeData

然后创建一个名为AppTheme的小部件,该小部件使用AppThemeData包提供provider的实例。

class AppTheme extends StatelessWidget {
  final Widget child;

  AppTheme({this.child});

  @override
  Widget build(BuildContext context) {
    final themeData = AppThemeData(context);
    return Provider.value(value: themeData, child: child);
  }
}

最后,用AppTheme包装整个应用程序。要访问主题,您可以调用context.watch<AppThemeData>()。或创建此扩展程序...

extension BuildContextExtension on BuildContext {
  AppThemeData get appTheme {
    return watch<AppThemeData>();
  }
}

...并使用context.appTheme。我通常将final theme = context.appTheme;放在小部件构建方法的第一行。

答案 1 :(得分:5)

我扩展了标准 ThemeData 以便在任何时候都可以像这样访问自己的主题字段:

Theme.of(context).own().errorShade

或者像这样:

ownTheme(context).errorShade

一个主题可以用如下的新字段定义和扩展(通过在某个 addOwn() 实例上调用的 ThemeData):

final ThemeData lightTheme = (ThemeData.light().copyWith(
    accentColor: Colors.grey.withAlpha(128),
    backgroundColor: Color.fromARGB(255, 255, 255, 255),
    textTheme: TextTheme(
      caption: TextStyle(
          fontSize: 17.0, fontFamily: 'Montserrat', color: Colors.black),
    )))
  ..addOwn(OwnThemeFields(
      errorShade: Color.fromARGB(240, 255, 200, 200),
      textBaloon: Color.fromARGB(240, 255, 200, 200)));

final ThemeData darkTheme = ThemeData.dark().copyWith( ...
...

可以通过传统方式在 MaterialApp 小部件中设置后面的主题:

MaterialApp(
...
  theme: lightTheme,
  darkTheme: darkTheme,
)

这个想法是将主题化所需的所有自定义字段放在一个单独的类 OwnThemeFields 中。

然后使用 2 个方法扩展 ThemeData 类:

  1. addOwn()ThemedData 的某个实例连接到 OwnThemeFields 实例
  2. own() 允许查找与给定主题数据关联的自己的字段

还可以创建 ownTheme 辅助方法来缩短对自己字段的提取。

class OwnThemeFields {
  final Color errorShade;
  final Color textBaloon;

  const OwnThemeFields({Color errorShade, Color textBaloon})
      : this.errorShade = errorShade,
        this.textBaloon = textBaloon;

  factory OwnThemeFields.empty() {
    return OwnThemeFields(errorShade: Colors.black, textBaloon: Colors.black);
  }
}

extension ThemeDataExtensions on ThemeData {
  static Map<InputDecorationTheme, OwnThemeFields> _own = {};

  void addOwn(OwnThemeFields own) {
    _own[this.inputDecorationTheme] = own;
  }

  static OwnThemeFields empty = null;

  OwnThemeFields own() {
    var o = _own[this.inputDecorationTheme];
    if (o == null) {
      if (empty == null) empty = OwnThemeFields.empty();
      o = empty;
    }
    return o;
  }
}

OwnThemeFields ownTheme(BuildContext context) => Theme.of(context).own();

完整来源:https://github.com/maxim-saplin/dikt/blob/master/lib/ui/themes.dart

答案 2 :(得分:1)

Dart 2.7更高版本,扩展支持

您可以为系统类添加扩展名

仅添加实例属性很容易,但是如果您将获得动态颜色

您需要考虑一下。例如,使用常量在明暗模式下获取颜色

确定是否为暗模式

两种方式

  • MediaQuery.of(context).platformBrightnes == Brightness.dark;
  • Theme.of(context).brightness == Brightness.dark;

As you can see, you need the context, the context

为BuildContext添加扩展

这是代码

extension MYContext on BuildContext {
  Color dynamicColor({int light, int dark}) {
    return (Theme.of(this).brightness == Brightness.light)
        ? Color(light)
        : Color(dark);
  }

  Color dynamicColour({Color light, Color dark}) {
    return (Theme.of(this).brightness == Brightness.light)
        ? light
        : dark;
  }

  /// the white background
  Color get bgWhite => dynamicColor(light: 0xFFFFFFFF, dark: 0xFF000000);
}

使用方法

import 'package:flutter/material.dart';
import 'buildcontext_extension.dart';

class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: context.bgWhite,
    );
  }
}

此颜色可能需要多个文件,因此您可以创建一个public.dart文件来管理所有内容

Like This

public.dart


library public;

// Export some common header files

// extensions
export 'buildcontext_extension.dart';

DarkMode图像支持

将浅色图像与深色图像归为同一类

some code

static String getImgPath(String name, {
    String folder = '', 
    String format = 'png', 
    bool isDark = false, 
    bool needDark = true
  }) {
    String finalImagePath;
    if (needDark) {
      final folderName = isDark ? '${folder}_dark' : folder;
      finalImagePath = 'assets/images/$folderName/$name.$format';
    } else {
      finalImagePath = 'assets/images/$folder/$name.$format';
    }
    String isDarkPath = isDark ? "? DarkMode" : "? LightMode";
    print('$isDarkPath imagePath ? $finalImagePath');
    return finalImagePath;
  }

答案 3 :(得分:1)

使用此库 https://developers.googleblog.com/2017/12/creating-custom-estimators-in-tensorflow.html 进行主题切换。 并创建 ColorSheme 的扩展

extension MenuColorScheme on ColorScheme {
      Color get menuBackground => brightness == Brightness.light
          ? InlLightColors.White
          : InlDarkColors.Black;
}

在小部件中使用

Container(
      color: Theme.of(context).colorScheme.menuBackground,
...
)

这种方式非常简单优雅。很高兴编码。

答案 4 :(得分:0)

您无法延长ThemeData,因为物质组件再也找不到了。

除了Flutter中包含的MyThemeData之外,你可以以同样的方式创建并提供ThemeData

创建一个扩展CustomThemeWidget的小部件InheritedWidget,并在那里提供您的自定义主题。

如果您想从当前主题中获取值

myTheme = CustomThemeWidget.of(context).myTheme;

要更改当前主题更改MyThemeData

中的CustomThemeWidget.myTheme

<强>更新

https://github.com/flutter/flutter/pull/14793/files所示,应该可以通过覆盖ThemeData来扩展ThemeData并将其作为runtimeType提供

另请参阅https://github.com/flutter/flutter/issues/16487#event-1573761656

中的评论

答案 5 :(得分:0)

我还发现ThemeData受到限制。我已经做过并且将来将要用于我所有应用程序的工作是创建自己的ThemeData

我创建了一个名为color_themes.dart的文件,并创建了一个名为class的{​​{1}},它的构造函数带有所需颜色的名称。例如ColorThemes;

cyclingColor

然后可以通过导入文件并调用class ColorThemes { const static cyclingColor = const Color(0xffb74093); } 来调用这些颜色。可以在ColorThemes.cyclingColor中分配这些值,以使这些颜色默认为ThemeData。使用此方法的好处之一是,您不需要像这样{/ {1}}那样使用/引用ColorThemes,从而使在提取的小部件中使用代码变得容易得多。

答案 6 :(得分:0)

现在可以通过使用Dart 2.7中的扩展方法来实现,请查看this article

答案 7 :(得分:0)

如果不使用所有textTheme标题,这是一个简单的解决方法,您可以设置其中一些颜色并像通常使用其他颜色一样使用它们。

设置标题1的颜色: ThemeData(textTheme: TextTheme(headline1: TextStyle(color: Colors.red),),),

使用它: RawMaterialButton(fillColor: Theme.of(context).textTheme.headline1.color,onPressed: onPressed,)

答案 8 :(得分:0)

我创建了一个类似于 ThemeData 实现的实现:

@override
Widget build(BuildContext context) {
     final Brightness platformBrightness = Theme.of(context).brightness;
     final bool darkTheme = platformBrightness == Brightness.dark;

     return CustomAppTheme(
               customAppTheme:
                   darkTheme ? CustomAppThemeData.dark : CustomAppThemeData.light,
               child: Icon(Icons.add, color: CustomAppTheme.of(context).addColor,),
     );
}
import 'package:calendarflutter/style/custom_app_theme_data.dart';
import 'package:flutter/material.dart';

class CustomAppTheme extends InheritedWidget {
  CustomAppTheme({
    Key key,
    @required Widget child,
    this.customAppTheme,
  }) : super(key: key, child: child);

  final CustomAppThemeData customAppTheme;

  static CustomAppThemeData of(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<CustomAppTheme>()
        .customAppTheme;
  }

  @override
  bool updateShouldNotify(CustomAppTheme oldWidget) =>
      customAppTheme != oldWidget.customAppTheme;
}
import 'package:flutter/material.dart';

class CustomAppThemeData {
  final Color plusColor;

  const CustomAppThemeData({
    @required this.plusColor,
  });

  static CustomAppThemeData get dark {
    return CustomAppThemeData(
      plusColor: Colors.red,
    );
  }

  static CustomAppThemeData get light {
    return CustomAppThemeData(
      plusColor: Colors.green,
    );
  }
}