skills/flutter/skills/flutter-theming-apps

flutter-theming-apps

SKILL.md

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

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'),
    ),
  ],
)
Weekly Installs
778
Repository
flutter/skills
GitHub Stars
685
First Seen
3 days ago
Installed on
codex765
opencode763
gemini-cli762
kimi-cli761
github-copilot761
cursor761