Skip to content

Commit d5751be

Browse files
authored
High contrast color scheme based on system forced colors (#165068)
This PR introduces a `bool useSystemColors` parameter to the `ThemeData` constructor. The goal from this PR is to enable users to easily create high contrast themes that are based on system colors for their `MaterialApp`: ```dart MaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), highContrastTheme: ThemeData(useSystemColors: true, ...), highContrastDarkTheme: ThemeData(useSystemColors: true, ...), ) ``` The `MaterialApp` widget will automatically pick the correct one of the 4 themes based on system settings (light/dark mode, high contrast enabled/disabled). Depends on flutter/flutter#164933 Closes flutter/flutter#118853
1 parent 39103d9 commit d5751be

File tree

2 files changed

+542
-2
lines changed

2 files changed

+542
-2
lines changed

packages/flutter/lib/src/material/theme_data.dart

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/// @docImport 'package:flutter/material.dart';
66
library;
77

8-
import 'dart:ui' show Color, lerpDouble;
8+
import 'dart:ui' show Color, SystemColor, SystemColorPalette, lerpDouble;
99

1010
import 'package:flutter/cupertino.dart';
1111
import 'package:flutter/foundation.dart';
@@ -32,8 +32,10 @@ import 'dialog_theme.dart';
3232
import 'divider_theme.dart';
3333
import 'drawer_theme.dart';
3434
import 'dropdown_menu_theme.dart';
35+
import 'elevated_button.dart';
3536
import 'elevated_button_theme.dart';
3637
import 'expansion_tile_theme.dart';
38+
import 'filled_button.dart';
3739
import 'filled_button_theme.dart';
3840
import 'floating_action_button_theme.dart';
3941
import 'icon_button_theme.dart';
@@ -50,6 +52,7 @@ import 'menu_theme.dart';
5052
import 'navigation_bar_theme.dart';
5153
import 'navigation_drawer_theme.dart';
5254
import 'navigation_rail_theme.dart';
55+
import 'outlined_button.dart';
5356
import 'outlined_button_theme.dart';
5457
import 'page_transitions_theme.dart';
5558
import 'popup_menu_theme.dart';
@@ -63,6 +66,7 @@ import 'slider_theme.dart';
6366
import 'snack_bar_theme.dart';
6467
import 'switch_theme.dart';
6568
import 'tab_bar_theme.dart';
69+
import 'text_button.dart';
6670
import 'text_button_theme.dart';
6771
import 'text_selection_theme.dart';
6872
import 'text_theme.dart';
@@ -246,6 +250,11 @@ class ThemeData with Diagnosticable {
246250
/// a component theme parameter like [sliderTheme], [toggleButtonsTheme],
247251
/// or [bottomNavigationBarTheme].
248252
///
253+
/// When [useSystemColors] is true and the platform supports system colors, then the system colors
254+
/// will be used to override certain theme colors. The [colorScheme], [textTheme],
255+
/// [elevatedButtonTheme], [outlinedButtonTheme], [textButtonTheme], [filledButtonTheme], and
256+
/// [floatingActionButtonTheme] are overriden by the system colors.
257+
///
249258
/// See also:
250259
///
251260
/// * [ThemeData.from], which creates a ThemeData from a [ColorScheme].
@@ -270,6 +279,7 @@ class ThemeData with Diagnosticable {
270279
ScrollbarThemeData? scrollbarTheme,
271280
InteractiveInkFeatureFactory? splashFactory,
272281
bool? useMaterial3,
282+
bool? useSystemColors,
273283
VisualDensity? visualDensity,
274284
// COLOR
275285
ColorScheme? colorScheme,
@@ -387,6 +397,7 @@ class ThemeData with Diagnosticable {
387397
scrollbarTheme ??= const ScrollbarThemeData();
388398
visualDensity ??= VisualDensity.defaultDensityForPlatform(platform);
389399
useMaterial3 ??= true;
400+
useSystemColors ??= false;
390401
final bool useInkSparkle = platform == TargetPlatform.android && !kIsWeb;
391402
splashFactory ??=
392403
useMaterial3
@@ -557,7 +568,8 @@ class ThemeData with Diagnosticable {
557568
buttonBarTheme ??= const ButtonBarThemeData();
558569
dialogBackgroundColor ??= isDark ? Colors.grey[800]! : Colors.white;
559570
indicatorColor ??= colorScheme.secondary == primaryColor ? Colors.white : colorScheme.secondary;
560-
return ThemeData.raw(
571+
572+
ThemeData theme = ThemeData.raw(
561573
// For the sanity of the reader, make sure these properties are in the same
562574
// order in every place that they are separated by section comments (e.g.
563575
// GENERAL CONFIGURATION). Each section except for deprecations should be
@@ -651,6 +663,11 @@ class ThemeData with Diagnosticable {
651663
dialogBackgroundColor: dialogBackgroundColor,
652664
indicatorColor: indicatorColor,
653665
);
666+
667+
if (useSystemColors) {
668+
theme = theme._overrideWithSystemColors();
669+
}
670+
return theme;
654671
}
655672

656673
/// Create a [ThemeData] given a set of exact values. Most values must be
@@ -1763,6 +1780,115 @@ class ThemeData with Diagnosticable {
17631780
});
17641781
}
17651782

1783+
ThemeData _overrideWithSystemColors() {
1784+
if (!SystemColor.platformProvidesSystemColors) {
1785+
return this;
1786+
}
1787+
1788+
final SystemColorPalette systemColors =
1789+
brightness == Brightness.dark ? SystemColor.dark : SystemColor.light;
1790+
1791+
ThemeData theme = this;
1792+
1793+
theme = theme.copyWith(
1794+
colorScheme: colorScheme.copyWith(
1795+
secondary: systemColors.accentColor.value,
1796+
onSecondary: systemColors.accentColorText.value,
1797+
surface: systemColors.canvas.value,
1798+
onSurface: systemColors.canvasText.value,
1799+
),
1800+
textTheme: textTheme.apply(
1801+
displayColor: systemColors.canvasText.value,
1802+
bodyColor: systemColors.canvasText.value,
1803+
),
1804+
);
1805+
1806+
final bool overrideButtons =
1807+
systemColors.buttonFace.value != null ||
1808+
systemColors.buttonBorder.value != null ||
1809+
systemColors.buttonText.value != null;
1810+
1811+
if (overrideButtons) {
1812+
theme = theme.copyWith(
1813+
elevatedButtonTheme: ElevatedButtonThemeData(
1814+
style: ElevatedButton.styleFrom(
1815+
foregroundColor: systemColors.buttonText.value,
1816+
backgroundColor: systemColors.buttonFace.value,
1817+
side:
1818+
systemColors.buttonBorder.value == null
1819+
? null
1820+
: BorderSide(color: systemColors.buttonBorder.value!),
1821+
),
1822+
),
1823+
textButtonTheme: TextButtonThemeData(
1824+
style: TextButton.styleFrom(
1825+
foregroundColor: systemColors.buttonText.value,
1826+
backgroundColor: systemColors.buttonFace.value,
1827+
side:
1828+
systemColors.buttonBorder.value == null
1829+
? null
1830+
: BorderSide(color: systemColors.buttonBorder.value!),
1831+
),
1832+
),
1833+
outlinedButtonTheme: OutlinedButtonThemeData(
1834+
style: OutlinedButton.styleFrom(
1835+
foregroundColor: systemColors.buttonText.value,
1836+
backgroundColor: systemColors.buttonFace.value,
1837+
side:
1838+
systemColors.buttonBorder.value == null
1839+
? null
1840+
: BorderSide(color: systemColors.buttonBorder.value!),
1841+
),
1842+
),
1843+
filledButtonTheme: FilledButtonThemeData(
1844+
style: FilledButton.styleFrom(
1845+
foregroundColor: systemColors.buttonText.value,
1846+
backgroundColor: systemColors.buttonFace.value,
1847+
side:
1848+
systemColors.buttonBorder.value == null
1849+
? null
1850+
: BorderSide(color: systemColors.buttonBorder.value!),
1851+
),
1852+
),
1853+
floatingActionButtonTheme: FloatingActionButtonThemeData(
1854+
backgroundColor: systemColors.buttonFace.value,
1855+
foregroundColor: systemColors.buttonText.value,
1856+
),
1857+
);
1858+
}
1859+
1860+
final bool overrideInputDecoration =
1861+
systemColors.field.value != null || systemColors.fieldText.value != null;
1862+
1863+
if (overrideInputDecoration) {
1864+
theme = theme.copyWith(
1865+
inputDecorationTheme: inputDecorationTheme.copyWith(
1866+
fillColor: systemColors.field.value,
1867+
labelStyle:
1868+
inputDecorationTheme.labelStyle?.copyWith(color: systemColors.fieldText.value) ??
1869+
TextStyle(color: systemColors.fieldText.value),
1870+
hintStyle:
1871+
inputDecorationTheme.hintStyle?.copyWith(color: systemColors.fieldText.value) ??
1872+
TextStyle(color: systemColors.fieldText.value),
1873+
helperStyle:
1874+
inputDecorationTheme.helperStyle?.copyWith(color: systemColors.fieldText.value) ??
1875+
TextStyle(color: systemColors.fieldText.value),
1876+
prefixStyle:
1877+
inputDecorationTheme.prefixStyle?.copyWith(color: systemColors.fieldText.value) ??
1878+
TextStyle(color: systemColors.fieldText.value),
1879+
suffixStyle:
1880+
inputDecorationTheme.suffixStyle?.copyWith(color: systemColors.fieldText.value) ??
1881+
TextStyle(color: systemColors.fieldText.value),
1882+
counterStyle:
1883+
inputDecorationTheme.counterStyle?.copyWith(color: systemColors.fieldText.value) ??
1884+
TextStyle(color: systemColors.fieldText.value),
1885+
),
1886+
);
1887+
}
1888+
1889+
return theme;
1890+
}
1891+
17661892
/// Linearly interpolate between two themes.
17671893
///
17681894
/// {@macro dart.ui.shadow.lerp}

0 commit comments

Comments
 (0)