Flutter是否有可能“扩展” ThemeData

Cra*_*igt 10 dart flutter

我可能会很想念某些东西,因为我太陌生了,但是我发现ThemeData的选项非常有限(至少在我了解如何实现它的情况下)。

如果您在下面的MaterialUp中查看此随机设计,我想建模大致如下:

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

这样,在我的应用程序中的任何地方,我都可以引用自行车,跑步,游泳,健身房的颜色(或在我的应用程序/设计中有意义的任何颜色),并使事物保持一致。

MaterialUp的随机设计

目前在Flutter中有推荐的方法来实现这一目标吗?我有什么选择?

fhu*_*cho 20

我推荐这种方法,它很简单,适用于热重载,并且可以轻松扩展以支持在深色和浅色主题之间切换。

首先创建你自己的模拟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
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,用AppTheme. 要访问主题,您可以调用context.watch<AppThemeData>()。或者创建这个扩展...

extension BuildContextExtension on BuildContext {
  AppThemeData get appTheme {
    return watch<AppThemeData>();
  }
}
Run Code Online (Sandbox Code Playgroud)

...并使用context.appTheme. 我一般放在final theme = context.appTheme;widget build方法的第一行。


Lak*_*ami 15

2022年:使用flutter 3中引入的ThemeExtensions

这是一个链接!到我写的媒体文章。

  1. 创建您的 ThemeExtension 类
import 'package:flutter/material.dart';

@immutable
class MyCardTheme extends ThemeExtension<MyCardTheme> {
  const MyCardTheme({
    this.background = Colors.white,
    this.shape = const RoundedRectangleBorder(
      borderRadius: BorderRadius.all(
        Radius.circular(8),
      ),
    ),
  });

  final Color background;
  final ShapeBorder shape;

  @override
  MyCardTheme copyWith({
    Color? background,
    ShapeBorder? shape,
  }) {
    return MyCardTheme(
      background: background ?? this.background,
      shape: shape ?? this.shape,
    );
  }

  @override
  MyCardTheme lerp(ThemeExtension<MyCardTheme>? other, double t) {
    if (other is! MyCardTheme) {
      return this;
    }
    return MyCardTheme(
      background: Color.lerp(background, other.background, t) ?? Colors.white,
      shape: ShapeBorder.lerp(shape, other.shape, t) ??
          const RoundedRectangleBorder(
            borderRadius: BorderRadius.all(
              Radius.circular(8),
            ),
          ),
    );
  }

  @override
  String toString() => 'MyCardTheme('
      'background: $background, radius: $shape'
      ')';
}
Run Code Online (Sandbox Code Playgroud)
  1. 根据要求创建深色和浅色主题
MyCardTheme lightCardTheme = MyCardTheme(
  background: Colors.blueGrey[200]!,
  shape: const RoundedRectangleBorder(
    borderRadius: BorderRadius.all(
      Radius.circular(24),
    ),
  ),
);

MyCardTheme darkCardTheme = MyCardTheme(
  background: Colors.blueGrey[800]!,
  shape: const RoundedRectangleBorder(
    borderRadius: BorderRadius.all(
      Radius.circular(24),
    ),
  ),
);
Run Code Online (Sandbox Code Playgroud)
  1. 为您的 ThemeData 添加浅色和深色主题的扩展。
 theme: ThemeData(
    primarySwatch: Colors.green,
    cardTheme: const CardTheme(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.all(
          Radius.circular(8),
        ),
      ),
      color: Colors.green,
    ),
    extensions: <ThemeExtension<dynamic>>[
      lightCardTheme,
    ],
  ),
  darkTheme: ThemeData(
    brightness: Brightness.dark,
    primarySwatch: Colors.green,
    cardTheme: const CardTheme(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.all(
          Radius.circular(8),
        ),
      ),
      color: Colors.green,
    ),
    extensions: <ThemeExtension<dynamic>>[
      darkCardTheme,
    ],
  ),
Run Code Online (Sandbox Code Playgroud)
  1. 在您的构建方法中使用它们
final MyCardTheme customCardTheme =
        Theme.of(context).extension<MyCardTheme>()!;
Card(
    shape: customCardTheme.shape,
    color: customCardTheme.background,
    child: Container(
             padding: const EdgeInsets.all(16),
             child: const Text('Card styled from custom theme')),
          ),
    ),

Run Code Online (Sandbox Code Playgroud)


Max*_*lin 10

我扩展了标准 ThemeData 以便在任何时候都可以访问自己的主题字段,如下所示:

Theme.of(context).own().errorShade
Run Code Online (Sandbox Code Playgroud)

或者像这样:

ownTheme(context).errorShade
Run Code Online (Sandbox Code Playgroud)

