Skip to content

Commit df110ef

Browse files
authored
Provide Material 3 defaults for vanilla Chip widget. (#111597)
1 parent d0a1e9e commit df110ef

File tree

11 files changed

+183
-16
lines changed

11 files changed

+183
-16
lines changed

dev/tools/gen_defaults/bin/gen_defaults.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ Future<void> main(List<String> args) async {
104104
tokens['colorsLight'] = _readTokenFile('color_light.json');
105105
tokens['colorsDark'] = _readTokenFile('color_dark.json');
106106

107+
ActionChipTemplate('Chip', '$materialLib/chip.dart', tokens).updateFile();
107108
ActionChipTemplate('ActionChip', '$materialLib/action_chip.dart', tokens).updateFile();
108109
AppBarTemplate('AppBar', '$materialLib/app_bar.dart', tokens).updateFile();
109110
BannerTemplate('Banner', '$materialLib/banner.dart', tokens).updateFile();

dev/tools/gen_defaults/lib/template.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,13 @@ abstract class TokenTemplate {
170170
final Map<String, dynamic> shape = tokens[tokens['$componentToken.shape']!]! as Map<String, dynamic>;
171171
switch (shape['family']) {
172172
case 'SHAPE_FAMILY_ROUNDED_CORNERS':
173+
final double topLeft = shape['topLeft'] as double;
174+
final double topRight = shape['topRight'] as double;
175+
final double bottomLeft = shape['bottomLeft'] as double;
176+
final double bottomRight = shape['bottomRight'] as double;
177+
if (topLeft == topRight && topLeft == bottomLeft && topLeft == bottomRight) {
178+
return '${prefix}RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular($topLeft)))';
179+
}
173180
return '${prefix}RoundedRectangleBorder(borderRadius: '
174181
'BorderRadius.only('
175182
'topLeft: Radius.circular(${shape['topLeft']}), '

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ class _ActionChipDefaultsM3 extends ChipThemeData {
184184
const _ActionChipDefaultsM3(this.context, this.isEnabled)
185185
: super(
186186
elevation: 0.0,
187-
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0), bottomLeft: Radius.circular(8.0), bottomRight: Radius.circular(8.0))),
187+
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
188188
showCheckmark: true,
189189
);
190190

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ class _CardDefaultsM3 extends CardTheme {
223223
clipBehavior: Clip.none,
224224
elevation: 1.0,
225225
margin: const EdgeInsets.all(4.0),
226-
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(12.0), topRight: Radius.circular(12.0), bottomLeft: Radius.circular(12.0), bottomRight: Radius.circular(12.0))),
226+
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0))),
227227
);
228228

229229
final BuildContext context;

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

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,11 +1164,15 @@ class _RawChipState extends State<RawChip> with MaterialStateMixin, TickerProvid
11641164
final ThemeData theme = Theme.of(context);
11651165
final ChipThemeData chipTheme = ChipTheme.of(context);
11661166
final Brightness brightness = chipTheme.brightness ?? theme.brightness;
1167-
final ChipThemeData chipDefaults = widget.defaultProperties ?? ChipThemeData.fromDefaults(
1168-
brightness: brightness,
1169-
secondaryColor: brightness == Brightness.dark ? Colors.tealAccent[200]! : theme.primaryColor,
1170-
labelStyle: theme.textTheme.bodyLarge!,
1171-
);
1167+
final ChipThemeData chipDefaults = widget.defaultProperties ??
1168+
(theme.useMaterial3
1169+
? _ChipDefaultsM3(context, widget.isEnabled)
1170+
: ChipThemeData.fromDefaults(
1171+
brightness: brightness,
1172+
secondaryColor: brightness == Brightness.dark ? Colors.tealAccent[200]! : theme.primaryColor,
1173+
labelStyle: theme.textTheme.bodyLarge!,
1174+
)
1175+
);
11721176
final TextDirection? textDirection = Directionality.maybeOf(context);
11731177
final OutlinedBorder resolvedShape = _getShape(theme, chipTheme, chipDefaults);
11741178

