Flutter 中的动态主题属性,可以在运行时设置

Dynamic theme properties in Flutter which can be set in runtime

我想创建自己的主题属性,这些属性可以在运行时动态设置。 我试图为 TextTheme 创建一个扩展,看起来像这样:


extension CustomTextTheme on TextTheme {
  TextStyle get heading => themeMode == ThemeMode.light
      ? TextStyle(
          color: GlobalTheme.defaultLightTheme.textTheme.headline.color,
          fontSize: GlobalTheme.defaultLightTheme.textTheme.headline.fontSize,
        )
      : TextStyle(
          color: GlobalTheme.defaultDarkTheme.textTheme.headline.color,
          fontSize: GlobalTheme.defaultLightTheme.textTheme.headline.fontSize,
        );
}

问题是如何在运行时动态更改扩展属性。 我想要存档的是,我可以从服务器加载“主题配置”并在每个设备上动态设置该主题。

要在小部件树中传递和获取值,您需要 InheritedWidget

这是一种特殊类型的 Widgets,它只是在小部件之间传输信息(例如 Theme 传送 ThemeData)。 您不能使用新字段扩展 ThemeData,因为扩展不会触发 Theme 上的更新。 但是您可以创建自己的 CustomTheme,它将与原始版本共存。

class CustomThemeData {
  const CustomThemeData(this.heading);

  final TextStyle heading;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is CustomThemeData &&
          runtimeType == other.runtimeType &&
          heading == other.heading;

  @override
  int get hashCode => heading.hashCode;
}

现在创建一个 InheritedWidget,它将提供自定义主题数据值(通过 of)并将增加更新数据的可能性(通过 update

class CustomTheme extends InheritedWidget {
  const CustomTheme({
    Key? key,
    required this.data,
    required this.onUpdate,
    required Widget child,
  }) : super(key: key, child: child);

  final CustomThemeData data;
  final void Function(CustomThemeData) onUpdate;

  static CustomThemeData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CustomTheme>()!.data;
  }

  static void update(BuildContext context, CustomThemeData data) {
    context.dependOnInheritedWidgetOfExactType<CustomTheme>()!.onUpdate(data);
  }

  @override
  bool updateShouldNotify(covariant CustomTheme oldWidget) {
    return data != oldWidget.data;
  }
}

这是自定义主题的支架

class ThemeSwitcherWidget extends StatefulWidget {
  final CustomThemeData initialTheme;
  final Widget child;

  const ThemeSwitcherWidget({
    Key? key,
    required this.initialTheme,
    required this.child,
  }) : super(key: key);

  @override
  _ThemeSwitcherWidgetState createState() => _ThemeSwitcherWidgetState();
}

class _ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
  CustomThemeData? _updatedTheme;

  @override
  Widget build(BuildContext context) {
    return CustomTheme(
      data: _updatedTheme ?? widget.initialTheme,
      onUpdate: (newData) => setState(() {
        _updatedTheme = newData;
      }),
      child: widget.child,
    );
  }
}

这里有一个关于如何使用所有这些美丽的例子:

void main() {
  runApp(
    const ThemeSwitcherWidget(
      initialTheme: CustomThemeData(TextStyle()),
      child: MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'text',
                style: CustomTheme.of(context).heading,
              ),
              ElevatedButton(
                  onPressed: () {
                    CustomTheme.update(
                        context,
                        const CustomThemeData(TextStyle(
                          color: Colors.red,
                          fontSize: 44,
                        )));
                  },
                  child: const Text('Change theme')),
            ],
          ),
        ),
      ),
    );
  }
}

为了使代码不那么冗长,您可以使用 provider,它将为您带来所有更新的魔力。