flutter-theming-apps
Original:🇺🇸 English
Not Translated
Customizes the visual appearance of a Flutter app using the theming system. Use when defining global styles, colors, or typography for an application.
3installs
Sourcegsmlg-dev/code-agent
Added on
NPX Install
npx skill4agent add gsmlg-dev/code-agent flutter-theming-appsSKILL.md Content
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 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
Material 3 Guidelines
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
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
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
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
Workflows
Workflow: Migrating Legacy Themes to 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
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 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
Examples
Example: Modern Material 3 ThemeData Setup
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(),
);Example: State-Dependent 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'),
)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'),
),
],
)