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
Added on

NPX Install

npx skill4agent add gsmlg-dev/code-agent flutter-theming-apps

SKILL.md Content

Implementing Flutter Theming and Adaptive Design

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
    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

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'),
    ),
  ],
)