可以使用以下新字段定义和扩展主题(通过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( ...
...
Run Code Online (Sandbox Code Playgroud)

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

MaterialApp(
...
  theme: lightTheme,
  darkTheme: darkTheme,
)
Run Code Online (Sandbox Code Playgroud)

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

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

  1. addOwn()将某个实例连接ThemedDataOwnThemeFields实例
  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();
Run Code Online (Sandbox Code Playgroud)

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

  • @MaximSaplin您依靠“inputDecorationTheme”来设置集合的键,并且没有任何东西可以确保这不会是一个常量。我建议改为依赖“亮度”(如果两个主题的亮度恰好相同,则在代码中添加警告注释,程序员应该选择其他内容)。 (2认同)

小智 9

除了扩展之外,您还可以使用 flutter 中的新功能ThemeExtension。我们可以添加自定义样式,甚至可以在 css 中使用类类型主题配置。

例子:


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

@immutable
class MyColors extends ThemeExtension<MyColors> {
  const MyColors({
    required this.brandColor,
    required this.danger,
  });

  final Color? brandColor;
  final Color? danger;

  @override
  MyColors copyWith({Color? brandColor, Color? danger}) {
    return MyColors(
      brandColor: brandColor ?? this.brandColor,
      danger: danger ?? this.danger,
    );
  }

  @override
  MyColors lerp(ThemeExtension<MyColors>? other, double t) {
    if (other is! MyColors) {
      return this;
    }
    return MyColors(
      brandColor: Color.lerp(brandColor, other.brandColor, t),
      danger: Color.lerp(danger, other.danger, t),
    );
  }

  // Optional
  @override
  String toString() => 'MyColors(brandColor: $brandColor, danger: $danger)';
}

void main() {
  // Slow down time to see lerping.
  timeDilation = 5.0;
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool isLightTheme = true;

  void toggleTheme() {
    setState(() => isLightTheme = !isLightTheme);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: MyApp._title,
      theme: ThemeData.light().copyWith(
        extensions: <ThemeExtension<dynamic>>[
          const MyColors(
            brandColor: Color(0xFF1E88E5),
            danger: Color(0xFFE53935),
          ),
        ],
      ),
      darkTheme: ThemeData.dark().copyWith(
        extensions: <ThemeExtension<dynamic>>[
          const MyColors(
            brandColor: Color(0xFF90CAF9),
            danger: Color(0xFFEF9A9A),
          ),
        ],
      ),
      themeMode: isLightTheme ? ThemeMode.light : ThemeMode.dark,
      home: Home(
        isLightTheme: isLightTheme,
        toggleTheme: toggleTheme,
      ),
    );
  }
}

class Home extends StatelessWidget {
  const Home({
    Key? key,
    required this.isLightTheme,
    required this.toggleTheme,
  }) : super(key: key);

  final bool isLightTheme;
  final void Function() toggleTheme;

  @override
  Widget build(BuildContext context) {
    final MyColors myColors = Theme.of(context).extension<MyColors>()!;
    return Material(
      child: Center(
          child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(width: 100, height: 100, color: myColors.brandColor),
          const SizedBox(width: 10),
          Container(width: 100, height: 100, color: myColors.danger),
          const SizedBox(width: 50),
          IconButton(
            icon: Icon(isLightTheme ? Icons.nightlight : Icons.wb_sunny),
            onPressed: toggleTheme,
          ),
        ],
      )),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

来自Flutter API 文档


Gün*_*uer 7

您无法扩展,ThemeData因为材料组件将不再找到它。

MyThemeData除了ThemeData包含在 Flutter中的内容之外,您还可以以相同的方式创建和提供。

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

当您想从当前主题中获取值时,请使用

myTheme = CustomThemeWidget.of(context).myTheme;
Run Code Online (Sandbox Code Playgroud)

要改变当前的主题变化MyThemeDataCustomThemeWidget.myTheme

更新

https://github.com/flutter/flutter/pull/14793/files所示,应该可以通过覆盖来扩展ThemeData和提供它ThemeDataruntimeType

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


mrg*_*t96 5

我还发现这ThemeData是限制性的。我已经所做的以及将来将为我的所有应用程序所做的就是创建我自己的ThemeData.

我创建了一个名为的文件color_themes.dart,并使用构造函数创建了一个名为我想要的颜色名称的class文件。ColorThemes例如cyclingColor;

class ColorThemes {
    static const cyclingColor = const Color(0xffb74093); 
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过导入文件并调用ColorThemes.cyclingColor您可以在您的文件中分配这些值来调用这些ThemeData颜色,以使这些颜色默认为您的ColorThemes. 使用此方法的好处之一是您不需要context像这样使用/引用,ThemeData.of(context)从而可以更轻松地在提取的小部件中使用代码。

  • 然后,您就避免了多重主题和黑暗模式的好处。 (5认同)

小智 5

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);
}
Run Code Online (Sandbox Code Playgroud)

如何使用

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

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

这个颜色可能需要多个文件,所以你可以创建一个 public.dart 文件来管理它

Like This

公众.dart


library public;

// Export some common header files

// extensions
export 'buildcontext_extension.dart';

Run Code Online (Sandbox Code Playgroud)

暗模式图像支持

将浅色图像与深色图像放在同一类别中

some code

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);
}
Run Code Online (Sandbox Code Playgroud)