Skip to content

Commit bf3e856

Browse files
Add backgroundColor to RadioThemeData (#171326)
Part of flutter/flutter#168787 Follow-up of flutter/flutter#171204 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent 10298ed commit bf3e856

4 files changed

Lines changed: 92 additions & 26 deletions

File tree

dev/tools/gen_defaults/lib/radio_template.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ class _RadioDefaultsM3 extends RadioThemeData {
8888
8989
@override
9090
VisualDensity get visualDensity => _theme.visualDensity;
91+
92+
@override
93+
WidgetStateProperty<Color> get backgroundColor =>
94+
WidgetStateProperty.all<Color>(Colors.transparent);
9195
}
9296
''';
9397
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ class Radio<T> extends StatefulWidget {
418418
/// {@endtemplate}
419419
final bool? enabled;
420420

421+
/// {@template flutter.material.Radio.backgroundColor}
421422
/// The color of the background of the radio button, in all [WidgetState]s.
422423
///
423424
/// Resolves in the following states:
@@ -427,6 +428,7 @@ class Radio<T> extends StatefulWidget {
427428
/// * [WidgetState.disabled].
428429
///
429430
/// If null, then it is transparent in all states.
431+
/// {@endtemplate}
430432
final WidgetStateProperty<Color?>? backgroundColor;
431433

432434
/// The side for the circular border of the radio button, in all
@@ -673,11 +675,14 @@ class _RadioPaintState extends State<_RadioPaint> {
673675
radioTheme.fillColor?.resolve(inactiveStates);
674676
final Color effectiveInactiveColor =
675677
inactiveColor ?? defaults.fillColor!.resolve(inactiveStates)!;
676-
// TODO(ValentinVignal): Add backgroundColor to RadioThemeData.
677678
final Color activeBackgroundColor =
678-
widget.backgroundColor?.resolve(activeStates) ?? Colors.transparent;
679+
widget.backgroundColor?.resolve(activeStates) ??
680+
radioTheme.backgroundColor?.resolve(activeStates) ??
681+
defaults.backgroundColor!.resolve(activeStates)!;
679682
final Color inactiveBackgroundColor =
680-
widget.backgroundColor?.resolve(inactiveStates) ?? Colors.transparent;
683+
widget.backgroundColor?.resolve(inactiveStates) ??
684+
radioTheme.backgroundColor?.resolve(inactiveStates) ??
685+
defaults.backgroundColor!.resolve(inactiveStates)!;
681686

682687
final Set<MaterialState> focusedStates =
683688
widget.toggleableState.states..add(MaterialState.focused);
@@ -908,6 +913,10 @@ class _RadioDefaultsM2 extends RadioThemeData {
908913

909914
@override
910915
VisualDensity get visualDensity => _theme.visualDensity;
916+
917+
@override
918+
WidgetStateProperty<Color> get backgroundColor =>
919+
WidgetStateProperty.all<Color>(Colors.transparent);
911920
}
912921

913922
// BEGIN GENERATED TOKEN PROPERTIES - Radio<T>
@@ -992,6 +1001,10 @@ class _RadioDefaultsM3 extends RadioThemeData {
9921001

9931002
@override
9941003
VisualDensity get visualDensity => _theme.visualDensity;
1004+
1005+
@override
1006+
WidgetStateProperty<Color> get backgroundColor =>
1007+
WidgetStateProperty.all<Color>(Colors.transparent);
9951008
}
9961009
// dart format on
9971010

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class RadioThemeData with Diagnosticable {
4949
this.splashRadius,
5050
this.materialTapTargetSize,
5151
this.visualDensity,
52+
this.backgroundColor,
5253
});
5354

5455
/// {@macro flutter.widget.RawRadio.mouseCursor}
@@ -86,6 +87,9 @@ class RadioThemeData with Diagnosticable {
8687
/// default value is the value of [ThemeData.visualDensity].
8788
final VisualDensity? visualDensity;
8889

90+
/// {@macro flutter.material.Radio.backgroundColor}
91+
final WidgetStateProperty<Color?>? backgroundColor;
92+
8993
/// Creates a copy of this object but with the given fields replaced with the
9094
/// new values.
9195
RadioThemeData copyWith({
@@ -95,6 +99,7 @@ class RadioThemeData with Diagnosticable {
9599
double? splashRadius,
96100
MaterialTapTargetSize? materialTapTargetSize,
97101
VisualDensity? visualDensity,
102+
WidgetStateProperty<Color?>? backgroundColor,
98103
}) {
99104
return RadioThemeData(
100105
mouseCursor: mouseCursor ?? this.mouseCursor,
@@ -103,6 +108,7 @@ class RadioThemeData with Diagnosticable {
103108
splashRadius: splashRadius ?? this.splashRadius,
104109
materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
105110
visualDensity: visualDensity ?? this.visualDensity,
111+
backgroundColor: backgroundColor ?? this.backgroundColor,
106112
);
107113
}
108114

@@ -125,6 +131,12 @@ class RadioThemeData with Diagnosticable {
125131
),
126132
splashRadius: lerpDouble(a?.splashRadius, b?.splashRadius, t),
127133
visualDensity: t < 0.5 ? a?.visualDensity : b?.visualDensity,
134+
backgroundColor: WidgetStateProperty.lerp<Color?>(
135+
a?.backgroundColor,
136+
b?.backgroundColor,
137+
t,
138+
Color.lerp,
139+
),
128140
);
129141
}
130142

@@ -136,6 +148,7 @@ class RadioThemeData with Diagnosticable {
136148
splashRadius,
137149
materialTapTargetSize,
138150
visualDensity,
151+
backgroundColor,
139152
);
140153

141154
@override
@@ -152,7 +165,8 @@ class RadioThemeData with Diagnosticable {
152165
other.overlayColor == overlayColor &&
153166
other.splashRadius == splashRadius &&
154167
other.materialTapTargetSize == materialTapTargetSize &&
155-
other.visualDensity == visualDensity;
168+
other.visualDensity == visualDensity &&
169+
other.backgroundColor == backgroundColor;
156170
}
157171

158172
@override
@@ -190,6 +204,13 @@ class RadioThemeData with Diagnosticable {
190204
properties.add(
191205
DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: null),
192206
);
207+
properties.add(
208+
DiagnosticsProperty<WidgetStateProperty<Color?>>(
209+
'backgroundColor',
210+
backgroundColor,
211+
defaultValue: null,
212+
),
213+
);
193214
}
194215
}
195216

packages/flutter/test/material/radio_theme_test.dart

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void main() {
2727
expect(themeData.splashRadius, null);
2828
expect(themeData.materialTapTargetSize, null);
2929
expect(themeData.visualDensity, null);
30+
expect(themeData.backgroundColor, null);
3031

3132
const RadioTheme theme = RadioTheme(data: RadioThemeData(), child: SizedBox());
3233
expect(theme.data.mouseCursor, null);
@@ -35,6 +36,7 @@ void main() {
3536
expect(theme.data.splashRadius, null);
3637
expect(theme.data.materialTapTargetSize, null);
3738
expect(theme.data.visualDensity, null);
39+
expect(theme.data.backgroundColor, null);
3840
});
3941

4042
testWidgets('Default RadioThemeData debugFillProperties', (WidgetTester tester) async {
@@ -47,18 +49,19 @@ void main() {
4749
.map((DiagnosticsNode node) => node.toString())
4850
.toList();
4951

50-
expect(description, <String>[]);
52+
expect(description, const <String>[]);
5153
});
5254

5355
testWidgets('RadioThemeData implements debugFillProperties', (WidgetTester tester) async {
5456
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
5557
const RadioThemeData(
56-
mouseCursor: MaterialStatePropertyAll<MouseCursor>(SystemMouseCursors.click),
57-
fillColor: MaterialStatePropertyAll<Color>(Color(0xfffffff0)),
58-
overlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffff1)),
58+
mouseCursor: WidgetStatePropertyAll<MouseCursor>(SystemMouseCursors.click),
59+
fillColor: WidgetStatePropertyAll<Color>(Color(0xfffffff0)),
60+
overlayColor: WidgetStatePropertyAll<Color>(Color(0xfffffff1)),
5961
splashRadius: 1.0,
6062
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
6163
visualDensity: VisualDensity.standard,
64+
backgroundColor: WidgetStatePropertyAll<Color>(Color(0xfffffff2)),
6265
).debugFillProperties(builder);
6366

6467
final List<String> description =
@@ -76,6 +79,7 @@ void main() {
7679
'splashRadius: 1.0',
7780
'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap',
7881
'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)',
82+
'backgroundColor: WidgetStatePropertyAll(${const Color(0xfffffff2)})',
7983
]),
8084
);
8185
});
@@ -88,6 +92,8 @@ void main() {
8892
const Color selectedFillColor = Color(0xfffffff1);
8993
const Color focusOverlayColor = Color(0xfffffff2);
9094
const Color hoverOverlayColor = Color(0xfffffff3);
95+
const Color defaultBackgroundColor = Color(0xfffffff4);
96+
const Color selectedBackgroundColor = Color(0xfffffff5);
9197
const double splashRadius = 1.0;
9298
const MaterialTapTargetSize materialTapTargetSize = MaterialTapTargetSize.shrinkWrap;
9399
const VisualDensity visualDensity = VisualDensity(horizontal: 1, vertical: 1);
@@ -96,25 +102,31 @@ void main() {
96102
return MaterialApp(
97103
theme: ThemeData(
98104
radioTheme: RadioThemeData(
99-
mouseCursor: const MaterialStatePropertyAll<MouseCursor>(mouseCursor),
100-
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
101-
if (states.contains(MaterialState.selected)) {
105+
mouseCursor: const WidgetStatePropertyAll<MouseCursor>(mouseCursor),
106+
fillColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
107+
if (states.contains(WidgetState.selected)) {
102108
return selectedFillColor;
103109
}
104110
return defaultFillColor;
105111
}),
106-
overlayColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
107-
if (states.contains(MaterialState.focused)) {
112+
overlayColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
113+
if (states.contains(WidgetState.focused)) {
108114
return focusOverlayColor;
109115
}
110-
if (states.contains(MaterialState.hovered)) {
116+
if (states.contains(WidgetState.hovered)) {
111117
return hoverOverlayColor;
112118
}
113119
return null;
114120
}),
115121
splashRadius: splashRadius,
116122
materialTapTargetSize: materialTapTargetSize,
117123
visualDensity: visualDensity,
124+
backgroundColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
125+
if (states.contains(WidgetState.selected)) {
126+
return selectedBackgroundColor;
127+
}
128+
return defaultBackgroundColor;
129+
}),
118130
),
119131
),
120132
home: Scaffold(
@@ -134,7 +146,7 @@ void main() {
134146
expect(
135147
_getRadioMaterial(tester),
136148
paints
137-
..circle(color: Colors.transparent)
149+
..circle(color: defaultBackgroundColor)
138150
..circle(color: defaultFillColor),
139151
);
140152
// Size from MaterialTapTargetSize.shrinkWrap with added VisualDensity.
@@ -146,7 +158,7 @@ void main() {
146158
expect(
147159
_getRadioMaterial(tester),
148160
paints
149-
..circle(color: Colors.transparent)
161+
..circle(color: selectedBackgroundColor)
150162
..circle(color: selectedFillColor),
151163
);
152164

@@ -177,6 +189,8 @@ void main() {
177189
const Color themeSelectedFillColor = Color(0xfffffff1);
178190
const Color themeFocusOverlayColor = Color(0xfffffff2);
179191
const Color themeHoverOverlayColor = Color(0xfffffff3);
192+
const Color themeDefaultBackgroundColor = Color(0xfffffff4);
193+
const Color themeSelectedBackgroundColor = Color(0xfffffff5);
180194
const double themeSplashRadius = 1.0;
181195
const MaterialTapTargetSize themeMaterialTapTargetSize = MaterialTapTargetSize.padded;
182196
const VisualDensity themeVisualDensity = VisualDensity.standard;
@@ -186,6 +200,8 @@ void main() {
186200
const Color selectedFillColor = Color(0xfffffff1);
187201
const Color focusColor = Color(0xfffffff2);
188202
const Color hoverColor = Color(0xfffffff3);
203+
const Color defaultBackgroundColor = Color(0xfffffff4);
204+
const Color selectedBackgroundColor = Color(0xfffffff5);
189205
const double splashRadius = 2.0;
190206
const MaterialTapTargetSize materialTapTargetSize = MaterialTapTargetSize.shrinkWrap;
191207
const VisualDensity visualDensity = VisualDensity(horizontal: 1, vertical: 1);
@@ -194,25 +210,31 @@ void main() {
194210
return MaterialApp(
195211
theme: ThemeData(
196212
radioTheme: RadioThemeData(
197-
mouseCursor: const MaterialStatePropertyAll<MouseCursor>(themeMouseCursor),
198-
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
199-
if (states.contains(MaterialState.selected)) {
213+
mouseCursor: const WidgetStatePropertyAll<MouseCursor>(themeMouseCursor),
214+
fillColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
215+
if (states.contains(WidgetState.selected)) {
200216
return themeSelectedFillColor;
201217
}
202218
return themeDefaultFillColor;
203219
}),
204-
overlayColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
205-
if (states.contains(MaterialState.focused)) {
220+
overlayColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
221+
if (states.contains(WidgetState.focused)) {
206222
return themeFocusOverlayColor;
207223
}
208-
if (states.contains(MaterialState.hovered)) {
224+
if (states.contains(WidgetState.hovered)) {
209225
return themeHoverOverlayColor;
210226
}
211227
return null;
212228
}),
213229
splashRadius: themeSplashRadius,
214230
materialTapTargetSize: themeMaterialTapTargetSize,
215231
visualDensity: themeVisualDensity,
232+
backgroundColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
233+
if (states.contains(WidgetState.selected)) {
234+
return themeSelectedBackgroundColor;
235+
}
236+
return themeDefaultBackgroundColor;
237+
}),
216238
),
217239
),
218240
home: Scaffold(
@@ -222,8 +244,8 @@ void main() {
222244
groupValue: 0,
223245
autofocus: autofocus,
224246
mouseCursor: mouseCursor,
225-
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
226-
if (states.contains(MaterialState.selected)) {
247+
fillColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
248+
if (states.contains(WidgetState.selected)) {
227249
return selectedFillColor;
228250
}
229251
return defaultFillColor;
@@ -233,6 +255,12 @@ void main() {
233255
splashRadius: splashRadius,
234256
materialTapTargetSize: materialTapTargetSize,
235257
visualDensity: visualDensity,
258+
backgroundColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
259+
if (states.contains(WidgetState.selected)) {
260+
return selectedBackgroundColor;
261+
}
262+
return defaultBackgroundColor;
263+
}),
236264
),
237265
),
238266
);
@@ -244,7 +272,7 @@ void main() {
244272
expect(
245273
_getRadioMaterial(tester),
246274
paints
247-
..circle(color: Colors.transparent)
275+
..circle(color: defaultBackgroundColor)
248276
..circle(color: defaultFillColor),
249277
);
250278
// Size from MaterialTapTargetSize.shrinkWrap with added VisualDensity.
@@ -256,7 +284,7 @@ void main() {
256284
expect(
257285
_getRadioMaterial(tester),
258286
paints
259-
..circle(color: Colors.transparent)
287+
..circle(color: selectedBackgroundColor)
260288
..circle(color: selectedFillColor),
261289
);
262290

0 commit comments

Comments
 (0)