Skip to content

Commit a6da104

Browse files
authored
Adds support for the Material Badge widget, BadgeTheme, BadgeThemeData (#114560)
1 parent 5280135 commit a6da104

File tree

9 files changed

+796
-0
lines changed

9 files changed

+796
-0
lines changed

dev/tools/gen_defaults/bin/gen_defaults.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import 'dart:io';
1919

2020
import 'package:gen_defaults/action_chip_template.dart';
2121
import 'package:gen_defaults/app_bar_template.dart';
22+
import 'package:gen_defaults/badge_template.dart';
2223
import 'package:gen_defaults/banner_template.dart';
2324
import 'package:gen_defaults/bottom_app_bar_template.dart';
2425
import 'package:gen_defaults/bottom_sheet_template.dart';
@@ -54,6 +55,7 @@ Future<void> main(List<String> args) async {
5455
const List<String> tokenFiles = <String>[
5556
'badge.json',
5657
'banner.json',
58+
'badge.json',
5759
'bottom_app_bar.json',
5860
'button_elevated.json',
5961
'button_filled.json',
@@ -125,6 +127,8 @@ Future<void> main(List<String> args) async {
125127
ActionChipTemplate('Chip', '$materialLib/chip.dart', tokens).updateFile();
126128
ActionChipTemplate('ActionChip', '$materialLib/action_chip.dart', tokens).updateFile();
127129
AppBarTemplate('AppBar', '$materialLib/app_bar.dart', tokens).updateFile();
130+
BottomAppBarTemplate('BottomAppBar', '$materialLib/bottom_app_bar.dart', tokens).updateFile();
131+
BadgeTemplate('Badge', '$materialLib/badge.dart', tokens).updateFile();
128132
BannerTemplate('Banner', '$materialLib/banner.dart', tokens).updateFile();
129133
BottomAppBarTemplate('BottomAppBar', '$materialLib/bottom_app_bar.dart', tokens).updateFile();
130134
BottomSheetTemplate('BottomSheet', '$materialLib/bottom_sheet.dart', tokens).updateFile();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'template.dart';
6+
7+
class BadgeTemplate extends TokenTemplate {
8+
const BadgeTemplate(super.blockName, super.fileName, super.tokens, {
9+
super.colorSchemePrefix = '_colors.',
10+
});
11+
12+
@override
13+
String generate() => '''
14+
class _${blockName}DefaultsM3 extends BadgeThemeData {
15+
_${blockName}DefaultsM3(this.context) : super(
16+
smallSize: ${tokens["md.comp.badge.size"]},
17+
largeSize: ${tokens["md.comp.badge.large.size"]},
18+
padding: const EdgeInsets.symmetric(horizontal: 4),
19+
alignment: const AlignmentDirectional(12, -4),
20+
);
21+
22+
final BuildContext context;
23+
late final ThemeData _theme = Theme.of(context);
24+
late final ColorScheme _colors = _theme.colorScheme;
25+
26+
@override
27+
Color? get backgroundColor => ${color("md.comp.badge.color")};
28+
29+
@override
30+
Color? get foregroundColor => ${color("md.comp.badge.large.label-text.color")};
31+
32+
@override
33+
TextStyle? get textStyle => ${textStyle("md.comp.badge.large.label-text")};
34+
}
35+
''';
36+
}

packages/flutter/lib/material.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export 'src/material/app_bar_theme.dart';
3030
export 'src/material/arc.dart';
3131
export 'src/material/autocomplete.dart';
3232
export 'src/material/back_button.dart';
33+
export 'src/material/badge.dart';
34+
export 'src/material/badge_theme.dart';
3335
export 'src/material/banner.dart';
3436
export 'src/material/banner_theme.dart';
3537
export 'src/material/bottom_app_bar.dart';
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/widgets.dart';
6+
7+
import 'badge_theme.dart';
8+
import 'color_scheme.dart';
9+
import 'theme.dart';
10+
11+
/// A Material Design "badge".
12+
///
13+
/// A badge's [label] conveys a small amount of information about its
14+
/// [child], like a count or status. If the label is null then this is
15+
/// a "small" badge that's displayed as a [smallSize] diameter filled
16+
/// circle. Otherwise this is a [StadiumBorder] shaped "large" badge
17+
/// with height [largeSize].
18+
///
19+
/// Badges are typically used to decorate the icon within a
20+
/// BottomNavigationBarItem] or a [NavigationRailDestination]
21+
/// or a button's icon, as in [TextButton.icon]. The badges default
22+
/// configuration is intended to work well with a default sized (24)
23+
/// [Icon].
24+
class Badge extends StatelessWidget {
25+
/// Create a Badge that stacks [label] on top of [child].
26+
///
27+
/// If [label] is null then just a filled circle is displayed. Otherwise
28+
/// the [label] is displayed within a [StadiumBorder] shaped area.
29+
const Badge({
30+
super.key,
31+
this.backgroundColor,
32+
this.foregroundColor,
33+
this.smallSize,
34+
this.largeSize,
35+
this.textStyle,
36+
this.padding,
37+
this.alignment,
38+
this.label,
39+
this.child,
40+
});
41+
42+
/// The badge's fill color.
43+
///
44+
/// Defaults to the [BadgeTheme]'s background color, or
45+
/// [ColorScheme.errorColor] if the theme value is null.
46+
final Color? backgroundColor;
47+
48+
/// The color of the badge's [label] text.
49+
///
50+
/// This color overrides the color of the label's [textStyle].
51+
///
52+
/// Defaults to the [BadgeTheme]'s foreground color, or
53+
/// [ColorScheme.onError] if the theme value is null.
54+
final Color? foregroundColor;
55+
56+
/// The diameter of the badge if [label] is null.
57+
///
58+
/// Defaults to the [BadgeTheme]'s small size, or 6 if the theme value
59+
/// is null.
60+
final double? smallSize;
61+
62+
/// The badge's height if [label] is non-null.
63+
///
64+
/// Defaults to the [BadgeTheme]'s large size, or 16 if the theme value
65+
/// is null. If the default value is overridden then it may be useful to
66+
/// also override [padding] and [alignment].
67+
final double? largeSize;
68+
69+
/// The [DefaultTextStyle] for the badge's label.
70+
///
71+
/// The text style's color is overwritten by the [foregroundColor].
72+
///
73+
/// This value is only used if [label] is non-null.
74+
///
75+
/// Defaults to the [BadgeTheme]'s text style, or the overall theme's
76+
/// [TextTheme.labelSmall] if the badge theme's value is null. If
77+
/// the default text style is overridden then it may be useful to
78+
/// also override [largeSize], [padding], and [alignment].
79+
final TextStyle? textStyle;
80+
81+
/// The padding added to the badge's label.
82+
///
83+
/// This value is only used if [label] is non-null.
84+
///
85+
/// Defaults to the [BadgeTheme]'s padding, or 4 pixels on the
86+
/// left and right if the theme's value is null.
87+
final EdgeInsetsGeometry? padding;
88+
89+
/// The location of the [label] relative to the [child].
90+
///
91+
/// This value is only used if [label] is non-null.
92+
///
93+
/// Defaults to the [BadgeTheme]'s alignment, or `start = 12`
94+
/// and `top = -4` if the theme's value is null.
95+
final AlignmentDirectional? alignment;
96+
97+
/// The badge's content, typically a [Text] widget that contains 1 to 4
98+
/// characters.
99+
///
100+
/// If the label is null then this is a "small" badge that's
101+
/// displayed as a [smallSize] diameter filled circle. Otherwise
102+
/// this is a [StadiumBorder] shaped "large" badge with height [largeSize].
103+
final Widget? label;
104+
105+
/// The widget that the badge is stacked on top of.
106+
///
107+
/// Typically this is an default sized [Icon] that's part of a
108+
/// [BottomNavigationBarItem] or a [NavigationRailDestination].
109+
final Widget? child;
110+
111+
@override
112+
Widget build(BuildContext context) {
113+
final BadgeThemeData badgeTheme = BadgeTheme.of(context);
114+
final BadgeThemeData defaults = _BadgeDefaultsM3(context);
115+
final double effectiveSmallSize = smallSize ?? badgeTheme.smallSize ?? defaults.smallSize!;
116+
final double effectiveLargeSize = largeSize ?? badgeTheme.largeSize ?? defaults.largeSize!;
117+
118+
final Widget badge = DefaultTextStyle(
119+
style: (textStyle ?? badgeTheme.textStyle ?? defaults.textStyle!).copyWith(
120+
color: foregroundColor ?? badgeTheme.foregroundColor ?? defaults.foregroundColor!,
121+
),
122+
child: IntrinsicWidth(
123+
child: Container(
124+
height: label == null ? effectiveSmallSize : effectiveLargeSize,
125+
clipBehavior: Clip.antiAlias,
126+
decoration: ShapeDecoration(
127+
color: backgroundColor ?? badgeTheme.backgroundColor ?? defaults.backgroundColor!,
128+
shape: const StadiumBorder(),
129+
),
130+
padding: label == null ? null : (padding ?? badgeTheme.padding ?? defaults.padding!),
131+
alignment: label == null ? null : Alignment.center,
132+
child: label ?? SizedBox(width: effectiveSmallSize, height: effectiveSmallSize),
133+
),
134+
),
135+
);
136+
137+
if (child == null) {
138+
return badge;
139+
}
140+
141+
final AlignmentDirectional effectiveAlignment = alignment ?? badgeTheme.alignment ?? defaults.alignment!;
142+
return
143+
Stack(
144+
clipBehavior: Clip.none,
145+
children: <Widget>[
146+
child!,
147+
Positioned.directional(
148+
textDirection: Directionality.of(context),
149+
start: label == null ? null : effectiveAlignment.start,
150+
end: label == null ? 0 : null,
151+
top: label == null ? 0 : effectiveAlignment.y,
152+
child: badge,
153+
),
154+
],
155+
);
156+
}
157+
}
158+
159+
// BEGIN GENERATED TOKEN PROPERTIES - Badge
160+
161+
// Do not edit by hand. The code between the "BEGIN GENERATED" and
162+
// "END GENERATED" comments are generated from data in the Material
163+
// Design token database by the script:
164+
// dev/tools/gen_defaults/bin/gen_defaults.dart.
165+
166+
// Token database version: v0_137
167+
168+
class _BadgeDefaultsM3 extends BadgeThemeData {
169+
_BadgeDefaultsM3(this.context) : super(
170+
smallSize: 6.0,
171+
largeSize: 16.0,
172+
padding: const EdgeInsets.symmetric(horizontal: 4),
173+
alignment: const AlignmentDirectional(12, -4),
174+
);
175+
176+
final BuildContext context;
177+
late final ThemeData _theme = Theme.of(context);
178+
late final ColorScheme _colors = _theme.colorScheme;
179+
180+
@override
181+
Color? get backgroundColor => _colors.error;
182+
183+
@override
184+
Color? get foregroundColor => _colors.onError;
185+
186+
@override
187+
TextStyle? get textStyle => Theme.of(context).textTheme.labelSmall;
188+
}
189+
190+
// END GENERATED TOKEN PROPERTIES - Badge

0 commit comments

Comments
 (0)