blob: e4fd4c29e5d1bd7e4361be75ffd3fbc863b76d9b [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'badge_theme.dart';
import 'color_scheme.dart';
import 'theme.dart';
/// A Material Design "badge".
///
/// A badge's [label] conveys a small amount of information about its
/// [child], like a count or status. If the label is null then this is
/// a "small" badge that's displayed as a [smallSize] diameter filled
/// circle. Otherwise this is a [StadiumBorder] shaped "large" badge
/// with height [largeSize].
///
/// Badges are typically used to decorate the icon within a
/// BottomNavigationBarItem] or a [NavigationRailDestination]
/// or a button's icon, as in [TextButton.icon]. The badge's default
/// configuration is intended to work well with a default sized (24)
/// [Icon].
class Badge extends StatelessWidget {
/// Create a Badge that stacks [label] on top of [child].
///
/// If [label] is null then just a filled circle is displayed. Otherwise
/// the [label] is displayed within a [StadiumBorder] shaped area.
const Badge({
super.key,
this.backgroundColor,
this.textColor,
this.smallSize,
this.largeSize,
this.textStyle,
this.padding,
this.alignment,
this.label,
this.isLabelVisible = true,
this.child,
});
/// The badge's fill color.
///
/// Defaults to the [BadgeTheme]'s background color, or
/// [ColorScheme.errorColor] if the theme value is null.
final Color? backgroundColor;
/// The color of the badge's [label] text.
///
/// This color overrides the color of the label's [textStyle].
///
/// Defaults to the [BadgeTheme]'s foreground color, or
/// [ColorScheme.onError] if the theme value is null.
final Color? textColor;
/// The diameter of the badge if [label] is null.
///
/// Defaults to the [BadgeTheme]'s small size, or 6 if the theme value
/// is null.
final double? smallSize;
/// The badge's height if [label] is non-null.
///
/// Defaults to the [BadgeTheme]'s large size, or 16 if the theme value
/// is null. If the default value is overridden then it may be useful to
/// also override [padding] and [alignment].
final double? largeSize;
/// The [DefaultTextStyle] for the badge's label.
///
/// The text style's color is overwritten by the [textColor].
///
/// This value is only used if [label] is non-null.
///
/// Defaults to the [BadgeTheme]'s text style, or the overall theme's
/// [TextTheme.labelSmall] if the badge theme's value is null. If
/// the default text style is overridden then it may be useful to
/// also override [largeSize], [padding], and [alignment].
final TextStyle? textStyle;
/// The padding added to the badge's label.
///
/// This value is only used if [label] is non-null.
///
/// Defaults to the [BadgeTheme]'s padding, or 4 pixels on the
/// left and right if the theme's value is null.
final EdgeInsetsGeometry? padding;
/// The location of the [label] relative to the [child].
///
/// This value is only used if [label] is non-null.
///
/// Defaults to the [BadgeTheme]'s alignment, or `start = 12`
/// and `top = -4` if the theme's value is null.
final AlignmentDirectional? alignment;
/// The badge's content, typically a [Text] widget that contains 1 to 4
/// characters.
///
/// If the label is null then this is a "small" badge that's
/// displayed as a [smallSize] diameter filled circle. Otherwise
/// this is a [StadiumBorder] shaped "large" badge with height [largeSize].
final Widget? label;
/// If false, the badge's [label] is not included.
///
/// This flag is true by default. It's intended to make it convenient
/// to create a badge that's only shown under certain conditions.
final bool isLabelVisible;
/// The widget that the badge is stacked on top of.
///
/// Typically this is an default sized [Icon] that's part of a
/// [BottomNavigationBarItem] or a [NavigationRailDestination].
final Widget? child;
@override
Widget build(BuildContext context) {
if (!isLabelVisible) {
return child ?? const SizedBox();
}
final BadgeThemeData badgeTheme = BadgeTheme.of(context);
final BadgeThemeData defaults = _BadgeDefaultsM3(context);
final double effectiveSmallSize = smallSize ?? badgeTheme.smallSize ?? defaults.smallSize!;
final double effectiveLargeSize = largeSize ?? badgeTheme.largeSize ?? defaults.largeSize!;
final Widget badge = DefaultTextStyle(
style: (textStyle ?? badgeTheme.textStyle ?? defaults.textStyle!).copyWith(
color: textColor ?? badgeTheme.textColor ?? defaults.textColor!,
),
child: IntrinsicWidth(
child: Container(
height: label == null ? effectiveSmallSize : effectiveLargeSize,
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
color: backgroundColor ?? badgeTheme.backgroundColor ?? defaults.backgroundColor!,
shape: const StadiumBorder(),
),
padding: label == null ? null : (padding ?? badgeTheme.padding ?? defaults.padding!),
alignment: label == null ? null : Alignment.center,
child: label ?? SizedBox(width: effectiveSmallSize, height: effectiveSmallSize),
),
),
);
if (child == null) {
return badge;
}
final AlignmentDirectional effectiveAlignment = alignment ?? badgeTheme.alignment ?? defaults.alignment!;
return
Stack(
clipBehavior: Clip.none,
children: <Widget>[
child!,
Positioned.directional(
textDirection: Directionality.of(context),
start: label == null ? null : effectiveAlignment.start,
end: label == null ? 0 : null,
top: label == null ? 0 : effectiveAlignment.y,
child: badge,
),
],
);
}
}
// BEGIN GENERATED TOKEN PROPERTIES - Badge
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_141
class _BadgeDefaultsM3 extends BadgeThemeData {
_BadgeDefaultsM3(this.context) : super(
smallSize: 6.0,
largeSize: 16.0,
padding: const EdgeInsets.symmetric(horizontal: 4),
alignment: const AlignmentDirectional(12, -4),
);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
@override
Color? get backgroundColor => _colors.error;
@override
Color? get textColor => _colors.onError;
@override
TextStyle? get textStyle => Theme.of(context).textTheme.labelSmall;
}
// END GENERATED TOKEN PROPERTIES - Badge