我是 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(),
);
}
}
我建议你做一个
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 视频:链接