flutter-theming-apps
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImplementing Flutter Theming and Adaptive Design
实现Flutter主题与自适应设计
Contents
目录
Core Theming Concepts
核心主题概念
Flutter applies styling in a strict hierarchy: styles applied to the specific widget -> themes that override the immediate parent theme -> the main app theme.
- Define app-wide themes using the property of
themewith aMaterialAppinstance.ThemeData - Override themes for specific widget subtrees by wrapping them in a widget and using
Theme.Theme.of(context).copyWith(...) - Do not use deprecated properties:
ThemeData- Replace with
accentColor.colorScheme.secondary - Replace with
accentTextTheme(usingtextThemefor contrast).colorScheme.onSecondary - Replace with
AppBarTheme.color.AppBarTheme.backgroundColor
- Replace
Flutter 采用严格的样式层级:应用到特定Widget的样式 -> 覆盖父级主题的主题 -> 应用主主题。
- 使用的
MaterialApp属性搭配theme实例定义全局应用主题。ThemeData - 通过将Widget子树包裹在组件中,并使用
Theme来覆盖特定子树的主题。Theme.of(context).copyWith(...) - 请勿使用已废弃的属性:
ThemeData- 用替代
colorScheme.secondary。accentColor - 用(搭配
textTheme保证对比度)替代colorScheme.onSecondary。accentTextTheme - 用替代
AppBarTheme.backgroundColor。AppBarTheme.color
- 用
Material 3 Guidelines
Material 3 指南
Material 3 is the default theme as of Flutter 3.16.
- Colors: Generate color schemes using . This ensures accessible contrast ratios.
ColorScheme.fromSeed(seedColor: Colors.blue) - Elevation: Material 3 uses to indicate elevation instead of just drop shadows. To revert to M2 shadow behavior, set
ColorScheme.surfaceTintand define asurfaceTint: Colors.transparent.shadowColor - Typography: Material 3 updates font sizes, weights, and line heights. If text wrapping breaks legacy layouts, adjust on the specific
letterSpacing.TextStyle - Modern Components:
- Replace with
BottomNavigationBar.NavigationBar - Replace with
Drawer.NavigationDrawer - Replace with
ToggleButtons.SegmentedButton - Use for a high-emphasis button without the elevation of
FilledButton.ElevatedButton
- Replace
从Flutter 3.16开始,Material 3成为默认主题。
- 颜色:使用生成配色方案,确保符合可访问性对比度标准。
ColorScheme.fromSeed(seedColor: Colors.blue) - 阴影层级:Material 3 使用来标识阴影层级,而非仅依赖阴影效果。若要恢复M2的阴影行为,设置
ColorScheme.surfaceTint并定义surfaceTint: Colors.transparent。shadowColor - 排版:Material 3 更新了字体大小、字重和行高。如果文本换行破坏了原有布局,可调整特定的
TextStyle。letterSpacing - 现代组件:
- 用替代
NavigationBar。BottomNavigationBar - 用替代
NavigationDrawer。Drawer - 用替代
SegmentedButton。ToggleButtons - 对于无需阴影层级的高优先级按钮,使用而非
FilledButton。ElevatedButton
- 用
Component Theme Normalization
组件主题规范化
Component themes in have been normalized to use classes rather than widgets.
ThemeData*ThemeData*ThemeWhen defining , strictly use the suffix for the following properties:
ThemeData*ThemeData- : Use
cardTheme(NotCardThemeData)CardTheme - : Use
dialogTheme(NotDialogThemeData)DialogTheme - : Use
tabBarTheme(NotTabBarThemeData)TabBarTheme - : Use
appBarTheme(NotAppBarThemeData)AppBarTheme - : Use
bottomAppBarTheme(NotBottomAppBarThemeData)BottomAppBarTheme - : Use
inputDecorationTheme(NotInputDecorationThemeData)InputDecorationTheme
ThemeData*ThemeData*Theme定义时,以下属性必须严格使用后缀:
ThemeData*ThemeData- :使用
cardTheme(而非CardThemeData)CardTheme - :使用
dialogTheme(而非DialogThemeData)DialogTheme - :使用
tabBarTheme(而非TabBarThemeData)TabBarTheme - :使用
appBarTheme(而非AppBarThemeData)AppBarTheme - :使用
bottomAppBarTheme(而非BottomAppBarThemeData)BottomAppBarTheme - :使用
inputDecorationTheme(而非InputDecorationThemeData)InputDecorationTheme
Button Styling
按钮样式
Legacy button classes (, , ) are obsolete.
FlatButtonRaisedButtonOutlineButton- Use ,
TextButton, andElevatedButton.OutlinedButton - Configure button appearance using a object.
ButtonStyle - For simple overrides based on the theme's color scheme, use the static utility method: .
TextButton.styleFrom(foregroundColor: Colors.blue) - For state-dependent styling (hovered, focused, pressed, disabled), use .
MaterialStateProperty.resolveWith
旧版按钮类(、、)已废弃。
FlatButtonRaisedButtonOutlineButton- 使用、
TextButton和ElevatedButton。OutlinedButton - 使用对象配置按钮外观。
ButtonStyle - 若要基于主题配色方案进行简单覆盖,可使用静态工具方法:。
TextButton.styleFrom(foregroundColor: Colors.blue) - 对于依赖状态的样式(悬停、聚焦、按下、禁用),使用。
MaterialStateProperty.resolveWith
Platform Idioms & Adaptive Design
平台规范与自适应设计
When building adaptive apps, respect platform-specific norms to reduce cognitive load and build user trust.
- Scrollbars: Desktop users expect omnipresent scrollbars; mobile users expect them only during scrolling. Toggle on the
thumbVisibilitywidget based on the platform.Scrollbar - Selectable Text: Web and desktop users expect text to be selectable. Wrap text in or
SelectableText.SelectableText.rich - Horizontal Button Order: Windows places confirmation buttons on the left; macOS/Linux place them on the right. Use a with
Rowfor Windows andTextDirection.rtlfor others.TextDirection.ltr - Context Menus & Tooltips: Desktop users expect hover and right-click interactions. Implement for hover states and use context menu packages for right-click actions.
Tooltip
构建自适应应用时,需遵循平台特定规范,以降低用户认知负荷并建立信任。
- 滚动条:桌面用户期望滚动条始终可见;移动用户仅希望滚动时显示。根据平台切换组件的
Scrollbar属性。thumbVisibility - 可选中文本:Web和桌面用户期望文本可选中。将文本包裹在或
SelectableText中。SelectableText.rich - 按钮水平顺序:Windows系统将确认按钮放在左侧;macOS/Linux放在右侧。使用组件,Windows系统设置
Row,其他系统设置TextDirection.rtl。TextDirection.ltr - 上下文菜单与工具提示:桌面用户期望悬停和右键交互。为悬停状态实现,并使用上下文菜单包处理右键操作。
Tooltip
Workflows
工作流程
Workflow: Migrating Legacy Themes to Material 3
工作流程:将旧版主题迁移至Material 3
Use this workflow when updating an older Flutter codebase.
Task Progress:
- 1. Remove from
useMaterial3: false(it is true by default).ThemeData - 2. Replace manual definitions with
ColorScheme.ColorScheme.fromSeed() - 3. Run validator -> review errors -> fix: Search for and replace deprecated ,
accentColor,accentColorBrightness, andaccentIconTheme.accentTextTheme - 4. Run validator -> review errors -> fix: Search for and replace with
AppBarTheme(color: ...).backgroundColor - 5. Update component properties to use
ThemeDataclasses (e.g.,*ThemeData).cardTheme: CardThemeData() - 6. Replace legacy buttons (->
FlatButton,TextButton->RaisedButton,ElevatedButton->OutlineButton).OutlinedButton - 7. Replace legacy navigation components (->
BottomNavigationBar,NavigationBar->Drawer).NavigationDrawer
当更新旧版Flutter代码库时,可使用此工作流程。
任务进度:
- 1. 从中移除
ThemeData(默认值为true)。useMaterial3: false - 2. 用替代手动定义的
ColorScheme.fromSeed()。ColorScheme - 3. 运行验证器 -> 查看错误 -> 修复:搜索并替换已废弃的、
accentColor、accentColorBrightness和accentIconTheme。accentTextTheme - 4. 运行验证器 -> 查看错误 -> 修复:搜索并替换为
AppBarTheme(color: ...)。backgroundColor - 5. 将的组件属性更新为使用
ThemeData类(例如*ThemeData)。cardTheme: CardThemeData() - 6. 替换旧版按钮(->
FlatButton,TextButton->RaisedButton,ElevatedButton->OutlineButton)。OutlinedButton - 7. 替换旧版导航组件(->
BottomNavigationBar,NavigationBar->Drawer)。NavigationDrawer
Workflow: Implementing Adaptive UI Components
工作流程:实现自适应UI组件
Use this workflow when building a widget intended for both mobile and desktop/web.
Task Progress:
- 1. If displaying a list/grid, wrap it in a and set
Scrollbar.thumbVisibility: DeviceType.isDesktop - 2. If displaying read-only data, use instead of
SelectableText.Text - 3. If implementing a dialog with action buttons, check the platform. If Windows, set on the button
TextDirection.rtl.Row - 4. If implementing interactive elements, wrap them in widgets to support mouse hover states.
Tooltip
当构建同时面向移动、桌面和Web的Widget时,可使用此工作流程。
任务进度:
- 1. 若显示列表/网格,将其包裹在中,并根据平台设置
Scrollbar。thumbVisibility: DeviceType.isDesktop - 2. 若显示只读数据,使用而非
SelectableText。Text - 3. 若实现带操作按钮的对话框,检查平台。如果是Windows系统,在按钮中设置
Row。TextDirection.rtl - 4. 若实现交互元素,将其包裹在组件中以支持鼠标悬停状态。
Tooltip
Examples
示例
Example: Modern Material 3 ThemeData Setup
示例:现代Material 3 ThemeData配置
dart
MaterialApp(
title: 'Adaptive App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.light,
),
// Use *ThemeData classes for component normalization
appBarTheme: const AppBarThemeData(
backgroundColor: Colors.deepPurple, // Do not use 'color'
elevation: 0,
),
cardTheme: const CardThemeData(
elevation: 2,
),
textTheme: const TextTheme(
bodyMedium: TextStyle(letterSpacing: 0.2),
),
),
home: const MyHomePage(),
);dart
MaterialApp(
title: 'Adaptive App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.light,
),
// 使用*ThemeData类进行组件规范化
appBarTheme: const AppBarThemeData(
backgroundColor: Colors.deepPurple, // 请勿使用'color'
elevation: 0,
),
cardTheme: const CardThemeData(
elevation: 2,
),
textTheme: const TextTheme(
bodyMedium: TextStyle(letterSpacing: 0.2),
),
),
home: const MyHomePage(),
);Example: State-Dependent ButtonStyle
示例:依赖状态的ButtonStyle
dart
TextButton(
style: ButtonStyle(
// Default color
foregroundColor: MaterialStateProperty.all<Color>(Colors.blue),
// State-dependent overlay color
overlayColor: MaterialStateProperty.resolveWith<Color?>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return Colors.blue.withOpacity(0.04);
}
if (states.contains(MaterialState.focused) || states.contains(MaterialState.pressed)) {
return Colors.blue.withOpacity(0.12);
}
return null; // Defer to the widget's default.
},
),
),
onPressed: () {},
child: const Text('Adaptive Button'),
)dart
TextButton(
style: ButtonStyle(
// 默认颜色
foregroundColor: MaterialStateProperty.all<Color>(Colors.blue),
// 依赖状态的覆盖色
overlayColor: MaterialStateProperty.resolveWith<Color?>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return Colors.blue.withOpacity(0.04);
}
if (states.contains(MaterialState.focused) || states.contains(MaterialState.pressed)) {
return Colors.blue.withOpacity(0.12);
}
return null; // 遵循组件默认值
},
),
),
onPressed: () {},
child: const Text('Adaptive Button'),
)Example: Adaptive Dialog Button Order
示例:自适应对话框按钮顺序
dart
Row(
// Windows expects confirmation on the left (RTL reverses the standard LTR Row)
textDirection: Platform.isWindows ? TextDirection.rtl : TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('Confirm'),
),
],
)dart
Row(
// Windows系统期望确认按钮在左侧(RTL会反转标准LTR的Row顺序)
textDirection: Platform.isWindows ? TextDirection.rtl : TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('Confirm'),
),
],
)