Core Defaults
This chapter describes the ThemeData
modifications FlexColorScheme
core does
when you use it without opting in on the opinionated component themes.
FlexColorScheme does not use the ThemeData.from
factory with a passed
in ColorScheme
to make its ThemeData
object. It uses the ThemeData
factory directly, with
many additional custom theming tweaks.
It of course defines a ColorScheme
that it uses in its
ThemeData
. FlexColorScheme uses color calculations for the primary color branded/blended
surfaces, and for the lazy schemes that do not specify all colors in a color scheme.
It utilizes the Material Color Utilities
from the Google Material theme that Flutter SDK also uses, to make key colors seed
generated computational color schemes. It extends the new M3 CorePalette
in the
Material Color Utilities to be able to tweak the ColorScheme
generation
parameters in it. It also uses its own custom mid-layer Scheme implementation, with
customizable Tone to ColorScheme mapping.
Most other things it does are more basic, it is just a lot of moving parts. However, when just using the core, and not opting in on additional component themes, it is a bit less. Some component themes are however also created by the core setup, they are listed further below.
Design Goals
Flutter's default Theme and its ThemeData use a design where all the component theme's in the default
ThemeData have properties that are all null. It is always the widget component that defines
the default behavior and look when its theme and its properties are undefined.
The widget colors for such cases are then typically defined by ThemeData.colorScheme
.
This Flutter theming design goal is described in this
document. It is
partially implemented, but there are still some components in Flutter remaining that do
not adhere to this design, and there are also many color properties still remaining in ThemeData
.
FlexColorScheme assigns its defined and computed colors to the ThemeData.colorScheme
to
ensure that its colors are applied to all widgets that adhere to this newer standard.
Then it also defines all still existing legacy direct colors in ThemeData
, that some Flutter Widgets
still use. These ThemeData
colors are all set to use ColorScheme
appropriate colors, or from it
derived colors. Thanks to this, there are not any built-in Widgets in Flutter that do not
get color themed by FlexColorScheme. As we can see in Roads to ThemeData, this
is not the case with Flutter's own ColorScheme based theming methods, or any of its
theming methods, you have to adjust ThemeData a bit manually as well to ge there.
The ThemeData created by core FlexColorScheme tries to not create and modify component themes when it does not
have to. However, to meet its own design goals, FlexColorScheme still has to create a few sub-themes and set some
of their properties. In some rare cases this is done to correct theming issues in Flutter SDK, but mostly to
reach its own design goals. Below is a list of all the component themes it creates and properties that you can
currently expect to have none null values in them. These component themes and their properties,
would otherwise be null when just using default ThemeData()
or ThemeData.from()
factories, if they
did not specify any component themes.
Even if some component themes are currently not null, and have some none null properties in FlexColorScheme
based ThemeData
, you should still always use null fallback values when you access use ThemeData sub-themes and
their properties in Theme.of(context)
that are null in default ThemeData
, or any of its component themes.
For example, when using the AppBar's theme data, don't just use:
final Color aColor = Theme.of(context).appBarTheme.backgroundColor;
It should not be considered safe to access properties like it without using null fallbacks. Instead, do something similar to this:
final Color aColor = Theme.of(context)?.appBarTheme?.backgroundColor ??
Theme.of(context).colorScheme.primary;
FlexColorScheme will try to not change past creation of its none null sub-themes. However, changes to these implementation details are only considered breaking if they produce a theme that is visibly different from past behavior.
Flutter SDK may also change some of its implementation details of ThemeData
. Take for example the broken
Chip
case, if it is modified to fix its dark theme bug, then FlexColorScheme might have no reason
for its own fix. In cases where Flutter defaults moves in a direction that makes a custom definition for it
no longer needed in FlexColorScheme, such core sub-theme definitions will eventually be
removed from its core component themes, but not without due deprecation warning first.
One past such case was the floatingActionButtonTheme
sub-theme that FlexColorScheme created in earlier
versions. The used sub-theme did not change Flutter's default behavior. However, in some older versions of
Flutter there was a deprecation warning if the sub-theme was not defined. Later it was
observed that Flutter SDK default and FlexColorScheme sub-theme agreed 100% on the design. There was
no longer any deprecation warning if the sub-theme was totally removed from FlexColorScheme's theme definition.
This sub-theme definition was thus no longer needed and was removed starting from FlexColorScheme
version 2.0.0-nullsafety.2. Another similar change was the deprecation of ThemeData.accentColor
.
Core Component Themes
Here are all the component themes that core FlexColorScheme currently defines:
-
AppBarTheme in
ThemeData.appBarTheme
is NOT null. The actual values are defined to match the offered convenience theming options for the AppBar. The property values depend on made configuration choices.- backgroundColor: Depends on chosen
appBarStyle
. - foregroundColor: Black if brightness of backgroundColor is light otherwise white.
- iconTheme: Not null, defines:
- color: : Depends on chosen
appBarStyle
.
- color: : Depends on chosen
- actionsIconTheme: Not null, defines:
- color: : Depends on chosen
appBarStyle
.
- color: : Depends on chosen
- elevation: As defined, default is 0
- systemOverlayStyle: A custom SystemUiOverlayStyle is defined
- backwardsCompatibility:
false
- backgroundColor: Depends on chosen
-
BottomAppBarTheme in
ThemeData.bottomAppBarTheme
is NOT null.- Color:
colorScheme.background
- elevation: As defined, default is 0
- Color:
-
TextSelectionThemeData in
ThemeData.bottomAppBarTheme
is NOT null.- selectionColor:
dark ? primary.withOpacity(0.50) : primary.withOpacity(0.30)
- selectionHandleColor:
primaryColorDark
- selectionColor:
-
InputDecorationTheme in
ThemeData.inputDecorationTheme
is NOT null.- filled:
true
- fillColor:
dark ? primary.withOpacity(0.06) : primary.withOpacity(0.35)
- filled:
-
ButtonThemeData in
ThemeData.buttonTheme
is NOT null.- colorScheme:
colorScheme
- textTheme:
ButtonTextTheme.primary
- materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap
- padding:
EdgeInsets.symmetric(horizontal: 16)
- colorScheme:
-
ChipThemeData in
ThemeData.chipTheme
is NOT null. It was originally defined to fix issue #65663 and usesChipThemeData.fromDefaults
to set:- secondaryColor:
colorScheme.primary
- brightness:
colorScheme.brightness
- labelStyle:
textTheme.bodyText1
- secondaryColor:
-
TabBarTheme in
ThemeData.tabBarTheme
is NOT null. Its colors depend on if a theme appropriate for current active AppBar background color (default), or one for background and surface color usage is selected. It sets:- indicatorSize:
TabBarIndicatorSize.tab
- labelStyle:
TextTheme().button
- labelColor: Depends on selected and targeted main usage.
- unselectedLabelColor: Depends on selected and targeted main usage.
- indicatorSize:
-
BottomNavigationBarThemeData in
ThemeData.bottomNavigationBarTheme
is NOT null.- selectedIconTheme: Not null, defines:
- color:
colorScheme.primary
- color:
- selectedItemColor:
colorScheme.primary
- selectedIconTheme: Not null, defines:
-
TooltipThemeData in
ThemeData.tooltipTheme
is NOT null. This theme is modified to address issue #71429 and to also enable a toggle that inverts the tooltip colors, which is a style often used on Windows desktops. The toggle can e.g. be used to change the style depending on the current platform. The used logic and theme changes are defined as.- margin:
EdgeInsets.symmetric(horizontal: 12, vertical: 6)
- padding:
desktop ? EdgeInsets.fromLTRB(8, 3, 8, 4) : EdgeInsets.symmetric(horizontal: 16, vertical: 8)
- textStyle:
textTheme.bodyText2.copyWith( inherit: false, color: tooltipsMatchBackground ? dark ? Colors.white : Colors.black : dark ? Colors.black : Colors.white, fontSize: desktop ? 12 : 14)
- decoration:
tooltipsMatchBackground ? BoxDecoration( color: isDark ? const Color(0xED444444) : const Color(0xF0FCFCFC), borderRadius: const BorderRadius.all(Radius.circular(4)), border: Border.all(color: dividerThemeColor)) : null // Use default Flutter SDK decoration.
- margin:
Core ThemeData Customizations
In addition to the primary color branded surfaces, full shaded schemes from just one primary color, true
black and AppBar convenience tricks. The returned ThemeData
contains some opinionated modifications and theme
modifications compared to what you get if you would just use the standard ThemeData.from
a ColorScheme
.
You can still of course override the returned ThemeData
with your own theme modifications and
additions, by using the copyWith
method on the resulting ThemeData
object.
The following lists the differences, compared to the standard ThemeData.from
factory,
as well as the rationale behind the made design choices and changes to the defaults.
-
scaffoldBackgroundColor has its own color property in
FlexColorScheme
and can if so desired differ from theColorScheme.background
color. When using primary color blended surfaces and backgrounds, it is important to be able to vary the very prominentScaffoldBackgroundColor
separately from other surfaces and backgrounds. -
The dialogBackgroundColor uses the
ColorScheme.surface
color instead of the defaultColorScheme.background
. In order to preserve theelevationOverlayColor
in dark mode whenColorScheme.surface
andColorScheme.background
differs due to different surface blends, theColorScheme.surface
was used to ensure dialogs that are always elevated gets the overlay color applied in dark theme mode. For more info see issue #90353. -
The indicatorColor is same as
effectiveTabColor
. It uses a function with logic to determine its color based on if aTabBarTheme
was selected that should work on currentAppBar
background color, or on surface/background colors. -
For toggleableActiveColor the
ColorScheme.secondary
color is used. The Flutter default just uses the defaultThemeData
colors and not the actual colors you define in theColorScheme
you create your theme from. Perhaps an oversight in Flutter? See issue #65782. Also see practical examples in Roads to ThemeData. This color property will be deprecated in Flutter, see issue 91772. -
Flutter themes created with
ThemeData.from
do not define any color scheme related color for the primaryColorDark color, FlexColorScheme does. See issue #65782. TheThemeData.from
leaves this color atThemeData
factory default, this may not match your scheme. Flutter SDK Widgets seldom use this color, so the issue is rarely seen. This color property will be deprecated in Flutter, see issue 91772. -
Flutter themes created with
ThemeData.from
do not define any color scheme based color for the primaryColorLight color, FlexColorScheme does. See issue #65782. TheThemeData.from
leaves this color atThemeData
factory default this may not match your scheme. Flutter SDK Widgets seldom use this color, so the issue is rarely seen. This color property will be deprecated in Flutter, see issue 91772. -
Flutter themes created with
ThemeData.from
do not define any color scheme based color for the secondaryHeaderColor color, FlexColorScheme does. See issue #65782.ThemeData.from
leaves this color atThemeData
factory default this may not match your scheme. Flutter SDK Widgets seldom use this color, so the issue is rarely seen. This color property will be deprecated in Flutter, see issue 91772. -
Background color for AppBarTheme can use a custom themed color in both light and dark themes, that is not dependent on the theme's primary or surface color. In the versions prior to Flutter 2.0.0 doing this was very difficult to do, as presented in #50606. A new feature in Flutter 2.0.0 implemented via: #71184 makes this easy and better. FlexColorScheme's implementation has been changed to use this new AppBarTheme feature starting from version 2.0.0-nullsafety.2.
-
The AppBarTheme elevation defaults to 0, an iOs style influenced opinionated choice. It can easily be adjusted directly in the
FlexColorScheme
definition with property valueappBarElevation
without creating a sub theme or usingcopyWith
. -
The bottomAppBarColor uses color scheme background color to match the background color of the drawer, bottom navigation bar, possible side menu and system navigation bar on android, if theming of it is used. This is a slight change from the ColorScheme default that uses surface color.
-
The BottomAppBarTheme elevation defaults to
appBarElevation
or 0 if it is null. It can easily be adjusted directly in theFlexColorScheme
definition with property valuebottomAppBarElevation
without creating a sub theme or usingcopyWith
. -
In TextSelectionThemeData, the Flutter default for selectionColor is
colorScheme.primary
with opacity value0.4
for dark-mode and0.12
for light mode. Here, primary with 0.5 opacity for dark-mode, and0.3
for light mode is used. The default for selectionHandleColor iscolorScheme.primary
, here we use the slightly darker shade primaryColorDark instead, which does not have a proper color scheme color value in Flutter standardColorScheme
based themes. -
A predefined slightly opinionated InputDecorationTheme is used. It sets
filled
totrue
and fill color to color scheme primary color with opacity0.035
in light mode and with opacity0.06
in dark-mode. Since the used theme, does not define aborder
property ofTextField
, an app can easily use both the default underline style, or the outline style by just specifyingOutlineInputBorder()
, when an outlinedTextField
is desired. If you don't want the filled style, or the primary colored borders in dark-mode, you can override them back withcopyWith
. -
The property fixTextFieldOutlineLabel is set to
true
by default, it looks better. The only reason why it is not the default in Flutter, is for default backwards legacy design compatibility. -
NOTE: Since the old buttons have been deprecated in Flutter 2.0.0 they are no longer presented or used in code in FlexColorScheme and its examples. However, FlexColorScheme still defines the theme for them described below. Defining the theme does not yet cause any deprecation warnings or errors, as long as that is the case, this theming will be kept available to support out of the box nice themes for the old buttons as before. It is however stated and noted in master channel that the next release (after 2.10) of Flutter will completely remove the old buttons and the old
buttonTheme
will then be deprecated and later removed as well. It FlexColorScheme will then need to deprecate and remove the old button theme. -
Button theming is applied to ThemeData.buttonColor using color
colorScheme.primary
color. -
For ThemeData.buttonTheme the entire color scheme is passed to its
colorScheme
property, and it usestextTheme
set toButtonTextTheme.primary
, plus minor changes to padding and tap target size. These modifications make the old buttons almost match the default design and look of their corresponding newer buttons. TheRaisedButton
looks very similar toElevatedButton
,OutlineButton
toOutlinedButton
andFlatButton
toTextButton
. There are some differences in margins and minor details, especially in dark-mode, but they are very similar. -
The default theme for Chips contain a design bug that makes the selected ChoiceChip widget look disabled in dark-mode, regardless if was created with
ThemeData
orThemeData.from
factory. See issue #65663. The ChipThemeData modification used here fixes the issue. -
For TabBarTheme, the Flutter standard selected tab and indicator color is onSurface in dark-mode and onPrimary in light mode, which is designed to fit an AppBar colored TabBar. This is kept, and the default via
FlexTabBarStyle.forAppBar
style, with a minor modification. If AppBar is "light", then black87 is used, not black, this is the same as the textTheme on AppBar in light AppBar brightness. If theFlexTabBarStyle.forBackground
style was used, the selected color is always color scheme primary color, which works well on surface, background and scaffold background colors.The unselected TabBar color when
FlexTabBarStyle.forBackground
style is used, is always the onSurface color with 60% opacity. This is also the color if the AppBar background color brightness is light AND its color is white, surface or background colored. When the styleFlexTabBarStyle.forAppBar
is used, the unselected tab bar color is the selected tab color with 70% opacity. This opacity value is the same as Flutter default for the default theme that is also designed for AppBar usage. -
The BottomNavigationBarThemeData uses color scheme primary color for the selected item in both light and dark theme mode. Flutter default uses primary in light mode, but defaults to secondary color in dark mode. Primary color is a design used on iOS by default for the bottom navigation bar in both theme modes. We agree and think it looks better as the default choice for apps in both theme modes.
-
Default tooltipTheme in Flutter is currently a bit flawed on desktop and web, because it defaults to using a very small font (10dp). See issue #71429. The default theming also does not handle multiline tooltips very well. The here used
TooltipThemeData
theme design, corrects both these issues. It uses 12dp font on desktop and web instead of 10dp, and some padding instead of a height constraint to ensure that multiline tooltips look nice too. -
FlexColorScheme tooltips also include a boolean property tooltipsMatchBackground, that can be toggled to not use Flutter's Material default design that has a theme mode inverted background. Tooltips using light background in light theme and dark in dark, are commonly used on the Windows desktop platform. You can easily tie this extra property to the used platform to make an automatic platform adaptation of the tooltip style if you like. You can also use it to give users a preference toggle where they change the tooltip style to their liking.
-
The FlexColorScheme property transparentStatusBar is set to true by default. It is used to make the AppBar one-toned on Android devices, like on iOS devices. Set it to
false
if you want to restore the default Android two toned design.
Optional Component Themes
FlexColorScheme also offers highly opinionated widget component themes that enables you to get more heavily styled and themed widgets automatically. You can customize these styles further via FlexSubThemesData.
These component themes are as stated opinionated design choices. You may or may not like them. They can be modified and tuned, some of them only a bit, while others offer quite extensive quick configuration options. The intent is to keep the component themes visual results consistent from version to version. However, changes in the SDK and supporting new features may require minor style breaking changes to them from time to time.
The defaults for these mostly still Flutter Material 2 based using component themes, draw inspiration from Material 3. Their defaults follow the Material 3 Design Guide, within reasonable limits of current Flutter Material 2 based theming capabilities.
The component themes will be modified and extended when Material 3 features reaches Flutter stable channel, to use actual Material 3 implementations in Flutter. Since Material 3 will eventually become the new normal, FlexColorScheme component themes will potentially make some opinionated design choices concerning them in future versions, this remains to be seen.
To enable the optional component theming feature in FlexColorScheme
, pass in
a default FlexSubThemesData()
to its subThemesData
property.
This gives you the FlexColorScheme
default opinionated component theme setup.
Naturally FlexColorScheme
would not be "flex" if the
FlexSubThemesData
data class did not also contain many optional quick configuration parameters
for the component themes.
The list of properties in the configuration class has grown very large.
While it can be a bit tricky to maintain it, using it is easy and quite
a convenient way to adjust commonly used properties on UI component
widget themes using simple "flat" property values.
No need for deep ShapeBorder
definitions one very component theme for a
simple border radius change, nor for the complex MaterialState
properties.
At least not as long as choices and offered flat config options covers
what you need.
A common use case for FlexSubThemes
and is easy to
use customization of default border radius on all Flutter UI components
and elements that supports border radius either via [ShapeBorder]
or [BorderRadiusGeometry]. This is be done with a single property, the
defaultRadius
.
When you opt-in on using the component themes, the FlexColorScheme.toTheme
method uses
the passed in FlexSubThemesData
configuration data object, passed in via
FlexColorScheme.subThemesData
, to create the component themes.
In some cases, typically for older core related "legacy" component theme cases,
the themes are created directly in the toTheme
method.
However, in most cases separate static sub-theme helper functions
from the FlexSubThemes
class are used. The component themes that are defined
directly in the toTheme
method, will be moved into the FlexSubThemes
class
as well in a future update.
You can also use these static component theme helpers without using FlexColorScheme to define custom components themes, or even without using FlexColorScheme at all.
The FlexSubThemesData
configuration has
no direct impact on these static helpers. It is FlexColorScheme
that uses
the FlexSubThemesData
class to configure the opt-in sub-themes based on
the setup information provided via FlexColorScheme.subThemesData
.
There are component theming helpers available for:
ButtonThemeData
for old deprecated buttons, viabuttonTheme
.BottomNavigationBarThemeData
forBottomNavigationBar
viabottomNavigationBar
.BottomSheetThemeData
forBottomSheet
viabottomSheetTheme
.CardTheme
forCard
viacardTheme
.CheckboxThemeData
forCheckbox
viacheckboxTheme
.ChipThemeData
forChip
viachipTheme
.DialogTheme
forDialog
viadialogTheme
.ElevatedButtonThemeData
forElevatedButton
viaelevatedButtonTheme
.FloatingActionButtonThemeData
forFloatingActionButton
viafloatingActionButtonTheme
.InputDecorationTheme
forInputDecoration
viainputDecorationTheme
.NavigationBarThemeData
forNavigationBar
vianavigationBarTheme
.NavigationRailThemeData
forNavigationRail
vianavigationRailTheme
.OutlinedButtonThemeData
forOutlinedButton
viaoutlinedButtonTheme
.PopupMenuThemeData
forPopupMenuButton
viapopupMenuTheme
.RadioThemeData
forRadio
viaradioTheme
.SnackBarThemeData
forSnackBar
viasnackBarTheme
.SwitchThemeData
forSwitch
viaswitchTheme
.TextButtonThemeData
forTextButton
viatextButtonTheme
.TimePickerThemeData
forTimePickerDialog
viatimePickerTheme
.ToggleButtonsThemeData
forToggleButtons
viatoggleButtonsTheme
.
In ToggleButtons
hover, press, selected and focus states are not
an exact match for the main buttons. It does not have as flexible styling
as the main buttons. The theme mimics the style of the OutlinedButton
for
not selected buttons, and the style of ElevatedButton
for selected
button. It does not support MaterialStateProperty
and has only
one state for different parts of the button. The selected and not selected,
states would need different property values to be able to match the general
buttons. It can therefore not fully match the same theme style, as the
Material states used on two different ButtonStyleButton
buttons that
it should match, but it is quite close.
The theme ButtonThemeData
is included to provide a very similar
theme style on the deprecated legacy buttons RaisedButton
,
OutlineButton
and FlatButton
as on the current main buttons. It is not
an exact match, since the legacy buttons do not offer as flexible
styling as the newer buttons. The legacy button theme follows and match the
styling on ToggleButtons
when it comes to hover, press, selected and focus.
Please consider phasing out the legacy buttons, as they are deprecated and
will be removed from the Flutter SDK in next stable release. Their theme,
the ButtonThemeData
will also soon be deprecated and later removed in Flutter.
The following widgets have rounded corners, but are excluded from the component border radius theming for now:
Tooltip
, generally so small that larger prominent rounding the opinionated sub-theming is designed for, is not a good fit. FlexColorScheme does include out of the box theming options for tooltips, that also adapts to color branding when opting in on sub themes, it also gets a bit more rounded when opting in.Scrollbar
, rounding on edges of scrollbars are left to platform default.- The
AppBar
andBottomAppBar
shape properties are left to their defaults. SnackBar
the floating SnackBar should be themed to also include border radius, but the none floating one should remain straight. This cannot can be done via current theming features.Drawer
should have have rounding on shown side edge, it is in recent version (2.8.0) possible to assign a Shape in the drawer theme. But Drawer uses same theme, when used as Drawer and EndDrawer and the rounding should be on end edge on Drawer and start edge in EndDrawer. It seem we cannot do this without having two Shapes in its theme or other usage behavior modifying it. A default behavior in SDK can implement it by looking up if Drawer is being used in Scaffold as Drawer or EndDrawer, but support via theme is needed too.
You can find more information about available component theme
helpers in the API docs.
You can as mentioned in the API guide also use these static sub-theme helpers
to manually define widget sub-theme, and even further modify them using
their copyWith
methods.