// 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'; void main() { testWidgets('Large Badge defaults', (WidgetTester tester) async { late final ThemeData theme; await tester.pumpWidget( MaterialApp( home: Align( alignment: Alignment.topLeft, child: Builder( builder: (BuildContext context) { // theme.textTheme is updated when the MaterialApp is built. theme = Theme.of(context); return const Badge(label: Text('0'), child: Icon(Icons.add)); }, ), ), ), ); expect( tester.renderObject(find.text('0')).text.style, theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onError), ); // default badge alignment = AlignmentDirection.topEnd // default offset for LTR = Offset(4, -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); expect(tester.getTopLeft(find.text('0')), const Offset(16, -4)); final RenderBox box = tester.renderObject(find.byType(Badge)); final rrect = RRect.fromLTRBR(12, -4, 31.5, 12, const Radius.circular(8)); expect(box, paints..rrect(rrect: rrect, color: theme.colorScheme.error)); }); testWidgets('Large Badge defaults with RTL', (WidgetTester tester) async { late final ThemeData theme; await tester.pumpWidget( MaterialApp( home: Directionality( textDirection: TextDirection.rtl, child: Align( alignment: Alignment.topLeft, child: Builder( builder: (BuildContext context) { // theme.textTheme is updated when the MaterialApp is built. theme = Theme.of(context); return const Badge(label: Text('0'), child: Icon(Icons.add)); }, ), ), ), ), ); expect( tester.renderObject(find.text('0')).text.style, theme.textTheme.labelSmall!.copyWith(color: theme.colorScheme.onError), ); 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.text('0')), const Offset(0, -4)); final RenderBox box = tester.renderObject(find.byType(Badge)); final rrect = RRect.fromLTRBR(-4, -4, 15.5, 12, const Radius.circular(8)); expect(box, paints..rrect(rrect: rrect, color: theme.colorScheme.error)); }); // Essentially the same as 'Large Badge defaults' testWidgets('Badge.count', (WidgetTester tester) async { late final ThemeData theme; Widget buildFrame(int count) { return MaterialApp( home: Align( alignment: Alignment.topLeft, child: Builder( builder: (BuildContext context) { // theme.textTheme is updated when the MaterialApp is built. if (count == 0) { theme = Theme.of(context); } return Badge.count(count: count, child: const Icon(Icons.add)); }, ), ), ); } await tester.pumpWidget(buildFrame(0)); expect( tester.renderObject(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 final rrect = RRect.fromLTRBR(12, -4, 31.5, 12, const Radius.circular(8)); expect(box, paints..rrect(rrect: rrect, color: theme.colorScheme.error)); await tester.pumpWidget(buildFrame(1000)); expect(find.text('999+'), findsOneWidget); }); testWidgets('Small Badge defaults', (WidgetTester tester) async { final theme = ThemeData(); 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 theme = ThemeData(); 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 theme = ThemeData(); const green = Color(0xff00ff00); const black = Color(0xff000000); await tester.pumpWidget( MaterialApp( theme: theme, home: const Align( alignment: Alignment.topLeft, child: Badge( textColor: green, backgroundColor: black, textStyle: TextStyle(fontSize: 10), label: Text('0'), child: Icon(Icons.add), ), ), ), ); final TextStyle textStyle = tester.renderObject(find.text('0')).text.style!; expect(textStyle.fontSize, 10); expect(textStyle.color, green); expect(tester.renderObject(find.byType(Badge)), paints..rrect(color: black)); }); testWidgets('isLabelVisible', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: Align( alignment: Alignment.topLeft, child: Badge(label: Text('0'), isLabelVisible: false, child: Icon(Icons.add)), ), ), ); expect(find.text('0'), findsNothing); expect(find.byType(Icon), findsOneWidget); 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)); expect(box, isNot(paints..rrect())); }); testWidgets('Large Badge alignment', (WidgetTester tester) async { const badgeRadius = Radius.circular(8); Widget buildFrame(Alignment alignment, [Offset offset = Offset.zero]) { return MaterialApp( 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, 16, 100 + 16, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.centerRight)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 100, 200, 100 + 16, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomLeft)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 200, 16, 200 + 16, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomCenter)); expect( box, paints..rrect(rrect: RRect.fromLTRBR(100 - 8, 200, 100 + 8, 200 + 16, badgeRadius)), ); await tester.pumpWidget(buildFrame(Alignment.bottomRight)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 16, 200, 200, 200 + 16, badgeRadius))); const 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, 16, 100 + 16, badgeRadius).shift(offset)), ); await tester.pumpWidget(buildFrame(Alignment.centerRight, offset)); expect( box, paints ..rrect(rrect: RRect.fromLTRBR(200 - 16, 100, 200, 100 + 16, badgeRadius).shift(offset)), ); await tester.pumpWidget(buildFrame(Alignment.bottomLeft, offset)); expect( box, paints..rrect(rrect: RRect.fromLTRBR(0, 200, 16, 200 + 16, badgeRadius).shift(offset)), ); await tester.pumpWidget(buildFrame(Alignment.bottomCenter, offset)); expect( box, paints ..rrect(rrect: RRect.fromLTRBR(100 - 8, 200, 100 + 8, 200 + 16, badgeRadius).shift(offset)), ); await tester.pumpWidget(buildFrame(Alignment.bottomRight, offset)); expect( box, paints ..rrect(rrect: RRect.fromLTRBR(200 - 16, 200, 200, 200 + 16, badgeRadius).shift(offset)), ); }); testWidgets('Small Badge alignment', (WidgetTester tester) async { const badgeRadius = Radius.circular(3); Widget buildFrame(Alignment alignment, [Offset offset = Offset.zero]) { return MaterialApp( 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, 6, 100 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.centerRight)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 100, 200, 100 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomLeft)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 200, 6, 200 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomCenter)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 3, 200, 100 + 3, 200 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomRight)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 200, 200, 200 + 6, badgeRadius))); const 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, 6, 100 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.centerRight, offset)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 100, 200, 100 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomLeft, offset)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, 200, 6, 200 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomCenter, offset)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(100 - 3, 200, 100 + 3, 200 + 6, badgeRadius))); await tester.pumpWidget(buildFrame(Alignment.bottomRight, offset)); expect(box, paints..rrect(rrect: RRect.fromLTRBR(200 - 6, 200, 200, 200 + 6, badgeRadius))); }); testWidgets('Badge Larger than large size', (WidgetTester tester) async { const badgeRadius = Radius.circular(15); Widget buildFrame(Alignment alignment, [Offset offset = Offset.zero]) { return MaterialApp( home: Align( alignment: Alignment.topLeft, child: Badge( // LargeSize = 16, make content of badge bigger than the default. label: Container(width: 30, height: 30, 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)); // Badge should scale with content expect(box, paints..rrect(rrect: RRect.fromLTRBR(0, -7, 30 + 8, 23, badgeRadius))); }); testWidgets('Badge renders at zero area', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: Center( child: SizedBox.shrink(child: Badge(label: Text('X'))), ), ), ); final Finder label = find.text('X'); expect(tester.getSize(label), Size.zero); }); testWidgets('Badge.count maxCount limits displayed value', (WidgetTester tester) async { Widget buildFrame(int count, [int maxCount = 999]) { return MaterialApp( home: Align( alignment: Alignment.topLeft, child: Badge.count(count: count, maxCount: maxCount, child: const Icon(Icons.add)), ), ); } await tester.pumpWidget(buildFrame(5, 99)); expect(find.text('5'), findsOneWidget); await tester.pumpWidget(buildFrame(99, 99)); expect(find.text('99'), findsOneWidget); await tester.pumpWidget(buildFrame(100, 99)); expect(find.text('99+'), findsOneWidget); await tester.pumpWidget(buildFrame(999)); expect(find.text('999'), findsOneWidget); await tester.pumpWidget(buildFrame(1000)); expect(find.text('999+'), findsOneWidget); // Test default maxCount (999) await tester.pumpWidget(buildFrame(1001)); expect(find.text('999+'), findsOneWidget); }); testWidgets('Badge.count asserts on negative count', (WidgetTester tester) async { Widget buildFrame(int count, [int maxCount = 999]) { return MaterialApp( home: Align( alignment: Alignment.topLeft, child: Badge.count(count: count, maxCount: maxCount, child: const Icon(Icons.add)), ), ); } expect(() => buildFrame(-1), throwsAssertionError); }); testWidgets('Badge.count asserts on non-positive maxCount', (WidgetTester tester) async { Widget buildFrame(int count, [int maxCount = 999]) { return MaterialApp( home: Align( alignment: Alignment.topLeft, child: Badge.count(count: count, maxCount: maxCount, child: const Icon(Icons.add)), ), ); } expect(() => buildFrame(5, 0), throwsAssertionError); }); testWidgets('Badge.count displays "0" when count is zero', (WidgetTester tester) async { Widget buildFrame(int count, [int maxCount = 999]) { return MaterialApp( home: Align( alignment: Alignment.topLeft, child: Badge.count(count: count, maxCount: maxCount, child: const Icon(Icons.add)), ), ); } await tester.pumpWidget(buildFrame(0, 5)); expect(find.text('0'), findsOneWidget); }); }