Flutter 主题和自定义小部件

问题描述 投票:0回答:1

我是 Flutter 主题化的新手,我正在尝试找出为 Flutter 应用程序主题化的正确方法 以及我的自定义小部件尽可能的最佳方法。

我的目标是:

  • theme
    MaterialApp
  • 属性中编写大部分应用程序主题
  • 对于那些在
    colorScheme
    上指定的
    MaterialApp
    无效的小部件,请在
    MaterialApp
    中写入其特定主题,例如:
    buttonTheme
  • 自定义小部件的行为应与框架小部件相同,因此它们应具有特定的主题,例如:由
    ElevatedButton
    及其子项组成的自定义小部件:
    CircularProgressIndicator
    Text
    应公开所有单独的组件主题作为自己主题的参数,组件也应该有自己的主题属性,例如组件之间的空格。
  • 自定义小部件还应该能够内联修改,这意味着在其声明中,不仅可以通过主题进行修改,但应尽可能避免这种情况。我宁愿声明多个主题并选择内联使用哪个主题,当然有一个默认主题。

我想知道这是否是正确的方法,是否有更好的方法,更简单的方法和/或更少的样板,以及我当前的实现中的某些内容是否没有意义,例如复制主题和主题包装在自定义小部件中。 ..

小部件主题

class ProgressElevatedButtonThemeData
    extends ThemeExtension<ProgressElevatedButtonThemeData> {
  final ProgressIndicatorThemeData? progressIndicatorTheme;
  final ElevatedButtonThemeData? elevatedButtonTheme;
  final TextTheme? textTheme;
  final double? spacing;

  const ProgressElevatedButtonThemeData({
    this.progressIndicatorTheme,
    this.elevatedButtonTheme,
    this.textTheme,
    this.spacing,
  });

  @override
  ThemeExtension<ProgressElevatedButtonThemeData> copyWith({
    ProgressIndicatorThemeData? progressIndicatorTheme,
    ElevatedButtonThemeData? elevatedButtonTheme,
    TextTheme? textTheme,
    double? spacing,
  }) {
    return ProgressElevatedButtonThemeData(
      progressIndicatorTheme: progressIndicatorTheme,
      elevatedButtonTheme: elevatedButtonTheme,
      textTheme: textTheme,
      spacing: spacing,
    );
  }

  @override
  ThemeExtension<ProgressElevatedButtonThemeData> lerp(
      covariant ThemeExtension<ProgressElevatedButtonThemeData>? other,
      double t) {
    if (other is! ProgressElevatedButtonThemeData) {
      return this;
    }

    return ProgressElevatedButtonThemeData(
      progressIndicatorTheme: ProgressIndicatorThemeData.lerp(
          progressIndicatorTheme, other.progressIndicatorTheme, t),
      elevatedButtonTheme: ElevatedButtonThemeData.lerp(
          elevatedButtonTheme, other.elevatedButtonTheme, t),
      textTheme: TextTheme.lerp(textTheme, other.textTheme, t),
      spacing: lerpDouble(spacing, other.spacing, t),
    );
  }
}

Widget 的简化版

class ProgressElevatedButton extends StatefulWidget {
  final String text;
  final double? spacing;
  final ButtonStyle? buttonStyle;
  final TextStyle? textStyle;
  final VoidCallback? onPressed;

  const ProgressElevatedButton({
    Key? key,
    required this.text,
    required this.spacing,
    required this.buttonStyle,
    required this.textStyle,
    this.onPressed,
  }) : super(key: key);

  @override
  State<ProgressElevatedButton> createState() => _ProgressElevatedButtonState();
}

class _ProgressElevatedButtonState extends State<ProgressElevatedButton> {
  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final widgetTheme = theme.extension<ProgressElevatedButtonThemeData>();
    final progressTheme = theme.copyWith(
        progressIndicatorTheme: widgetTheme?.progressIndicatorTheme);
    final buttonTheme =
        theme.copyWith(elevatedButtonTheme: widgetTheme?.elevatedButtonTheme);
    final textTheme = theme.copyWith(textTheme: widgetTheme?.textTheme);
    final spacing = widget.spacing ?? widgetTheme?.spacing ?? 16;