@@ -2184,3 +2188,77 @@ bool _hitIsOnDeleteIcon({
21842188
return adjustedPosition.dx <= accessibleDeleteButtonWidth;
21852189
}
21862190
}
2191+
2192+
// BEGIN GENERATED TOKEN PROPERTIES - Chip
2193+
2194+
// Do not edit by hand. The code between the "BEGIN GENERATED" and
2195+
// "END GENERATED" comments are generated from data in the Material
2196+
// Design token database by the script:
2197+
// dev/tools/gen_defaults/bin/gen_defaults.dart.
2198+
2199+
// Token database version: v0_127
2200+
2201+
class _ChipDefaultsM3 extends ChipThemeData {
2202+
const _ChipDefaultsM3(this.context, this.isEnabled)
2203+
: super(
2204+
elevation: 0.0,
2205+
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
2206+
showCheckmark: true,
2207+
);
2208+
2209+
final BuildContext context;
2210+
final bool isEnabled;
2211+
2212+
@override
2213+
TextStyle? get labelStyle => Theme.of(context).textTheme.labelLarge;
2214+
2215+
@override
2216+
Color? get backgroundColor => null;
2217+
2218+
@override
2219+
Color? get shadowColor => Colors.transparent;
2220+
2221+
@override
2222+
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
2223+
2224+
@override
2225+
Color? get selectedColor => null;
2226+
2227+
@override
2228+
Color? get checkmarkColor => null;
2229+
2230+
@override
2231+
Color? get disabledColor => null;
2232+
2233+
@override
2234+
Color? get deleteIconColor => null;
2235+
2236+
@override
2237+
BorderSide? get side => isEnabled
2238+
? BorderSide(color: Theme.of(context).colorScheme.outline)
2239+
: BorderSide(color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12));
2240+
2241+
@override
2242+
IconThemeData? get iconTheme => IconThemeData(
2243+
color: isEnabled
2244+
? Theme.of(context).colorScheme.primary
2245+
: Theme.of(context).colorScheme.onSurface,
2246+
size: 18.0,
2247+
);
2248+
2249+
@override
2250+
EdgeInsetsGeometry? get padding => const EdgeInsets.all(8.0);
2251+
2252+
/// The chip at text scale 1 starts with 8px on each side and as text scaling
2253+
/// gets closer to 2 the label padding is linearly interpolated from 8px to 4px.
2254+
/// Once the widget has a text scaling of 2 or higher than the label padding
2255+
/// remains 4px.
2256+
@override
2257+
EdgeInsetsGeometry? get labelPadding => EdgeInsets.lerp(
2258+
const EdgeInsets.symmetric(horizontal: 8.0),
2259+
const EdgeInsets.symmetric(horizontal: 4.0),
2260+
clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0),
2261+
)!;
2262+
}
2263+
2264+
// END GENERATED TOKEN PROPERTIES - Chip

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ class _FilterChipDefaultsM3 extends ChipThemeData {
195195
const _FilterChipDefaultsM3(this.context, this.isEnabled, this.isSelected)
196196
: super(
197197
elevation: 0.0,
198-
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0), bottomLeft: Radius.circular(8.0), bottomRight: Radius.circular(8.0))),
198+
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
199199
showCheckmark: true,
200200
);
201201

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,7 @@ class _DialogDefaultsM3 extends DialogTheme {
13961396
: super(
13971397
alignment: Alignment.center,
13981398
elevation: 6.0,
1399-
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(28.0), topRight: Radius.circular(28.0), bottomLeft: Radius.circular(28.0), bottomRight: Radius.circular(28.0))),
1399+
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0))),
14001400
);
14011401

14021402
final BuildContext context;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ class _FilterChipDefaultsM3 extends ChipThemeData {
204204
const _FilterChipDefaultsM3(this.context, this.isEnabled, this.isSelected)
205205
: super(
206206
elevation: 0.0,
207-
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0), bottomLeft: Radius.circular(8.0), bottomRight: Radius.circular(8.0))),
207+
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
208208
showCheckmark: true,
209209
);
210210

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -850,13 +850,13 @@ class _FABDefaultsM3 extends FloatingActionButtonThemeData {
850850
ShapeBorder? get shape {
851851
switch (type) {
852852
case _FloatingActionButtonType.regular:
853-
return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)));
853+
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
854854
case _FloatingActionButtonType.small:
855-
return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(12.0), topRight: Radius.circular(12.0), bottomLeft: Radius.circular(12.0), bottomRight: Radius.circular(12.0)));
855+
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0)));
856856
case _FloatingActionButtonType.large:
857-
return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(28.0), topRight: Radius.circular(28.0), bottomLeft: Radius.circular(28.0), bottomRight: Radius.circular(28.0)));
857+
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0)));
858858
case _FloatingActionButtonType.extended:
859-
return const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)));
859+
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
860860
}
861861
}
862862

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ class _InputChipDefaultsM3 extends ChipThemeData {
255255
const _InputChipDefaultsM3(this.context, this.isEnabled)
256256
: super(
257257
elevation: 0.0,
258-
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0), bottomLeft: Radius.circular(8.0), bottomRight: Radius.circular(8.0))),
258+
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
259259
showCheckmark: true,
260260
);
261261

packages/flutter/test/material/chip_test.dart

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ Finder findTooltipContainer(String tooltipText) {
215215
}
216216

