- Implementing Flutter Theming and Adaptive Design
- Contents
- Core Theming Concepts
- Material 3 Guidelines
- Component Theme Normalization
- Button Styling
- Platform Idioms & Adaptive Design
- Workflows
- Examples
- 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
- theme
- property of
- MaterialApp
- with a
- ThemeData
- instance.
- Override themes for specific widget subtrees by wrapping them in a
- Theme
- widget and using
- Theme.of(context).copyWith(...)
- .
- Do not
- use deprecated
- ThemeData
- properties:
- Replace
- accentColor
- with
- colorScheme.secondary
- .
- Replace
- accentTextTheme
- with
- textTheme
- (using
- colorScheme.onSecondary
- for contrast).
- Replace
- AppBarTheme.color
- with
- AppBarTheme.backgroundColor
- .
- Material 3 Guidelines
- Material 3 is the default theme as of Flutter 3.16.
- Colors:
- Generate color schemes using
- ColorScheme.fromSeed(seedColor: Colors.blue)
- . This ensures accessible contrast ratios.
- Elevation:
- Material 3 uses
- ColorScheme.surfaceTint
- to indicate elevation instead of just drop shadows. To revert to M2 shadow behavior, set
- surfaceTint: Colors.transparent
- and define a
- shadowColor
- .
- Typography:
- Material 3 updates font sizes, weights, and line heights. If text wrapping breaks legacy layouts, adjust
- letterSpacing
- on the specific
- TextStyle
- .
- Modern Components:
- Replace
- BottomNavigationBar
- with
- NavigationBar
- .
- Replace
- Drawer
- with
- NavigationDrawer
- .
- Replace
- ToggleButtons
- with
- SegmentedButton
- .
- Use
- FilledButton
- for a high-emphasis button without the elevation of
- ElevatedButton
- .
- Component Theme Normalization
- Component themes in
- ThemeData
- have been normalized to use
- *ThemeData
- classes rather than
- *Theme
- widgets.
- When defining
- ThemeData
- , strictly use the
- *ThemeData
- suffix for the following properties:
- cardTheme
-
- Use
- CardThemeData
- (Not
- CardTheme
- )
- dialogTheme
-
- Use
- DialogThemeData
- (Not
- DialogTheme
- )
- tabBarTheme
-
- Use
- TabBarThemeData
- (Not
- TabBarTheme
- )
- appBarTheme
-
- Use
- AppBarThemeData
- (Not
- AppBarTheme
- )
- bottomAppBarTheme
-
- Use
- BottomAppBarThemeData
- (Not
- BottomAppBarTheme
- )
- inputDecorationTheme
- Use
InputDecorationThemeData
(Not
InputDecorationTheme
)
Button Styling
Legacy button classes (
FlatButton
,
RaisedButton
,
OutlineButton
) are obsolete.
Use
TextButton
,
ElevatedButton
, and
OutlinedButton
.
Configure button appearance using a
ButtonStyle
object.
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
.
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
thumbVisibility
on the
Scrollbar
widget based on the platform.
Selectable Text:
Web and desktop users expect text to be selectable. Wrap text in
SelectableText
or
SelectableText.rich
.
Horizontal Button Order:
Windows places confirmation buttons on the left; macOS/Linux place them on the right. Use a
Row
with
TextDirection.rtl
for Windows and
TextDirection.ltr
for others.
Context Menus & Tooltips:
Desktop users expect hover and right-click interactions. Implement
Tooltip
for hover states and use context menu packages for right-click actions.
Workflows
Workflow: Migrating Legacy Themes to Material 3
Use this workflow when updating an older Flutter codebase.
Task Progress:
1. Remove
useMaterial3: false
from
ThemeData
(it is true by default).
2. Replace manual
ColorScheme
definitions with
ColorScheme.fromSeed()
.
3. Run validator -> review errors -> fix: Search for and replace deprecated
accentColor
,
accentColorBrightness
,
accentIconTheme
, and
accentTextTheme
.
4. Run validator -> review errors -> fix: Search for
AppBarTheme(color: ...)
and replace with
backgroundColor
.
5. Update
ThemeData
component properties to use
ThemeData
classes (e.g.,
cardTheme: CardThemeData()
).
6. Replace legacy buttons (
FlatButton
->
TextButton
,
RaisedButton
->
ElevatedButton
,
OutlineButton
->
OutlinedButton
).
7. Replace legacy navigation components (
BottomNavigationBar
->
NavigationBar
,
Drawer
->
NavigationDrawer
).
Workflow: Implementing Adaptive UI Components
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
Scrollbar
and set
thumbVisibility: DeviceType.isDesktop
.
2. If displaying read-only data, use
SelectableText
instead of
Text
.
3. If implementing a dialog with action buttons, check the platform. If Windows, set
TextDirection.rtl
on the button
Row
.
4. If implementing interactive elements, wrap them in
Tooltip
widgets to support mouse hover states.
Examples
Example: Modern Material 3 ThemeData Setup
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
(
)
,
)
;
Example: State-Dependent ButtonStyle
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' ) , ) Example: Adaptive Dialog Button Order 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' ) , ) , ] , )
flutter-theming-apps
安装
npx skills add https://github.com/flutter/skills --skill flutter-theming-apps