    return Theme(
      data: buttonTheme,
      child: ElevatedButton(
        style: widget.buttonStyle,
        onPressed: widget.onPressed,
        child: Row(
          children: [
            Theme(
              data: progressTheme,
              child: const CircularProgressIndicator(),
            ),
            SizedBox(width: spacing),
            Theme(
              data: textTheme,
              child: Text(widget.text),
            )
          ],
        ),
      ),
    );
  }
}

应用了主题的应用程序

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    // All the Framework's Widget will be using these colors
    final colorScheme = const ColorScheme.light().copyWith(
      primary: Colors.redAccent,
      // ... the rest of the colors of [ColorScheme] I need
    );

    // I've decided that the [primary] color from [colorScheme] is not the color
    // I want for [ProgressIndicator] so I changed it only for those Widgets.
    final progressIndicatorTheme = theme.progressIndicatorTheme.copyWith(
      color: Colors.green,
    );

    // Extending the theme to be able to use themes for my custom Widgets
    final extensions = <ThemeExtension<dynamic>>[
      // My custom Widget theme (ElevatedButton(children:[Progress, Text]))
      // I've decided that the [color] from [progressIndicatorTheme] is not the color
      // I want for my custom [ProgressElevatedButton] so I changed it only for those Widgets.
      ProgressElevatedButtonThemeData(
        progressIndicatorTheme: progressIndicatorTheme.copyWith(
          color: Colors.blue,
        ),
      ),
    ];

    return MaterialApp(
      title: 'Flutter Theming',
      // The theme for my whole app (no dark theme to simplify it)
      theme: ThemeData(
        colorScheme: colorScheme,
        progressIndicatorTheme: progressIndicatorTheme,
        extensions: extensions,
      ),
      home: const SizedBox.shrink(),
    );
  }
}
flutter widget themes
1个回答
-1
投票

我建议你做一个

ThemeManager

class ThemeManager with ChangeNotifier{

  ThemeMode _themeMode = ThemeMode.light;

  get themeMode => _themeMode;

  toggleTheme(bool isDark){
    _themeMode = isDark?ThemeMode.dark:ThemeMode.light;
    notifyListeners();
  }

}

themes
文件提供主题,您在其中指定了自己的
ThemeMode

const COLOR_PRIMARY = Colors.deepOrangeAccent;
const COLOR_ACCENT = Colors.orange;

ThemeData lightTheme = ThemeData(
  brightness: Brightness.light,
  primaryColor: COLOR_PRIMARY,
  elevatedButtonTheme: ElevatedButtonThemeData(
    style: ButtonStyle(
      padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
        EdgeInsets.symmetric(horizontal: 40.0,vertical: 20.0)
      ),
      shape: MaterialStateProperty.all<OutlinedBorder>(
        RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(20.0))
      ),
      backgroundColor: MaterialStateProperty.all<Color>(COLOR_ACCENT)
    ),
  ),
);

ThemeData darkTheme = ThemeData(

    brightness: Brightness.dark,
    accentColor: Colors.white,
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ButtonStyle(
          padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
              EdgeInsets.symmetric(horizontal: 40.0,vertical: 20.0)
          ),
          shape: MaterialStateProperty.all<OutlinedBorder>(
              RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(20.0)
              )
          ),
          backgroundColor: MaterialStateProperty.all<Color>(Colors.white),
          foregroundColor: MaterialStateProperty.all<Color>(Colors.black),
          overlayColor: MaterialStateProperty.all<Color>(Colors.black26)
      )
  ),
);

ThemeManager
文件中初始化
main

void main() {
  runApp(MyApp());
}

ThemeManager _themeManager = ThemeManager();

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void dispose() {
    _themeManager.removeListener(themeListener);
    super.dispose();
  }

  @override
  void initState() {
    _themeManager.addListener(themeListener);
    super.initState();
  }

  themeListener(){
    if(mounted){
      setState(() {

      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: lightTheme,
      darkTheme: darkTheme,
      themeMode: _themeManager.themeMode,
      home: MyHomeScreen(),
    );
  }
}

这里有一个关于它的精彩 YouTube 视频:链接

© www.soinside.com 2019 - 2024. All rights reserved.