217217
void main() {
218-
testWidgets('Chip defaults', (WidgetTester tester) async {
218+
testWidgets('M2 Chip defaults', (WidgetTester tester) async {
219219
late TextTheme textTheme;
220220

221221
Widget buildFrame(Brightness brightness) {
@@ -292,6 +292,87 @@ void main() {
292292
expect(labelStyle.wordSpacing, textTheme.bodyLarge?.wordSpacing);
293293
});
294294

295+
testWidgets('M3 Chip defaults', (WidgetTester tester) async {
296+
late TextTheme textTheme;
297+
final ThemeData lightTheme = ThemeData.light(useMaterial3: true);
298+
final ThemeData darkTheme = ThemeData.dark(useMaterial3: true);
299+
300+
Widget buildFrame(ThemeData theme) {
301+
return MaterialApp(
302+
theme: theme,
303+
home: Scaffold(
304+
body: Center(
305+
child: Builder(
306+
builder: (BuildContext context) {
307+
textTheme = Theme.of(context).textTheme;
308+
return Chip(
309+
avatar: const CircleAvatar(child: Text('A')),
310+
label: const Text('Chip A'),
311+
onDeleted: () { },
312+
);
313+
},
314+
),
315+
),
316+
),
317+
);
318+
}
319+
320+
await tester.pumpWidget(buildFrame(lightTheme));
321+
expect(getMaterial(tester).color, null);
322+
expect(getMaterial(tester).elevation, 0);
323+
expect(getMaterial(tester).shape, RoundedRectangleBorder(
324+
side: BorderSide(color: lightTheme.colorScheme.outline),
325+
borderRadius: BorderRadius.circular(8.0),
326+
));
327+
expect(getIconData(tester).color, lightTheme.colorScheme.primary);
328+
expect(getIconData(tester).opacity, null);
329+
expect(getIconData(tester).size, 18);
330+
331+
TextStyle labelStyle = getLabelStyle(tester, 'Chip A').style;
332+
expect(labelStyle.color, textTheme.labelLarge?.color);
333+
expect(labelStyle.fontFamily, textTheme.labelLarge?.fontFamily);
334+
expect(labelStyle.fontFamilyFallback, textTheme.labelLarge?.fontFamilyFallback);
335+
expect(labelStyle.fontFeatures, textTheme.labelLarge?.fontFeatures);
336+
expect(labelStyle.fontSize, textTheme.labelLarge?.fontSize);
337+
expect(labelStyle.fontStyle, textTheme.labelLarge?.fontStyle);
338+
expect(labelStyle.fontWeight, textTheme.labelLarge?.fontWeight);
339+
expect(labelStyle.height, textTheme.labelLarge?.height);
340+
expect(labelStyle.inherit, textTheme.labelLarge?.inherit);
341+
expect(labelStyle.leadingDistribution, textTheme.labelLarge?.leadingDistribution);
342+
expect(labelStyle.letterSpacing, textTheme.labelLarge?.letterSpacing);
343+
expect(labelStyle.overflow, textTheme.labelLarge?.overflow);
344+
expect(labelStyle.textBaseline, textTheme.labelLarge?.textBaseline);
345+
expect(labelStyle.wordSpacing, textTheme.labelLarge?.wordSpacing);
346+
347+
await tester.pumpWidget(buildFrame(darkTheme));
348+
await tester.pumpAndSettle(); // Theme transition animation
349+
expect(getMaterial(tester).color, null);
350+
expect(getMaterial(tester).elevation, 0);
351+
expect(getMaterial(tester).shape, RoundedRectangleBorder(
352+
side: BorderSide(color: darkTheme.colorScheme.outline),
353+
borderRadius: BorderRadius.circular(8.0),
354+
));
355+
expect(getIconData(tester).color, darkTheme.colorScheme.primary);
356+
expect(getIconData(tester).opacity, null);
357+
expect(getIconData(tester).size, 18);
358+
359+
labelStyle = getLabelStyle(tester, 'Chip A').style;
360+
expect(labelStyle.color, textTheme.labelLarge?.color);
361+
expect(labelStyle.fontFamily, textTheme.labelLarge?.fontFamily);
362+
expect(labelStyle.fontFamilyFallback, textTheme.labelLarge?.fontFamilyFallback);
363+
expect(labelStyle.fontFeatures, textTheme.labelLarge?.fontFeatures);
364+
expect(labelStyle.fontSize, textTheme.labelLarge?.fontSize);
365+
expect(labelStyle.fontStyle, textTheme.labelLarge?.fontStyle);
366+
expect(labelStyle.fontWeight, textTheme.labelLarge?.fontWeight);
367+
expect(labelStyle.height, textTheme.labelLarge?.height);
368+
expect(labelStyle.inherit, textTheme.labelLarge?.inherit);
369+
expect(labelStyle.leadingDistribution, textTheme.labelLarge?.leadingDistribution);
370+
expect(labelStyle.letterSpacing, textTheme.labelLarge?.letterSpacing);
371+
expect(labelStyle.overflow, textTheme.labelLarge?.overflow);
372+
expect(labelStyle.textBaseline, textTheme.labelLarge?.textBaseline);
373+
expect(labelStyle.wordSpacing, textTheme.labelLarge?.wordSpacing);
374+
});
375+
295376
testWidgets('Chip control test', (WidgetTester tester) async {
296377
final FeedbackTester feedback = FeedbackTester();
297378
final List<String> deletedChipLabels = <String>[];

0 commit comments

Comments
 (0)