mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Correct Badge interpretation of its alignment parameter (#119853)
This commit is contained in:
parent
d8154fde7a
commit
75ca31b0e4
@ -16,7 +16,7 @@ class _${blockName}DefaultsM3 extends BadgeThemeData {
|
|||||||
smallSize: ${tokens["md.comp.badge.size"]},
|
smallSize: ${tokens["md.comp.badge.size"]},
|
||||||
largeSize: ${tokens["md.comp.badge.large.size"]},
|
largeSize: ${tokens["md.comp.badge.large.size"]},
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
alignment: const AlignmentDirectional(12, -4),
|
alignment: AlignmentDirectional.topEnd,
|
||||||
);
|
);
|
||||||
|
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'badge_theme.dart';
|
import 'badge_theme.dart';
|
||||||
@ -35,6 +36,7 @@ class Badge extends StatelessWidget {
|
|||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.alignment,
|
this.alignment,
|
||||||
|
this.offset,
|
||||||
this.label,
|
this.label,
|
||||||
this.isLabelVisible = true,
|
this.isLabelVisible = true,
|
||||||
this.child,
|
this.child,
|
||||||
@ -54,6 +56,7 @@ class Badge extends StatelessWidget {
|
|||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.alignment,
|
this.alignment,
|
||||||
|
this.offset,
|
||||||
required int count,
|
required int count,
|
||||||
this.isLabelVisible = true,
|
this.isLabelVisible = true,
|
||||||
this.child,
|
this.child,
|
||||||
@ -106,13 +109,29 @@ class Badge extends StatelessWidget {
|
|||||||
/// left and right if the theme's value is null.
|
/// left and right if the theme's value is null.
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
/// The location of the [label] relative to the [child].
|
/// Combined with [offset] to determine the location of the [label]
|
||||||
|
/// relative to the [child].
|
||||||
|
///
|
||||||
|
/// The alignment positions the label in the same way a child of an
|
||||||
|
/// [Align] widget is positioned, except that, the alignment is
|
||||||
|
/// resolved as if the label was a [largeSize] square and [offset]
|
||||||
|
/// is added to the result.
|
||||||
///
|
///
|
||||||
/// This value is only used if [label] is non-null.
|
/// This value is only used if [label] is non-null.
|
||||||
///
|
///
|
||||||
/// Defaults to the [BadgeTheme]'s alignment, or `start = 12`
|
/// Defaults to the [BadgeTheme]'s alignment, or
|
||||||
/// and `top = -4` if the theme's value is null.
|
/// [AlignmentDirectional.topEnd] if the theme's value is null.
|
||||||
final AlignmentDirectional? alignment;
|
final AlignmentGeometry? alignment;
|
||||||
|
|
||||||
|
/// Combined with [alignment] to determine the location of the [label]
|
||||||
|
/// relative to the [child].
|
||||||
|
///
|
||||||
|
/// This value is only used if [label] is non-null.
|
||||||
|
///
|
||||||
|
/// Defaults to the [BadgeTheme]'s offset, or
|
||||||
|
/// if the theme's value is null then `Offset(4, -4)` for
|
||||||
|
/// [TextDirection.ltr] or `Offset(-4, -4)` for [TextDirection.rtl].
|
||||||
|
final Offset? offset;
|
||||||
|
|
||||||
/// The badge's content, typically a [Text] widget that contains 1 to 4
|
/// The badge's content, typically a [Text] widget that contains 1 to 4
|
||||||
/// characters.
|
/// characters.
|
||||||
@ -168,24 +187,99 @@ class Badge extends StatelessWidget {
|
|||||||
return badge;
|
return badge;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AlignmentDirectional effectiveAlignment = alignment ?? badgeTheme.alignment ?? defaults.alignment!;
|
final AlignmentGeometry effectiveAlignment = alignment ?? badgeTheme.alignment ?? defaults.alignment!;
|
||||||
|
final TextDirection textDirection = Directionality.of(context);
|
||||||
|
final Offset defaultOffset = textDirection == TextDirection.ltr ? const Offset(4, -4) : const Offset(-4, -4);
|
||||||
|
final Offset effectiveOffset = offset ?? badgeTheme.offset ?? defaultOffset;
|
||||||
|
|
||||||
return
|
return
|
||||||
Stack(
|
Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
child!,
|
child!,
|
||||||
Positioned.directional(
|
Positioned.fill(
|
||||||
textDirection: Directionality.of(context),
|
child: _Badge(
|
||||||
start: label == null ? null : effectiveAlignment.start,
|
alignment: effectiveAlignment,
|
||||||
end: label == null ? 0 : null,
|
offset: label == null ? Offset.zero : effectiveOffset,
|
||||||
top: label == null ? 0 : effectiveAlignment.y,
|
textDirection: textDirection,
|
||||||
child: badge,
|
child: badge,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Badge extends SingleChildRenderObjectWidget {
|
||||||
|
const _Badge({
|
||||||
|
required this.alignment,
|
||||||
|
required this.offset,
|
||||||
|
required this.textDirection,
|
||||||
|
super.child, // the badge
|
||||||
|
});
|
||||||
|
|
||||||
|
final AlignmentGeometry alignment;
|
||||||
|
final Offset offset;
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_RenderBadge createRenderObject(BuildContext context) {
|
||||||
|
return _RenderBadge(
|
||||||
|
alignment: alignment,
|
||||||
|
offset: offset,
|
||||||
|
textDirection: Directionality.maybeOf(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateRenderObject(BuildContext context, _RenderBadge renderObject) {
|
||||||
|
renderObject
|
||||||
|
..alignment = alignment
|
||||||
|
..offset = offset
|
||||||
|
..textDirection = Directionality.maybeOf(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
|
||||||
|
properties.add(DiagnosticsProperty<Offset>('offset', offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RenderBadge extends RenderAligningShiftedBox {
|
||||||
|
_RenderBadge({
|
||||||
|
super.textDirection,
|
||||||
|
super.alignment,
|
||||||
|
required Offset offset,
|
||||||
|
}) : _offset = offset;
|
||||||
|
|
||||||
|
Offset get offset => _offset;
|
||||||
|
Offset _offset;
|
||||||
|
set offset(Offset value) {
|
||||||
|
if (_offset == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_offset = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout() {
|
||||||
|
final BoxConstraints constraints = this.constraints;
|
||||||
|
assert(constraints.hasBoundedWidth);
|
||||||
|
assert(constraints.hasBoundedHeight);
|
||||||
|
size = constraints.biggest;
|
||||||
|
|
||||||
|
child!.layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
|
final double badgeSize = child!.size.height;
|
||||||
|
final Alignment resolvedAlignment = alignment.resolve(textDirection);
|
||||||
|
final BoxParentData childParentData = child!.parentData! as BoxParentData;
|
||||||
|
childParentData.offset = offset + resolvedAlignment.alongOffset(Offset(size.width - badgeSize, size.height - badgeSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// BEGIN GENERATED TOKEN PROPERTIES - Badge
|
// BEGIN GENERATED TOKEN PROPERTIES - Badge
|
||||||
|
|
||||||
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
||||||
@ -200,7 +294,7 @@ class _BadgeDefaultsM3 extends BadgeThemeData {
|
|||||||
smallSize: 6.0,
|
smallSize: 6.0,
|
||||||
largeSize: 16.0,
|
largeSize: 16.0,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
alignment: const AlignmentDirectional(12, -4),
|
alignment: AlignmentDirectional.topEnd,
|
||||||
);
|
);
|
||||||
|
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
|
@ -41,6 +41,7 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.alignment,
|
this.alignment,
|
||||||
|
this.offset,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Overrides the default value for [Badge.backgroundColor].
|
/// Overrides the default value for [Badge.backgroundColor].
|
||||||
@ -62,7 +63,10 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
/// Overrides the default value for [Badge.alignment].
|
/// Overrides the default value for [Badge.alignment].
|
||||||
final AlignmentDirectional? alignment;
|
final AlignmentGeometry? alignment;
|
||||||
|
|
||||||
|
/// Overrides the default value for [Badge.offset].
|
||||||
|
final Offset? offset;
|
||||||
|
|
||||||
/// Creates a copy of this object but with the given fields replaced with the
|
/// Creates a copy of this object but with the given fields replaced with the
|
||||||
/// new values.
|
/// new values.
|
||||||
@ -73,7 +77,8 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
double? largeSize,
|
double? largeSize,
|
||||||
TextStyle? textStyle,
|
TextStyle? textStyle,
|
||||||
EdgeInsetsGeometry? padding,
|
EdgeInsetsGeometry? padding,
|
||||||
AlignmentDirectional? alignment,
|
AlignmentGeometry? alignment,
|
||||||
|
Offset? offset,
|
||||||
}) {
|
}) {
|
||||||
return BadgeThemeData(
|
return BadgeThemeData(
|
||||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||||
@ -83,6 +88,7 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
textStyle: textStyle ?? this.textStyle,
|
textStyle: textStyle ?? this.textStyle,
|
||||||
padding: padding ?? this.padding,
|
padding: padding ?? this.padding,
|
||||||
alignment: alignment ?? this.alignment,
|
alignment: alignment ?? this.alignment,
|
||||||
|
offset: offset ?? this.offset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +101,8 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
largeSize: lerpDouble(a?.largeSize, b?.largeSize, t),
|
largeSize: lerpDouble(a?.largeSize, b?.largeSize, t),
|
||||||
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, t),
|
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, t),
|
||||||
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
|
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
|
||||||
alignment: AlignmentDirectional.lerp(a?.alignment, b?.alignment, t),
|
alignment: AlignmentGeometry.lerp(a?.alignment, b?.alignment, t),
|
||||||
|
offset: Offset.lerp(a?.offset, b?.offset, t),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +115,7 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
textStyle,
|
textStyle,
|
||||||
padding,
|
padding,
|
||||||
alignment,
|
alignment,
|
||||||
|
offset,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -125,7 +133,8 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
&& other.largeSize == largeSize
|
&& other.largeSize == largeSize
|
||||||
&& other.textStyle == textStyle
|
&& other.textStyle == textStyle
|
||||||
&& other.padding == padding
|
&& other.padding == padding
|
||||||
&& other.alignment == alignment;
|
&& other.alignment == alignment
|
||||||
|
&& other.offset == offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -137,7 +146,8 @@ class BadgeThemeData with Diagnosticable {
|
|||||||
properties.add(DoubleProperty('largeSize', largeSize, defaultValue: null));
|
properties.add(DoubleProperty('largeSize', largeSize, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle, defaultValue: null));
|
properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<AlignmentDirectional>('alignment', alignment, defaultValue: null));
|
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<Offset>('offset', offset, defaultValue: null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ void main() {
|
|||||||
theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onError),
|
theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onError),
|
||||||
);
|
);
|
||||||
|
|
||||||
// default badge alignment = AlignmentDirectional(12, -4)
|
// default badge alignment = AlignmentDirection.topEnd
|
||||||
|
// default offset for LTR = Offset(4, -4)
|
||||||
// default padding = EdgeInsets.symmetric(horizontal: 4)
|
// default padding = EdgeInsets.symmetric(horizontal: 4)
|
||||||
// default largeSize = 16
|
// default largeSize = 16
|
||||||
// '0'.width = 12
|
// '0'.width = 12
|
||||||
@ -46,16 +47,9 @@ void main() {
|
|||||||
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
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));
|
expect(tester.getTopLeft(find.text('0')), const Offset(16, -4));
|
||||||
|
|
||||||
final RenderBox box = tester.renderObject(find.byType(Badge));
|
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));
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(12, -4, 32, 12, const Radius.circular(8)), color: theme.colorScheme.error));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,26 +83,13 @@ void main() {
|
|||||||
theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onError),
|
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.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
|
|
||||||
// x = icon.width - alignment.start - '0'.width - padding.right
|
expect(tester.getTopLeft(find.text('0')), const Offset(0, -4));
|
||||||
// y = alignment.top
|
|
||||||
expect(tester.getTopLeft(find.text('0')), const Offset(-4, -4));
|
|
||||||
|
|
||||||
final RenderBox box = tester.renderObject(find.byType(Badge));
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
// L = icon.width - alignment.start - '0.width' - padding.width
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(-4, -4, 16, 12, const Radius.circular(8)), color: theme.colorScheme.error));
|
||||||
// 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));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Essentially the same as 'Large Badge defaults'
|
// Essentially the same as 'Large Badge defaults'
|
||||||
@ -282,4 +263,153 @@ void main() {
|
|||||||
final RenderBox box = tester.renderObject(find.byType(Badge));
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
expect(box, isNot(paints..rrect()));
|
expect(box, isNot(paints..rrect()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Large Badge alignment', (WidgetTester tester) async {
|
||||||
|
const Radius badgeRadius = Radius.circular(8);
|
||||||
|
|
||||||
|
Widget buildFrame(Alignment alignment, [Offset offset = Offset.zero]) {
|
||||||
|
return MaterialApp(
|
||||||
|
theme: ThemeData.light(useMaterial3: true),
|
||||||
|
home: Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Badge(
|
||||||
|
// Default largeSize = 16, badge with label is "large".
|
||||||
|
label: Container(width: 8, height: 8, color: Colors.blue),
|
||||||
|
alignment: alignment,
|
||||||
|
offset: offset,
|
||||||
|
child: Container(
|
||||||
|
color: const Color(0xFF00FF00),
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topLeft));
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 0, 16, 16, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topCenter));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 8, 0, 100 + 8, 16, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topRight));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 0, 200, 16, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerLeft));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 100 - 8, 16, 100 + 8, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerRight));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 100 - 8, 200, 100 + 8, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomLeft));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 200 - 16, 16, 200, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomCenter));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 8, 200 - 16, 100 + 8, 200, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomRight));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 200 - 16, 200, 200, badgeRadius)));
|
||||||
|
|
||||||
|
const Offset offset = Offset(5, 10);
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topLeft, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 0, 16, 16, badgeRadius).shift(offset)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topCenter, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 8, 0, 100 + 8, 16, badgeRadius).shift(offset)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topRight, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 0, 200, 16, badgeRadius).shift(offset)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerLeft, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 100 - 8, 16, 100 + 8, badgeRadius).shift(offset)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerRight, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 100 - 8, 200, 100 + 8, badgeRadius).shift(offset)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomLeft, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 200 - 16, 16, 200, badgeRadius).shift(offset)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomCenter, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 8, 200 - 16, 100 + 8, 200, badgeRadius).shift(offset)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomRight, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 200 - 16, 200, 200, badgeRadius).shift(offset)));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Small Badge alignment', (WidgetTester tester) async {
|
||||||
|
const Radius badgeRadius = Radius.circular(3);
|
||||||
|
|
||||||
|
Widget buildFrame(Alignment alignment, [Offset offset = Offset.zero]) {
|
||||||
|
return MaterialApp(
|
||||||
|
theme: ThemeData.light(useMaterial3: true),
|
||||||
|
home: Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Badge(
|
||||||
|
// Default smallSize = 6, badge without label is "small".
|
||||||
|
alignment: alignment,
|
||||||
|
offset: offset, // Not used for smallSize badges.
|
||||||
|
child: Container(
|
||||||
|
color: const Color(0xFF00FF00),
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topLeft));
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(Badge));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 0, 6, 6, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topCenter));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 3, 0, 100 + 3, 6, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topRight));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 0, 200, 6, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerLeft));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 100 - 3, 6, 100 + 3, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerRight));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 100 - 3, 200, 100 + 3, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomLeft));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 200 - 6, 6, 200, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomCenter));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 3, 200 - 6, 100 + 3, 200, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomRight));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 200 - 6, 200, 200, badgeRadius)));
|
||||||
|
|
||||||
|
const Offset offset = Offset(5, 10); // Not used for smallSize Badges.
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topLeft, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 0, 6, 6, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topCenter, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 3, 0, 100 + 3, 6, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.topRight, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 0, 200, 6, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerLeft, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 100 - 3, 6, 100 + 3, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.centerRight, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 100 - 3, 200, 100 + 3, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomLeft, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 200 - 6, 6, 200, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomCenter, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 3, 200 - 6, 100 + 3, 200, badgeRadius)));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(Alignment.bottomRight, offset));
|
||||||
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 200 - 6, 200, 200, badgeRadius)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ void main() {
|
|||||||
expect(themeData.textStyle, null);
|
expect(themeData.textStyle, null);
|
||||||
expect(themeData.padding, null);
|
expect(themeData.padding, null);
|
||||||
expect(themeData.alignment, null);
|
expect(themeData.alignment, null);
|
||||||
|
expect(themeData.offset, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Default BadgeThemeData debugFillProperties', (WidgetTester tester) async {
|
testWidgets('Default BadgeThemeData debugFillProperties', (WidgetTester tester) async {
|
||||||
@ -47,6 +48,7 @@ void main() {
|
|||||||
textStyle: TextStyle(fontSize: 4),
|
textStyle: TextStyle(fontSize: 4),
|
||||||
padding: EdgeInsets.all(5),
|
padding: EdgeInsets.all(5),
|
||||||
alignment: AlignmentDirectional(6, 7),
|
alignment: AlignmentDirectional(6, 7),
|
||||||
|
offset: Offset.zero,
|
||||||
).debugFillProperties(builder);
|
).debugFillProperties(builder);
|
||||||
|
|
||||||
final List<String> description = builder.properties
|
final List<String> description = builder.properties
|
||||||
@ -61,7 +63,8 @@ void main() {
|
|||||||
'largeSize: 2.0',
|
'largeSize: 2.0',
|
||||||
'textStyle: TextStyle(inherit: true, size: 4.0)',
|
'textStyle: TextStyle(inherit: true, size: 4.0)',
|
||||||
'padding: EdgeInsets.all(5.0)',
|
'padding: EdgeInsets.all(5.0)',
|
||||||
'alignment: AlignmentDirectional(6.0, 7.0)'
|
'alignment: AlignmentDirectional(6.0, 7.0)',
|
||||||
|
'offset: Offset(0.0, 0.0)'
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -75,7 +78,8 @@ void main() {
|
|||||||
largeSize: 20,
|
largeSize: 20,
|
||||||
textStyle: TextStyle(fontSize: 12),
|
textStyle: TextStyle(fontSize: 12),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||||
alignment: AlignmentDirectional(24, 0),
|
alignment: Alignment.topRight,
|
||||||
|
offset: Offset(24, 0),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -95,8 +99,7 @@ void main() {
|
|||||||
// text width = 48 = fontSize * 4, text height = fontSize
|
// text width = 48 = fontSize * 4, text height = fontSize
|
||||||
expect(tester.getSize(find.text('1234')), const Size(48, 12));
|
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(33, 4));
|
||||||
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.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
@ -107,8 +110,7 @@ void main() {
|
|||||||
expect(textStyle.color, black);
|
expect(textStyle.color, black);
|
||||||
|
|
||||||
final RenderBox box = tester.renderObject(find.byType(Badge));
|
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(28, 0, 86, 20, const Radius.circular(10)), color: green));
|
||||||
expect(box, paints..rrect(rrect: RRect.fromLTRBR(24, 0, 82, 20, const Radius.circular(10)), color: green));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -125,7 +127,8 @@ void main() {
|
|||||||
largeSize: 20,
|
largeSize: 20,
|
||||||
textStyle: TextStyle(fontSize: 12),
|
textStyle: TextStyle(fontSize: 12),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||||
alignment: AlignmentDirectional(24, 0),
|
alignment: Alignment.topRight,
|
||||||
|
offset: Offset(24, 0),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -143,13 +146,13 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(tester.getSize(find.text('1234')), const Size(48, 12));
|
expect(tester.getSize(find.text('1234')), const Size(48, 12));
|
||||||
expect(tester.getTopLeft(find.text('1234')), const Offset(29, 4));
|
expect(tester.getTopLeft(find.text('1234')), const Offset(33, 4));
|
||||||
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
expect(tester.getSize(find.byType(Badge)), const Size(24, 24)); // default Icon size
|
||||||
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
expect(tester.getTopLeft(find.byType(Badge)), Offset.zero);
|
||||||
final TextStyle textStyle = tester.renderObject<RenderParagraph>(find.text('1234')).text.style!;
|
final TextStyle textStyle = tester.renderObject<RenderParagraph>(find.text('1234')).text.style!;
|
||||||
expect(textStyle.fontSize, 12);
|
expect(textStyle.fontSize, 12);
|
||||||
expect(textStyle.color, black);
|
expect(textStyle.color, black);
|
||||||
final RenderBox box = tester.renderObject(find.byType(Badge));
|
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));
|
expect(box, paints..rrect(rrect: RRect.fromLTRBR(28, 0, 86, 20, const Radius.circular(10)), color: green));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user