Updates and manages Flutter application styling by migrating legacy Material 2 implementations to Material 3, normalizing component themes, updating deprecated button classes, and adapting UI idioms for cross-platform consistency. Assumes a Flutter environment using Dart.
-
Analyze Current Theme State
Review the existing Flutter codebase to identify legacy Material 2 components, deprecated button classes (FlatButton, RaisedButton, OutlineButton), and outdated theme properties (e.g., accentColor, color in AppBarTheme).
STOP AND ASK THE USER: "What is the primary seed color for the new Material 3 ColorScheme, and which target platforms (iOS, Android, Windows, macOS, Linux, Web) should be prioritized for platform idioms?"
-
Decision Logic: Component Migration
When encountering legacy widgets, use the following decision tree to determine the replacement:
- IF
BottomNavigationBar -> REPLACE WITH NavigationBar (uses NavigationDestination).
- IF
Drawer -> REPLACE WITH NavigationDrawer (uses NavigationDrawerDestination).
- IF
ToggleButtons -> REPLACE WITH SegmentedButton (uses ButtonSegment and Set for selection).
- IF
FlatButton -> REPLACE WITH TextButton.
- IF
RaisedButton -> REPLACE WITH ElevatedButton (or FilledButton for no elevation).
- IF
OutlineButton -> REPLACE WITH OutlinedButton.
-
Implement App-Wide Material 3 Theme
Define the global ThemeData using ColorScheme.fromSeed. Ensure useMaterial3 is implicitly or explicitly true. Remove all references to deprecated accent properties (accentColor, accentColorBrightness, accentTextTheme, accentIconTheme).
MaterialApp(
title: 'App Name',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.light,
),
// Use colorScheme.secondary instead of accentColor
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
),
home: const MyHomePage(),
);
-
Normalize Component Themes
Update all component theme definitions in ThemeData to use their *ThemeData equivalents. Do not use the base theme classes for configuration.
cardTheme -> CardThemeData
dialogTheme -> DialogThemeData
tabBarTheme -> TabBarThemeData
appBarTheme -> AppBarThemeData (Replace color with backgroundColor)
bottomAppBarTheme -> BottomAppBarThemeData
inputDecorationTheme -> InputDecorationThemeData
ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
appBarTheme: const AppBarThemeData(
backgroundColor: Colors.blue, // Do not use 'color'
elevation: 4.0,
),
cardTheme: const CardThemeData(
elevation: 2.0,
),
);
-
Migrate Buttons and Button Styles
Replace legacy buttons. Use the styleFrom() static method for simple overrides, or ButtonStyle with MaterialStateProperty for state-dependent styling.
// Simple override using styleFrom
TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
disabledForegroundColor: Colors.red,
),
onPressed: () {},
child: const Text('TextButton'),
)
// State-dependent override using MaterialStateProperty
OutlinedButton(
style: ButtonStyle(
side: MaterialStateProperty.resolveWith<BorderSide>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return const BorderSide(color: Colors.blue, width: 2);
}
return const BorderSide(color: Colors.grey, width: 1);
}
),
),
onPressed: () {},
child: const Text('OutlinedButton'),
)
-
Decision Logic: Platform Idioms
Apply platform-specific adaptations based on the host OS to reduce cognitive load and build user trust.
- Scrollbars: IF desktop platform -> Set
thumbVisibility: true (or alwaysShown). IF mobile -> Use default auto-hiding behavior.
- Text Selection: IF text is informational and non-interactive -> Wrap in
SelectableText to allow mouse selection on Web/Desktop.
- Button Ordering: IF Windows -> Place confirmation button on the LEFT. IF macOS/Linux/Android/iOS -> Place confirmation button on the RIGHT.
// Platform-aware button ordering
TextDirection btnDirection = Platform.isWindows
? TextDirection.rtl
: TextDirection.ltr;
Row(
children: [
const Spacer(),
Row(
textDirection: btnDirection,
children: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('OK'),
),
],
),
],
)
-
Validate-and-Fix
Scan the generated code to verify that no FlatButton, RaisedButton, OutlineButton, or ButtonTheme classes remain. Verify that AppBarTheme does not use the color property. Fix any instances found.