mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Adds support for the Material Badge widget, BadgeTheme, BadgeThemeData (#114560)
This commit is contained in:
parent
5280135882
commit
a6da1042a8
@ -19,6 +19,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:gen_defaults/action_chip_template.dart';
|
import 'package:gen_defaults/action_chip_template.dart';
|
||||||
import 'package:gen_defaults/app_bar_template.dart';
|
import 'package:gen_defaults/app_bar_template.dart';
|
||||||
|
import 'package:gen_defaults/badge_template.dart';
|
||||||
import 'package:gen_defaults/banner_template.dart';
|
import 'package:gen_defaults/banner_template.dart';
|
||||||
import 'package:gen_defaults/bottom_app_bar_template.dart';
|
import 'package:gen_defaults/bottom_app_bar_template.dart';
|
||||||
import 'package:gen_defaults/bottom_sheet_template.dart';
|
import 'package:gen_defaults/bottom_sheet_template.dart';
|
||||||
@ -54,6 +55,7 @@ Future<void> main(List<String> args) async {
|
|||||||
const List<String> tokenFiles = <String>[
|
const List<String> tokenFiles = <String>[
|
||||||
'badge.json',
|
'badge.json',
|
||||||
'banner.json',
|
'banner.json',
|
||||||
|
'badge.json',
|
||||||
'bottom_app_bar.json',
|
'bottom_app_bar.json',
|
||||||
'button_elevated.json',
|
'button_elevated.json',
|
||||||
'button_filled.json',
|
'button_filled.json',
|
||||||
@ -125,6 +127,8 @@ Future<void> main(List<String> args) async {
|
|||||||
ActionChipTemplate('Chip', '$materialLib/chip.dart', tokens).updateFile();
|
ActionChipTemplate('Chip', '$materialLib/chip.dart', tokens).updateFile();
|
||||||
ActionChipTemplate('ActionChip', '$materialLib/action_chip.dart', tokens).updateFile();
|
ActionChipTemplate('ActionChip', '$materialLib/action_chip.dart', tokens).updateFile();
|
||||||
AppBarTemplate('AppBar', '$materialLib/app_bar.dart', tokens).updateFile();
|
AppBarTemplate('AppBar', '$materialLib/app_bar.dart', tokens).updateFile();
|
||||||
|
BottomAppBarTemplate('BottomAppBar', '$materialLib/bottom_app_bar.dart', tokens).updateFile();
|
||||||
|
BadgeTemplate('Badge', '$materialLib/badge.dart', tokens).updateFile();
|
||||||
BannerTemplate('Banner', '$materialLib/banner.dart', tokens).updateFile();
|
BannerTemplate('Banner', '$materialLib/banner.dart', tokens).updateFile();
|
||||||
BottomAppBarTemplate('BottomAppBar', '$materialLib/bottom_app_bar.dart', tokens).updateFile();
|
BottomAppBarTemplate('BottomAppBar', '$materialLib/bottom_app_bar.dart', tokens).updateFile();
|
||||||
BottomSheetTemplate('BottomSheet', '$materialLib/bottom_sheet.dart', tokens).updateFile();
|
BottomSheetTemplate('BottomSheet', '$materialLib/bottom_sheet.dart', tokens).updateFile();
|
||||||
|
36
dev/tools/gen_defaults/lib/badge_template.dart
Normal file
36
dev/tools/gen_defaults/lib/badge_template.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 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 'template.dart';
|
||||||
|
|
||||||
|
class BadgeTemplate extends TokenTemplate {
|
||||||
|
const BadgeTemplate(super.blockName, super.fileName, super.tokens, {
|
||||||
|
super.colorSchemePrefix = '_colors.',
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String generate() => '''
|
||||||
|
class _${blockName}DefaultsM3 extends BadgeThemeData {
|
||||||
|
_${blockName}DefaultsM3(this.context) : super(
|
||||||
|
smallSize: ${tokens["md.comp.badge.size"]},
|
||||||
|
largeSize: ${tokens["md.comp.badge.large.size"]},
|
||||||
|
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 => ${color("md.comp.badge.color")};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? get foregroundColor => ${color("md.comp.badge.large.label-text.color")};
|
||||||
|
|
||||||
|
@override
|
||||||
|
TextStyle? get textStyle => ${textStyle("md.comp.badge.large.label-text")};
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
}
|
@ -30,6 +30,8 @@ export 'src/material/app_bar_theme.dart';
|
|||||||
export 'src/material/arc.dart';
|
export 'src/material/arc.dart';
|
||||||
export 'src/material/autocomplete.dart';
|
export 'src/material/autocomplete.dart';
|
||||||
export 'src/material/back_button.dart';
|
export 'src/material/back_button.dart';
|
||||||
|
export 'src/material/badge.dart';
|
||||||
|
export 'src/material/badge_theme.dart';
|
||||||
export 'src/material/banner.dart';
|
export 'src/material/banner.dart';
|
||||||
export 'src/material/banner_theme.dart';
|
export 'src/material/banner_theme.dart';
|
||||||
export 'src/material/bottom_app_bar.dart';
|
export 'src/material/bottom_app_bar.dart';
|
||||||
|
190
packages/flutter/lib/src/material/badge.dart
Normal file
190
packages/flutter/lib/src/material/badge.dart
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// 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 badges 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.foregroundColor,
|
||||||
|
this.smallSize,
|
||||||
|
this.largeSize,
|
||||||
|
this.textStyle,
|
||||||
|
this.padding,
|
||||||
|
this.alignment,
|
||||||
|
this.label,
|
||||||
|
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? foregroundColor;
|
||||||
|
|
||||||
|
/// 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 [foregroundColor].
|
||||||
|
///
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// 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) {
|
||||||
|
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: foregroundColor ?? badgeTheme.foregroundColor ?? defaults.foregroundColor!,
|
||||||
|
),
|
||||||
|
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_137
|
||||||
|
|
||||||
|
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 foregroundColor => _colors.onError;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TextStyle? get textStyle => Theme.of(context).textTheme.labelSmall;
|
||||||
|
}
|
||||||
|
|
||||||
|
// END GENERATED TOKEN PROPERTIES - Badge
|
183
packages/flutter/lib/src/material/badge_theme.dart
Normal file
183
packages/flutter/lib/src/material/badge_theme.dart
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// 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 'dart:ui' show lerpDouble;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'theme.dart';
|
||||||
|
|
||||||
|
|
||||||
|
// Examples can assume:
|
||||||
|
// late BuildContext context;
|
||||||
|
|
||||||
|
/// Overrides the default properties values for descendant [Badge] widgets.
|
||||||
|
///
|
||||||
|
/// Descendant widgets obtain the current [BadgeThemeData] object
|
||||||
|
/// using `BadgeTheme.of(context)`. Instances of [BadgeThemeData] can
|
||||||
|
/// be customized with [BadgeThemeData.copyWith].
|
||||||
|
///
|
||||||
|
/// Typically a [BadgeThemeData] is specified as part of the
|
||||||
|
/// overall [Theme] with [ThemeData.badgeTheme].
|
||||||
|
///
|
||||||
|
/// All [BadgeThemeData] properties are `null` by default.
|
||||||
|
/// When null, the [Badge] will use the values from [ThemeData]
|
||||||
|
/// if they exist, otherwise it will provide its own defaults.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData], which describes the overall theme information for the
|
||||||
|
/// application.
|
||||||
|
@immutable
|
||||||
|
class BadgeThemeData with Diagnosticable {
|
||||||
|
/// Creates the set of color, style, and size properties used to configure [Badge].
|
||||||
|
const BadgeThemeData({
|
||||||
|
this.backgroundColor,
|
||||||
|
this.foregroundColor,
|
||||||
|
this.smallSize,
|
||||||
|
this.largeSize,
|
||||||
|
this.textStyle,
|
||||||
|
this.padding,
|
||||||
|
this.alignment,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.backgroundColor].
|
||||||
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.foregroundColor].
|
||||||
|
final Color? foregroundColor;
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.smallSize].
|
||||||
|
final double? smallSize;
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.largeSize].
|
||||||
|
final double? largeSize;
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.textStyle].
|
||||||
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.padding].
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.alignment].
|
||||||
|
final AlignmentDirectional? alignment;
|
||||||
|
|
||||||
|
/// Creates a copy of this object but with the given fields replaced with the
|
||||||
|
/// new values.
|
||||||
|
BadgeThemeData copyWith({
|
||||||
|
Color? backgroundColor,
|
||||||
|
Color? foregroundColor,
|
||||||
|
double? smallSize,
|
||||||
|
double? largeSize,
|
||||||
|
TextStyle? textStyle,
|
||||||
|
EdgeInsetsGeometry? padding,
|
||||||
|
AlignmentDirectional? alignment,
|
||||||
|
}) {
|
||||||
|
return BadgeThemeData(
|
||||||
|
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||||
|
foregroundColor: foregroundColor ?? this.foregroundColor,
|
||||||
|
smallSize: smallSize ?? this.smallSize,
|
||||||
|
largeSize: largeSize ?? this.largeSize,
|
||||||
|
textStyle: textStyle ?? this.textStyle,
|
||||||
|
padding: padding ?? this.padding,
|
||||||
|
alignment: alignment ?? this.alignment,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linearly interpolate between two [Badge] themes.
|
||||||
|
static BadgeThemeData lerp(BadgeThemeData? a, BadgeThemeData? b, double t) {
|
||||||
|
return BadgeThemeData(
|
||||||
|
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
|
||||||
|
foregroundColor: Color.lerp(a?.foregroundColor, b?.foregroundColor, t),
|
||||||
|
smallSize: lerpDouble(a?.smallSize, b?.smallSize, t),
|
||||||
|
largeSize: lerpDouble(a?.largeSize, b?.largeSize, t),
|
||||||
|
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, t),
|
||||||
|
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
|
||||||
|
alignment: AlignmentDirectional.lerp(a?.alignment, b?.alignment, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
backgroundColor,
|
||||||
|
foregroundColor,
|
||||||
|
smallSize,
|
||||||
|
largeSize,
|
||||||
|
textStyle,
|
||||||
|
padding,
|
||||||
|
alignment,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (other.runtimeType != runtimeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return other is BadgeThemeData
|
||||||
|
&& other.backgroundColor == backgroundColor
|
||||||
|
&& other.foregroundColor == foregroundColor
|
||||||
|
&& other.smallSize == smallSize
|
||||||
|
&& other.largeSize == largeSize
|
||||||
|
&& other.textStyle == textStyle
|
||||||
|
&& other.padding == padding
|
||||||
|
&& other.alignment == alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
|
||||||
|
properties.add(ColorProperty('foregroundColor', foregroundColor, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('smallSize', smallSize, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('largeSize', largeSize, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<AlignmentDirectional>('alignment', alignment, defaultValue: null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An inherited widget that overrides the default color style, and size
|
||||||
|
/// parameters for [Badge]s in this widget's subtree.
|
||||||
|
///
|
||||||
|
/// Values specified here override the defaults for [Badge] properties which
|
||||||
|
/// are not given an explicit non-null value.
|
||||||
|
class BadgeTheme extends InheritedTheme {
|
||||||
|
/// Creates a theme that overrides the default color parameters for [Badge]s
|
||||||
|
/// in this widget's subtree.
|
||||||
|
const BadgeTheme({
|
||||||
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
required super.child,
|
||||||
|
}) : assert(data != null);
|
||||||
|
|
||||||
|
/// Specifies the default color and size overrides for descendant [Badge] widgets.
|
||||||
|
final BadgeThemeData data;
|
||||||
|
|
||||||
|
/// The closest instance of this class that encloses the given context.
|
||||||
|
///
|
||||||
|
/// If there is no enclosing [BadgeTheme] widget, then
|
||||||
|
/// [ThemeData.badgeTheme] is used.
|
||||||
|
///
|
||||||
|
/// Typical usage is as follows:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// BadgeThemeData theme = BadgeTheme.of(context);
|
||||||
|
/// ```
|
||||||
|
static BadgeThemeData of(BuildContext context) {
|
||||||
|
final BadgeTheme? badgeTheme = context.dependOnInheritedWidgetOfExactType<BadgeTheme>();
|
||||||
|
return badgeTheme?.data ?? Theme.of(context).badgeTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget wrap(BuildContext context, Widget child) {
|
||||||
|
return BadgeTheme(data: data, child: child);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(BadgeTheme oldWidget) => data != oldWidget.data;
|
||||||
|
}
|
@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'app_bar_theme.dart';
|
import 'app_bar_theme.dart';
|
||||||
|
import 'badge_theme.dart';
|
||||||
import 'banner_theme.dart';
|
import 'banner_theme.dart';
|
||||||
import 'bottom_app_bar_theme.dart';
|
import 'bottom_app_bar_theme.dart';
|
||||||
import 'bottom_navigation_bar_theme.dart';
|
import 'bottom_navigation_bar_theme.dart';
|
||||||
@ -331,6 +332,7 @@ class ThemeData with Diagnosticable {
|
|||||||
Typography? typography,
|
Typography? typography,
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
AppBarTheme? appBarTheme,
|
AppBarTheme? appBarTheme,
|
||||||
|
BadgeThemeData? badgeTheme,
|
||||||
MaterialBannerThemeData? bannerTheme,
|
MaterialBannerThemeData? bannerTheme,
|
||||||
BottomAppBarTheme? bottomAppBarTheme,
|
BottomAppBarTheme? bottomAppBarTheme,
|
||||||
BottomNavigationBarThemeData? bottomNavigationBarTheme,
|
BottomNavigationBarThemeData? bottomNavigationBarTheme,
|
||||||
@ -583,6 +585,7 @@ class ThemeData with Diagnosticable {
|
|||||||
|
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme ??= const AppBarTheme();
|
appBarTheme ??= const AppBarTheme();
|
||||||
|
badgeTheme ??= const BadgeThemeData();
|
||||||
bannerTheme ??= const MaterialBannerThemeData();
|
bannerTheme ??= const MaterialBannerThemeData();
|
||||||
bottomAppBarTheme ??= const BottomAppBarTheme();
|
bottomAppBarTheme ??= const BottomAppBarTheme();
|
||||||
bottomNavigationBarTheme ??= const BottomNavigationBarThemeData();
|
bottomNavigationBarTheme ??= const BottomNavigationBarThemeData();
|
||||||
@ -676,6 +679,7 @@ class ThemeData with Diagnosticable {
|
|||||||
primaryIconTheme: primaryIconTheme,
|
primaryIconTheme: primaryIconTheme,
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme: appBarTheme,
|
appBarTheme: appBarTheme,
|
||||||
|
badgeTheme: badgeTheme,
|
||||||
bannerTheme: bannerTheme,
|
bannerTheme: bannerTheme,
|
||||||
bottomAppBarTheme: bottomAppBarTheme,
|
bottomAppBarTheme: bottomAppBarTheme,
|
||||||
bottomNavigationBarTheme: bottomNavigationBarTheme,
|
bottomNavigationBarTheme: bottomNavigationBarTheme,
|
||||||
@ -786,6 +790,7 @@ class ThemeData with Diagnosticable {
|
|||||||
required this.typography,
|
required this.typography,
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
required this.appBarTheme,
|
required this.appBarTheme,
|
||||||
|
required this.badgeTheme,
|
||||||
required this.bannerTheme,
|
required this.bannerTheme,
|
||||||
required this.bottomAppBarTheme,
|
required this.bottomAppBarTheme,
|
||||||
required this.bottomNavigationBarTheme,
|
required this.bottomNavigationBarTheme,
|
||||||
@ -954,6 +959,7 @@ class ThemeData with Diagnosticable {
|
|||||||
assert(typography != null),
|
assert(typography != null),
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
assert(appBarTheme != null),
|
assert(appBarTheme != null),
|
||||||
|
assert(badgeTheme != null),
|
||||||
assert(bannerTheme != null),
|
assert(bannerTheme != null),
|
||||||
assert(bottomAppBarTheme != null),
|
assert(bottomAppBarTheme != null),
|
||||||
assert(bottomNavigationBarTheme != null),
|
assert(bottomNavigationBarTheme != null),
|
||||||
@ -1487,6 +1493,9 @@ class ThemeData with Diagnosticable {
|
|||||||
/// textTheme of [AppBar]s.
|
/// textTheme of [AppBar]s.
|
||||||
final AppBarTheme appBarTheme;
|
final AppBarTheme appBarTheme;
|
||||||
|
|
||||||
|
/// A theme for customizing the color of [Badge]s.
|
||||||
|
final BadgeThemeData badgeTheme;
|
||||||
|
|
||||||
/// A theme for customizing the color and text style of a [MaterialBanner].
|
/// A theme for customizing the color and text style of a [MaterialBanner].
|
||||||
final MaterialBannerThemeData bannerTheme;
|
final MaterialBannerThemeData bannerTheme;
|
||||||
|
|
||||||
@ -1842,6 +1851,7 @@ class ThemeData with Diagnosticable {
|
|||||||
Typography? typography,
|
Typography? typography,
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
AppBarTheme? appBarTheme,
|
AppBarTheme? appBarTheme,
|
||||||
|
BadgeThemeData? badgeTheme,
|
||||||
MaterialBannerThemeData? bannerTheme,
|
MaterialBannerThemeData? bannerTheme,
|
||||||
BottomAppBarTheme? bottomAppBarTheme,
|
BottomAppBarTheme? bottomAppBarTheme,
|
||||||
BottomNavigationBarThemeData? bottomNavigationBarTheme,
|
BottomNavigationBarThemeData? bottomNavigationBarTheme,
|
||||||
@ -2003,6 +2013,7 @@ class ThemeData with Diagnosticable {
|
|||||||
typography: typography ?? this.typography,
|
typography: typography ?? this.typography,
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme: appBarTheme ?? this.appBarTheme,
|
appBarTheme: appBarTheme ?? this.appBarTheme,
|
||||||
|
badgeTheme: badgeTheme ?? this.badgeTheme,
|
||||||
bannerTheme: bannerTheme ?? this.bannerTheme,
|
bannerTheme: bannerTheme ?? this.bannerTheme,
|
||||||
bottomAppBarTheme: bottomAppBarTheme ?? this.bottomAppBarTheme,
|
bottomAppBarTheme: bottomAppBarTheme ?? this.bottomAppBarTheme,
|
||||||
bottomNavigationBarTheme: bottomNavigationBarTheme ?? this.bottomNavigationBarTheme,
|
bottomNavigationBarTheme: bottomNavigationBarTheme ?? this.bottomNavigationBarTheme,
|
||||||
@ -2206,6 +2217,7 @@ class ThemeData with Diagnosticable {
|
|||||||
typography: Typography.lerp(a.typography, b.typography, t),
|
typography: Typography.lerp(a.typography, b.typography, t),
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme: AppBarTheme.lerp(a.appBarTheme, b.appBarTheme, t),
|
appBarTheme: AppBarTheme.lerp(a.appBarTheme, b.appBarTheme, t),
|
||||||
|
badgeTheme: BadgeThemeData.lerp(a.badgeTheme, b.badgeTheme, t),
|
||||||
bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t),
|
bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t),
|
||||||
bottomAppBarTheme: BottomAppBarTheme.lerp(a.bottomAppBarTheme, b.bottomAppBarTheme, t),
|
bottomAppBarTheme: BottomAppBarTheme.lerp(a.bottomAppBarTheme, b.bottomAppBarTheme, t),
|
||||||
bottomNavigationBarTheme: BottomNavigationBarThemeData.lerp(a.bottomNavigationBarTheme, b.bottomNavigationBarTheme, t),
|
bottomNavigationBarTheme: BottomNavigationBarThemeData.lerp(a.bottomNavigationBarTheme, b.bottomNavigationBarTheme, t),
|
||||||
@ -2311,6 +2323,7 @@ class ThemeData with Diagnosticable {
|
|||||||
other.typography == typography &&
|
other.typography == typography &&
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
other.appBarTheme == appBarTheme &&
|
other.appBarTheme == appBarTheme &&
|
||||||
|
other.badgeTheme == badgeTheme &&
|
||||||
other.bannerTheme == bannerTheme &&
|
other.bannerTheme == bannerTheme &&
|
||||||
other.bottomAppBarTheme == bottomAppBarTheme &&
|
other.bottomAppBarTheme == bottomAppBarTheme &&
|
||||||
other.bottomNavigationBarTheme == bottomNavigationBarTheme &&
|
other.bottomNavigationBarTheme == bottomNavigationBarTheme &&
|
||||||
@ -2413,6 +2426,7 @@ class ThemeData with Diagnosticable {
|
|||||||
typography,
|
typography,
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme,
|
appBarTheme,
|
||||||
|
badgeTheme,
|
||||||
bannerTheme,
|
bannerTheme,
|
||||||
bottomAppBarTheme,
|
bottomAppBarTheme,
|
||||||
bottomNavigationBarTheme,
|
bottomNavigationBarTheme,
|
||||||
@ -2517,6 +2531,7 @@ class ThemeData with Diagnosticable {
|
|||||||
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography, level: DiagnosticLevel.debug));
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
properties.add(DiagnosticsProperty<AppBarTheme>('appBarTheme', appBarTheme, defaultValue: defaultData.appBarTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<AppBarTheme>('appBarTheme', appBarTheme, defaultValue: defaultData.appBarTheme, level: DiagnosticLevel.debug));
|
||||||
|
properties.add(DiagnosticsProperty<BadgeThemeData>('badgeTheme', badgeTheme, defaultValue: defaultData.badgeTheme, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<BottomAppBarTheme>('bottomAppBarTheme', bottomAppBarTheme, defaultValue: defaultData.bottomAppBarTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<BottomAppBarTheme>('bottomAppBarTheme', bottomAppBarTheme, defaultValue: defaultData.bottomAppBarTheme, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<BottomNavigationBarThemeData>('bottomNavigationBarTheme', bottomNavigationBarTheme, defaultValue: defaultData.bottomNavigationBarTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<BottomNavigationBarThemeData>('bottomNavigationBarTheme', bottomNavigationBarTheme, defaultValue: defaultData.bottomNavigationBarTheme, level: DiagnosticLevel.debug));
|
||||||
|
206
packages/flutter/test/material/badge_test.dart
Normal file
206
packages/flutter/test/material/badge_test.dart
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// 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/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Large Badge defaults', (WidgetTester tester) async {
|
||||||
|
late final ThemeData theme;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData.light(useMaterial3: true),
|
||||||
|
home: Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
// theme.textTtheme is updated when the MaterialApp is built.
|
||||||
|
theme = Theme.of(context);
|
||||||
|
return const Badge(
|
||||||
|
label: Text('0'),
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.renderObject<RenderParagraph>(find.text('0')).text.style,
|
||||||
|
theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onError),
|
||||||
|
);
|
||||||
|
|
||||||
|
// default badge alignment = AlignmentDirectional(12, -4)
|
||||||
|
// default padding = EdgeInsets.symmetric(horizontal: 4)
|
||||||
|
// default largeSize = 16
|
||||||
|
// '0'.width = 12
|
||||||
|
// icon.width = 24
|
||||||
|
|
||||||
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
|
|
||||||
|
// x = alignment.start + padding.left
|
||||||
|
// y = alignment.top
|
||||||
|
expect(tester.getTopLeft(find.text('0')), const Offset(16, -4));
|
||||||
|
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
// '0'.width = 12
|
||||||
|
// L = alignment.start
|
||||||
|
// T = alignment.top
|
||||||
|
// R = L + '0'.width + padding.width
|
||||||
|
// B = T + largeSize, R = largeSize/2
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(12, -4, 32, 12, const Radius.circular(8)), color: theme.colorScheme.error));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Large Badge defaults with RTL', (WidgetTester tester) async {
|
||||||
|
late final ThemeData theme;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData.light(useMaterial3: true),
|
||||||
|
home: Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
// theme.textTtheme is updated when the MaterialApp is built.
|
||||||
|
theme = Theme.of(context);
|
||||||
|
return const Badge(
|
||||||
|
label: Text('0'),
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.renderObject<RenderParagraph>(find.text('0')).text.style,
|
||||||
|
theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onError),
|
||||||
|
);
|
||||||
|
|
||||||
|
// default badge alignment = AlignmentDirectional(12, -4)
|
||||||
|
// default padding = EdgeInsets.symmetric(horizontal: 4)
|
||||||
|
// default largeSize = 16
|
||||||
|
// '0'.width = 12
|
||||||
|
// icon.width = 24
|
||||||
|
|
||||||
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
|
|
||||||
|
// x = icon.width - alignment.start - '0'.width - padding.right
|
||||||
|
// y = alignment.top
|
||||||
|
expect(tester.getTopLeft(find.text('0')), const Offset(-4, -4));
|
||||||
|
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
// L = icon.width - alignment.start - '0.width' - padding.width
|
||||||
|
// T = alignment.top
|
||||||
|
// R = L + '0.width' + padding.width
|
||||||
|
// B = T + largeSize
|
||||||
|
// R = largeSize/2
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(-8, -4, 12, 12, const Radius.circular(8)), color: theme.colorScheme.error));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Small Badge defaults', (WidgetTester tester) async {
|
||||||
|
final ThemeData theme = ThemeData.light(useMaterial3: true);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: theme,
|
||||||
|
home: const Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Badge(
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// default badge location is end=0, top=0
|
||||||
|
// default padding = EdgeInsets.symmetric(horizontal: 4)
|
||||||
|
// default smallSize = 6
|
||||||
|
// icon.width = 24
|
||||||
|
|
||||||
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
|
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
// L = icon.size.width - smallSize
|
||||||
|
// T = 0
|
||||||
|
// R = icon.size.width
|
||||||
|
// B = smallSize
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(18, 0, 24, 6, const Radius.circular(3)), color: theme.colorScheme.error));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Small Badge RTL defaults', (WidgetTester tester) async {
|
||||||
|
final ThemeData theme = ThemeData.light(useMaterial3: true);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: theme,
|
||||||
|
home: const Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Badge(
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// default badge location is end=0, top=0
|
||||||
|
// default smallSize = 6
|
||||||
|
// icon.width = 24
|
||||||
|
|
||||||
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
|
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
// L = 0
|
||||||
|
// T = 0
|
||||||
|
// R = smallSize
|
||||||
|
// B = smallSize
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 0, 6, 6, const Radius.circular(3)), color: theme.colorScheme.error));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Large Badge textStyle and colors', (WidgetTester tester) async {
|
||||||
|
final ThemeData theme = ThemeData.light(useMaterial3: true);
|
||||||
|
const Color green = Color(0xff00ff00);
|
||||||
|
const Color black = Color(0xff000000);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: theme,
|
||||||
|
home: const Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Badge(
|
||||||
|
foregroundColor: green,
|
||||||
|
backgroundColor: black,
|
||||||
|
textStyle: TextStyle(fontSize: 10),
|
||||||
|
label: Text('0'),
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final TextStyle textStyle = tester.renderObject<RenderParagraph>(find.text('0')).text.style!;
|
||||||
|
expect(textStyle.fontSize, 10);
|
||||||
|
expect(textStyle.color, green);
|
||||||
|
expect(tester.renderObject(find.byType(Badge)), paints..rrect(color: black));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
155
packages/flutter/test/material/badge_theme_test.dart
Normal file
155
packages/flutter/test/material/badge_theme_test.dart
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
// 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/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('BadgeThemeData copyWith, ==, hashCode basics', () {
|
||||||
|
expect(const BadgeThemeData(), const BadgeThemeData().copyWith());
|
||||||
|
expect(const BadgeThemeData().hashCode, const BadgeThemeData().copyWith().hashCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('BadgeThemeData defaults', () {
|
||||||
|
const BadgeThemeData themeData = BadgeThemeData();
|
||||||
|
expect(themeData.backgroundColor, null);
|
||||||
|
expect(themeData.foregroundColor, null);
|
||||||
|
expect(themeData.smallSize, null);
|
||||||
|
expect(themeData.largeSize, null);
|
||||||
|
expect(themeData.textStyle, null);
|
||||||
|
expect(themeData.padding, null);
|
||||||
|
expect(themeData.alignment, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Default BadgeThemeData debugFillProperties', (WidgetTester tester) async {
|
||||||
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
|
const BadgeThemeData().debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
expect(description, <String>[]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('BadgeThemeData implements debugFillProperties', (WidgetTester tester) async {
|
||||||
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
|
const BadgeThemeData(
|
||||||
|
backgroundColor: Color(0xfffffff0),
|
||||||
|
foregroundColor: Color(0xfffffff1),
|
||||||
|
smallSize: 1,
|
||||||
|
largeSize: 2,
|
||||||
|
textStyle: TextStyle(fontSize: 4),
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
alignment: AlignmentDirectional(6, 7),
|
||||||
|
).debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
expect(description, <String>[
|
||||||
|
'backgroundColor: Color(0xfffffff0)',
|
||||||
|
'foregroundColor: Color(0xfffffff1)',
|
||||||
|
'smallSize: 1.0',
|
||||||
|
'largeSize: 2.0',
|
||||||
|
'textStyle: TextStyle(inherit: true, size: 4.0)',
|
||||||
|
'padding: EdgeInsets.all(5.0)',
|
||||||
|
'alignment: AlignmentDirectional(6.0, 7.0)'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Badge uses ThemeData badge theme', (WidgetTester tester) async {
|
||||||
|
const Color green = Color(0xff00ff00);
|
||||||
|
const Color black = Color(0xff000000);
|
||||||
|
const BadgeThemeData badgeTheme = BadgeThemeData(
|
||||||
|
backgroundColor: green,
|
||||||
|
foregroundColor: black,
|
||||||
|
smallSize: 5,
|
||||||
|
largeSize: 20,
|
||||||
|
textStyle: TextStyle(fontSize: 12),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
alignment: AlignmentDirectional(24, 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData.light(useMaterial3: true).copyWith(
|
||||||
|
badgeTheme: badgeTheme,
|
||||||
|
),
|
||||||
|
home: const Scaffold(
|
||||||
|
body: Badge(
|
||||||
|
label: Text('1234'),
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// text width = 48 = fontSize * 4, text height = fontSize
|
||||||
|
expect(tester.getSize(find.text('1234')), const Size(48, 12));
|
||||||
|
|
||||||
|
// x = 29 = alignment.start + padding.left, y = 4 = (largeSize - fontSize) / 2
|
||||||
|
expect(tester.getTopLeft(find.text('1234')), const Offset(29, 4));
|
||||||
|
|
||||||
|
|
||||||
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
|
|
||||||
|
final TextStyle textStyle = tester.renderObject<RenderParagraph>(find.text('1234')).text.style!;
|
||||||
|
expect(textStyle.fontSize, 12);
|
||||||
|
expect(textStyle.color, black);
|
||||||
|
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
// L = alignment.start, T = alignment.top, R = L + fontSize * 4 + padding.width, B = largeSize R = largeSize/2
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(24, 0, 82, 20, const Radius.circular(10)), color: green));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// This test is essentially the same as 'Badge uses ThemeData badge theme'. In
|
||||||
|
// this case the theme is introduced with the BadgeTheme widget instead of
|
||||||
|
// ThemeData.badgeTheme.
|
||||||
|
testWidgets('Badge uses BadgeTheme', (WidgetTester tester) async {
|
||||||
|
const Color green = Color(0xff00ff00);
|
||||||
|
const Color black = Color(0xff000000);
|
||||||
|
const BadgeThemeData badgeTheme = BadgeThemeData(
|
||||||
|
backgroundColor: green,
|
||||||
|
foregroundColor: black,
|
||||||
|
smallSize: 5,
|
||||||
|
largeSize: 20,
|
||||||
|
textStyle: TextStyle(fontSize: 12),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
alignment: AlignmentDirectional(24, 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const MaterialApp(
|
||||||
|
home: BadgeTheme(
|
||||||
|
data: badgeTheme,
|
||||||
|
child: Scaffold(
|
||||||
|
body: Badge(
|
||||||
|
label: Text('1234'),
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tester.getSize(find.text('1234')), const Size(48, 12));
|
||||||
|
expect(tester.getTopLeft(find.text('1234')), const Offset(29, 4));
|
||||||
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
|
final TextStyle textStyle = tester.renderObject<RenderParagraph>(find.text('1234')).text.style!;
|
||||||
|
expect(textStyle.fontSize, 12);
|
||||||
|
expect(textStyle.color, black);
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(24, 0, 82, 20, const Radius.circular(10)), color: green));
|
||||||
|
});
|
||||||
|
}
|
@ -770,6 +770,7 @@ void main() {
|
|||||||
typography: Typography.material2018(),
|
typography: Typography.material2018(),
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme: const AppBarTheme(backgroundColor: Colors.black),
|
appBarTheme: const AppBarTheme(backgroundColor: Colors.black),
|
||||||
|
badgeTheme: const BadgeThemeData(backgroundColor: Colors.black),
|
||||||
bannerTheme: const MaterialBannerThemeData(backgroundColor: Colors.black),
|
bannerTheme: const MaterialBannerThemeData(backgroundColor: Colors.black),
|
||||||
bottomAppBarTheme: const BottomAppBarTheme(color: Colors.black),
|
bottomAppBarTheme: const BottomAppBarTheme(color: Colors.black),
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed),
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed),
|
||||||
@ -887,6 +888,7 @@ void main() {
|
|||||||
|
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme: const AppBarTheme(backgroundColor: Colors.white),
|
appBarTheme: const AppBarTheme(backgroundColor: Colors.white),
|
||||||
|
badgeTheme: const BadgeThemeData(backgroundColor: Colors.black),
|
||||||
bannerTheme: const MaterialBannerThemeData(backgroundColor: Colors.white),
|
bannerTheme: const MaterialBannerThemeData(backgroundColor: Colors.white),
|
||||||
bottomAppBarTheme: const BottomAppBarTheme(color: Colors.white),
|
bottomAppBarTheme: const BottomAppBarTheme(color: Colors.white),
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.shifting),
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.shifting),
|
||||||
@ -990,6 +992,7 @@ void main() {
|
|||||||
|
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
appBarTheme: otherTheme.appBarTheme,
|
appBarTheme: otherTheme.appBarTheme,
|
||||||
|
badgeTheme: otherTheme.badgeTheme,
|
||||||
bannerTheme: otherTheme.bannerTheme,
|
bannerTheme: otherTheme.bannerTheme,
|
||||||
bottomAppBarTheme: otherTheme.bottomAppBarTheme,
|
bottomAppBarTheme: otherTheme.bottomAppBarTheme,
|
||||||
bottomNavigationBarTheme: otherTheme.bottomNavigationBarTheme,
|
bottomNavigationBarTheme: otherTheme.bottomNavigationBarTheme,
|
||||||
@ -1092,6 +1095,7 @@ void main() {
|
|||||||
|
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
expect(themeDataCopy.appBarTheme, equals(otherTheme.appBarTheme));
|
expect(themeDataCopy.appBarTheme, equals(otherTheme.appBarTheme));
|
||||||
|
expect(themeDataCopy.badgeTheme, equals(otherTheme.badgeTheme));
|
||||||
expect(themeDataCopy.bannerTheme, equals(otherTheme.bannerTheme));
|
expect(themeDataCopy.bannerTheme, equals(otherTheme.bannerTheme));
|
||||||
expect(themeDataCopy.bottomAppBarTheme, equals(otherTheme.bottomAppBarTheme));
|
expect(themeDataCopy.bottomAppBarTheme, equals(otherTheme.bottomAppBarTheme));
|
||||||
expect(themeDataCopy.bottomNavigationBarTheme, equals(otherTheme.bottomNavigationBarTheme));
|
expect(themeDataCopy.bottomNavigationBarTheme, equals(otherTheme.bottomNavigationBarTheme));
|
||||||
@ -1230,6 +1234,7 @@ void main() {
|
|||||||
'primaryIconTheme',
|
'primaryIconTheme',
|
||||||
// COMPONENT THEMES
|
// COMPONENT THEMES
|
||||||
'appBarTheme',
|
'appBarTheme',
|
||||||
|
'badgeTheme',
|
||||||
'bannerTheme',
|
'bannerTheme',
|
||||||
'bottomAppBarTheme',
|
'bottomAppBarTheme',
|
||||||
'bottomNavigationBarTheme',
|
'bottomNavigationBarTheme',
|
||||||
|
Loading…
Reference in New Issue
Block a user