Customizes the visual appearance of a Flutter app using the theming system. Use when defining global styles, colors, or typography for an application.
Flutter applies styling in a strict hierarchy: styles applied to the specific widget -> themes that override the immediate parent theme -> the main app theme.
theme property of MaterialApp with a ThemeData instance.Theme widget and using Theme.of(context).copyWith(...).ThemeData properties:
accentColor with colorScheme.secondary.accentTextTheme with textTheme (using colorScheme.onSecondary for contrast).AppBarTheme.color with AppBarTheme.backgroundColor.Material 3 is the default theme as of Flutter 3.16.
ColorScheme.fromSeed(seedColor: Colors.blue). This ensures accessible contrast ratios.ColorScheme.surfaceTint to indicate elevation instead of just drop shadows. To revert to M2 shadow behavior, set surfaceTint: Colors.transparent and define a shadowColor.letterSpacing on the specific TextStyle.BottomNavigationBar with NavigationBar.Drawer with NavigationDrawer.ToggleButtons with SegmentedButton.FilledButton for a high-emphasis button without the elevation of ElevatedButton.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)Legacy button classes (FlatButton, RaisedButton, OutlineButton) are obsolete.
TextButton, ElevatedButton, and OutlinedButton.ButtonStyle object.TextButton.styleFrom(foregroundColor: Colors.blue).MaterialStateProperty.resolveWith.When building adaptive apps, respect platform-specific norms to reduce cognitive load and build user trust.
thumbVisibility on the Scrollbar widget based on the platform.SelectableText or SelectableText.rich.Row with TextDirection.rtl for Windows and TextDirection.ltr for others.Tooltip for hover states and use context menu packages for right-click actions.Use this workflow when updating an older Flutter codebase.
Task Progress:
useMaterial3: false from ThemeData (it is true by default).ColorScheme definitions with ColorScheme.fromSeed().accentColor, accentColorBrightness, accentIconTheme, and accentTextTheme.AppBarTheme(color: ...) and replace with backgroundColor.ThemeData component properties to use *ThemeData classes (e.g., cardTheme: CardThemeData()).FlatButton -> TextButton, RaisedButton -> ElevatedButton, OutlineButton -> OutlinedButton).BottomNavigationBar -> NavigationBar, Drawer -> NavigationDrawer).Use this workflow when building a widget intended for both mobile and desktop/web.
Task Progress:
Scrollbar and set thumbVisibility: DeviceType.isDesktop.SelectableText instead of Text.TextDirection.rtl on the button Row.Tooltip widgets to support mouse hover states.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(),
);
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'),
)
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'),
),
],
)