Unverified Commit 649877d2 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Update `chip_test.dart` tests for Material 3 (#140964)

Updated unit tests for `Tooltip` to have M2 and M3 versions.

More info in #139076
parent 2e6aac6e
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
@Tags(<String>['reduced-test-set']) @Tags(<String>['reduced-test-set'])
library; library;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -76,11 +77,10 @@ Widget wrapForChip({ ...@@ -76,11 +77,10 @@ Widget wrapForChip({
required Widget child, required Widget child,
TextDirection textDirection = TextDirection.ltr, TextDirection textDirection = TextDirection.ltr,
double textScaleFactor = 1.0, double textScaleFactor = 1.0,
Brightness brightness = Brightness.light, ThemeData? theme,
bool? useMaterial3,
}) { }) {
return MaterialApp( return MaterialApp(
theme: ThemeData(brightness: brightness, useMaterial3: useMaterial3), theme: theme,
home: Directionality( home: Directionality(
textDirection: textDirection, textDirection: textDirection,
child: MediaQuery( child: MediaQuery(
...@@ -145,11 +145,11 @@ Widget chipWithOptionalDeleteButton({ ...@@ -145,11 +145,11 @@ Widget chipWithOptionalDeleteButton({
String? chipTooltip, String? chipTooltip,
String? deleteButtonTooltipMessage, String? deleteButtonTooltipMessage,
VoidCallback? onPressed = doNothing, VoidCallback? onPressed = doNothing,
bool? useMaterial3, ThemeData? themeData,
}) { }) {
return wrapForChip( return wrapForChip(
textDirection: textDirection, textDirection: textDirection,
useMaterial3: useMaterial3, theme: themeData,
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
RawChip( RawChip(
...@@ -298,8 +298,8 @@ void main() { ...@@ -298,8 +298,8 @@ void main() {
testWidgets('M3 Chip defaults', (WidgetTester tester) async { testWidgets('M3 Chip defaults', (WidgetTester tester) async {
late TextTheme textTheme; late TextTheme textTheme;
final ThemeData lightTheme = ThemeData.light(useMaterial3: true); final ThemeData lightTheme = ThemeData.light();
final ThemeData darkTheme = ThemeData.dark(useMaterial3: true); final ThemeData darkTheme = ThemeData.dark();
Widget buildFrame(ThemeData theme) { Widget buildFrame(ThemeData theme) {
return MaterialApp( return MaterialApp(
...@@ -579,11 +579,11 @@ void main() { ...@@ -579,11 +579,11 @@ void main() {
}, },
); );
testWidgets('Chip in row works ok', (WidgetTester tester) async { testWidgets('Material2 - Chip in row works ok', (WidgetTester tester) async {
const TextStyle style = TextStyle(fontSize: 10.0); const TextStyle style = TextStyle(fontSize: 10.0);
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: const Row( child: const Row(
children: <Widget>[ children: <Widget>[
Chip(label: Text('Test'), labelStyle: style), Chip(label: Text('Test'), labelStyle: style),
...@@ -617,28 +617,93 @@ void main() { ...@@ -617,28 +617,93 @@ void main() {
expect(tester.getSize(find.byType(Chip)), const Size(800.0, 48.0)); expect(tester.getSize(find.byType(Chip)), const Size(800.0, 48.0));
}); });
testWidgets('Chip responds to materialTapTargetSize', (WidgetTester tester) async { testWidgets('Material3 - Chip in row works ok', (WidgetTester tester) async {
await tester.pumpWidget( const TextStyle style = TextStyle(fontSize: 10.0);
wrapForChip( await tester.pumpWidget(
useMaterial3: false, wrapForChip(
child: const Column( child: const Row(
children: <Widget>[ children: <Widget>[
Chip( Chip(label: Text('Test'), labelStyle: style),
label: Text('X'), ],
materialTapTargetSize: MaterialTapTargetSize.padded,
),
Chip(
label: Text('X'),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
), ),
); ),
expect(tester.getSize(find.byType(Chip).first), const Size(48.0, 48.0)); );
expect(tester.getSize(find.byType(Chip).last), const Size(38.0, 32.0)); expect(tester.getSize(find.byType(Text)).width, closeTo(40.4, 0.01));
}, expect(tester.getSize(find.byType(Text)).height, equals(14.0));
); expect(tester.getSize(find.byType(Chip)).width, closeTo(74.4, 0.01));
expect(tester.getSize(find.byType(Chip)).height, equals(48.0));
await tester.pumpWidget(
wrapForChip(
child: const Row(
children: <Widget>[
Flexible(child: Chip(label: Text('Test'), labelStyle: style)),
],
),
),
);
expect(tester.getSize(find.byType(Text)).width, closeTo(40.4, 0.01));
expect(tester.getSize(find.byType(Text)).height, equals(14.0));
expect(tester.getSize(find.byType(Chip)).width, closeTo(74.4, 0.01));
expect(tester.getSize(find.byType(Chip)).height, equals(48.0));
await tester.pumpWidget(
wrapForChip(
child: const Row(
children: <Widget>[
Expanded(child: Chip(label: Text('Test'), labelStyle: style)),
],
),
),
);
expect(tester.getSize(find.byType(Text)).width, closeTo(40.4, 0.01));
expect(tester.getSize(find.byType(Text)).height, equals(14.0));
expect(tester.getSize(find.byType(Chip)), const Size(800.0, 48.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Material2 - Chip responds to materialTapTargetSize', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
theme: ThemeData(useMaterial3: false),
child: const Column(
children: <Widget>[
Chip(
label: Text('X'),
materialTapTargetSize: MaterialTapTargetSize.padded,
),
Chip(
label: Text('X'),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
),
);
expect(tester.getSize(find.byType(Chip).first), const Size(48.0, 48.0));
expect(tester.getSize(find.byType(Chip).last), const Size(38.0, 32.0));
});
testWidgets('Material3 - Chip responds to materialTapTargetSize', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
child: const Column(
children: <Widget>[
Chip(
label: Text('X'),
materialTapTargetSize: MaterialTapTargetSize.padded,
),
Chip(
label: Text('X'),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
),
);
expect(tester.getSize(find.byType(Chip).first).width, closeTo(48.1, 0.01));
expect(tester.getSize(find.byType(Chip).first).height, equals(48.0));
expect(tester.getSize(find.byType(Chip).last).width, closeTo(48.1, 0.01));
expect(tester.getSize(find.byType(Chip).last).height, equals(38.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Delete button tap target is the right proportion of the chip', (WidgetTester tester) async { testWidgets('Delete button tap target is the right proportion of the chip', (WidgetTester tester) async {
final UniqueKey deleteKey = UniqueKey(); final UniqueKey deleteKey = UniqueKey();
...@@ -739,10 +804,10 @@ void main() { ...@@ -739,10 +804,10 @@ void main() {
expect(tester.getCenter(find.text('ABC')).dx, lessThan(tester.getCenter(find.byKey(iconKey)).dx)); expect(tester.getCenter(find.text('ABC')).dx, lessThan(tester.getCenter(find.byKey(iconKey)).dx));
}); });
testWidgets('Chip responds to textScaleFactor', (WidgetTester tester) async { testWidgets('Material2 - Chip responds to textScaleFactor', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: const Column( child: const Column(
children: <Widget>[ children: <Widget>[
Chip( Chip(
...@@ -816,12 +881,90 @@ void main() { ...@@ -816,12 +881,90 @@ void main() {
expect(tester.getSize(find.byType(Chip).last), const Size(132.0, 48.0)); expect(tester.getSize(find.byType(Chip).last), const Size(132.0, 48.0));
}); });
testWidgets('Labels can be non-text widgets', (WidgetTester tester) async { testWidgets('Material3 - Chip responds to textScaleFactor', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
child: const Column(
children: <Widget>[
Chip(
avatar: CircleAvatar(child: Text('A')),
label: Text('Chip A'),
),
Chip(
avatar: CircleAvatar(child: Text('B')),
label: Text('Chip B'),
),
],
),
),
);
expect(tester.getSize(find.text('Chip A')).width, closeTo(84.5, 0.1));
expect(tester.getSize(find.text('Chip A')).height, equals(20.0));
expect(tester.getSize(find.text('Chip B')).width, closeTo(84.5, 0.1));
expect(tester.getSize(find.text('Chip B')).height, equals(20.0));
await tester.pumpWidget(
wrapForChip(
textScaleFactor: 3.0,
child: const Column(
children: <Widget>[
Chip(
avatar: CircleAvatar(child: Text('A')),
label: Text('Chip A'),
),
Chip(
avatar: CircleAvatar(child: Text('B')),
label: Text('Chip B'),
),
],
),
),
);
expect(tester.getSize(find.text('Chip A')).width, closeTo(252.6, 0.1));
expect(tester.getSize(find.text('Chip A')).height, equals(60.0));
expect(tester.getSize(find.text('Chip B')).width, closeTo(252.6, 0.1));
expect(tester.getSize(find.text('Chip B')).height, equals(60.0));
expect(tester.getSize(find.byType(Chip).first).width, closeTo(338.6, 0.1));
expect(tester.getSize(find.byType(Chip).first).height, equals(78.0));
expect(tester.getSize(find.byType(Chip).last).width, closeTo(338.6, 0.1));
expect(tester.getSize(find.byType(Chip).last).height, equals(78.0));
// Check that individual text scales are taken into account.
await tester.pumpWidget(
wrapForChip(
child: const Column(
children: <Widget>[
Chip(
avatar: CircleAvatar(child: Text('A')),
label: Text('Chip A', textScaleFactor: 3.0),
),
Chip(
avatar: CircleAvatar(child: Text('B')),
label: Text('Chip B'),
),
],
),
),
);
expect(tester.getSize(find.text('Chip A')).width, closeTo(252.6, 0.01));
expect(tester.getSize(find.text('Chip A')).height, equals(60.0));
expect(tester.getSize(find.text('Chip B')).width, closeTo(84.59, 0.01));
expect(tester.getSize(find.text('Chip B')).height, equals(20.0));
expect(tester.getSize(find.byType(Chip).first).width, closeTo(346.6, 0.01));
expect(tester.getSize(find.byType(Chip).first).height, equals(78.0));
expect(tester.getSize(find.byType(Chip).last).width, closeTo(138.59, 0.01));
expect(tester.getSize(find.byType(Chip).last).height, equals(48.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Material2 - Labels can be non-text widgets', (WidgetTester tester) async {
final Key keyA = GlobalKey(); final Key keyA = GlobalKey();
final Key keyB = GlobalKey(); final Key keyB = GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Chip( Chip(
...@@ -843,6 +986,34 @@ void main() { ...@@ -843,6 +986,34 @@ void main() {
expect(tester.getSize(find.byType(Chip).last), const Size(58.0, 48.0)); expect(tester.getSize(find.byType(Chip).last), const Size(58.0, 48.0));
}); });
testWidgets('Material3 - Labels can be non-text widgets', (WidgetTester tester) async {
final Key keyA = GlobalKey();
final Key keyB = GlobalKey();
await tester.pumpWidget(
wrapForChip(
child: Column(
children: <Widget>[
Chip(
avatar: const CircleAvatar(child: Text('A')),
label: Text('Chip A', key: keyA),
),
Chip(
avatar: const CircleAvatar(child: Text('B')),
label: SizedBox(key: keyB, width: 10.0, height: 10.0),
),
],
),
),
);
expect(tester.getSize(find.byKey(keyA)).width, moreOrLessEquals(84.5, epsilon: 0.1));
expect(tester.getSize(find.byKey(keyA)).height, equals(20.0));
expect(tester.getSize(find.byKey(keyB)), const Size(10.0, 10.0));
expect(tester.getSize(find.byType(Chip).first).width, moreOrLessEquals(138.5, epsilon: 0.1));
expect(tester.getSize(find.byType(Chip).first).height, equals(48.0));
expect(tester.getSize(find.byType(Chip).last), const Size(60.0, 48.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Avatars can be non-circle avatar widgets', (WidgetTester tester) async { testWidgets('Avatars can be non-circle avatar widgets', (WidgetTester tester) async {
final Key keyA = GlobalKey(); final Key keyA = GlobalKey();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -961,12 +1132,12 @@ void main() { ...@@ -961,12 +1132,12 @@ void main() {
expect(tester.getBottomRight(find.byType(Icon)), const Offset(361.0, 309.0)); expect(tester.getBottomRight(find.byType(Icon)), const Offset(361.0, 309.0));
}); });
testWidgets('Avatar drawer works as expected on RawChip', (WidgetTester tester) async { testWidgets('Material2 - Avatar drawer works as expected on RawChip', (WidgetTester tester) async {
final GlobalKey labelKey = GlobalKey(); final GlobalKey labelKey = GlobalKey();
Future<void> pushChip({ Widget? avatar }) async { Future<void> pushChip({ Widget? avatar }) async {
return tester.pumpWidget( return tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
RawChip( RawChip(
...@@ -1003,24 +1174,28 @@ void main() { ...@@ -1003,24 +1174,28 @@ void main() {
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
// Avatar drawer should start expanding. // Avatar drawer should start expanding.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(81.2, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(81.2, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-18.8, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-18.8, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(13.2, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(13.2, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(86.7, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(86.7, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-13.3, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-13.3, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(18.6, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(18.6, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(94.7, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(94.7, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-5.3, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-5.3, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(26.7, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(26.7, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(99.5, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(99.5, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-0.5, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-0.5, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(31.5, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(31.5, epsilon: 0.1));
...@@ -1029,6 +1204,7 @@ void main() { ...@@ -1029,6 +1204,7 @@ void main() {
// height. // height.
await tester.pumpAndSettle(const Duration(milliseconds: 200)); await tester.pumpAndSettle(const Duration(milliseconds: 200));
expect(tester.getSize(find.byType(RawChip)), equals(const Size(104.0, 48.0))); expect(tester.getSize(find.byType(RawChip)), equals(const Size(104.0, 48.0)));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)), equals(const Offset(4.0, 12.0))); expect(tester.getTopLeft(find.byKey(avatarKey)), equals(const Offset(4.0, 12.0)));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(36.0, 17.0))); expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(36.0, 17.0)));
...@@ -1044,24 +1220,28 @@ void main() { ...@@ -1044,24 +1220,28 @@ void main() {
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
// Avatar drawer should start contracting. // Avatar drawer should start contracting.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(102.9, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(102.9, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(2.9, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(2.9, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(34.9, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(34.9, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(98.0, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(98.0, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-2.0, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-2.0, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(30.0, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(30.0, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(84.1, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(84.1, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-15.9, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-15.9, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(16.1, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(16.1, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(80.0, epsilon: 0.1)); expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(80.0, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0))); expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(24.0, 24.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-20.0, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-20.0, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(12.0, epsilon: 0.1)); expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(12.0, epsilon: 0.1));
...@@ -1074,14 +1254,138 @@ void main() { ...@@ -1074,14 +1254,138 @@ void main() {
expect(find.byKey(avatarKey), findsNothing); expect(find.byKey(avatarKey), findsNothing);
}); });
testWidgets('Delete button drawer works as expected on RawChip', (WidgetTester tester) async { testWidgets('Material3 - Avatar drawer works as expected on RawChip', (WidgetTester tester) async {
final GlobalKey labelKey = GlobalKey();
Future<void> pushChip({ Widget? avatar }) async {
return tester.pumpWidget(
wrapForChip(
child: Wrap(
children: <Widget>[
RawChip(
avatar: avatar,
label: Text('Chip', key: labelKey),
shape: const StadiumBorder(),
),
],
),
),
);
}
// No avatar
await pushChip();
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.1));
final GlobalKey avatarKey = GlobalKey();
// Add an avatar
await pushChip(
avatar: Container(
key: avatarKey,
color: const Color(0xff000000),
width: 40.0,
height: 40.0,
),
);
// Avatar drawer should start out closed.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)), equals(const Offset(-11.0, 14.0)));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
await tester.pump(const Duration(milliseconds: 20));
// Avatar drawer should start expanding.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(91.3, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-10, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(17.9, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(95.9, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-5.4, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(22.5, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(102.6, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(1.2, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(29.2, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(106.6, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(5.2, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(33.2, epsilon: 0.1));
// Wait for being done with animation, and make sure it didn't change
// height.
await tester.pumpAndSettle(const Duration(milliseconds: 200));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(110.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)), equals(const Offset(9.0, 14.0)));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(37.0, 14.0)));
// Remove the avatar again
await pushChip();
// Avatar drawer should start out open.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(110.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)), equals(const Offset(9.0, 14.0)));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(37.0, 14.0)));
await tester.pump(const Duration(milliseconds: 20));
// Avatar drawer should start contracting.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(109.5, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(8.1, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(36.1, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(105.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(4.0, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(32.0, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(93.7, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-7.6, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(20.3, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(avatarKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-11.0, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(17.0, epsilon: 0.1));
// Wait for being done with animation, make sure it didn't change
// height, and make sure that the avatar is no longer drawn.
await tester.pumpAndSettle(const Duration(milliseconds: 200));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
expect(find.byKey(avatarKey), findsNothing);
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Material2 - Delete button drawer works as expected on RawChip', (WidgetTester tester) async {
const Key labelKey = Key('label'); const Key labelKey = Key('label');
const Key deleteButtonKey = Key('delete'); const Key deleteButtonKey = Key('delete');
bool wasDeleted = false; bool wasDeleted = false;
Future<void> pushChip({ bool deletable = false }) async { Future<void> pushChip({ bool deletable = false }) async {
return tester.pumpWidget( return tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) { StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
...@@ -1191,17 +1495,147 @@ void main() { ...@@ -1191,17 +1495,147 @@ void main() {
expect(find.byKey(deleteButtonKey), findsNothing); expect(find.byKey(deleteButtonKey), findsNothing);
}); });
testWidgets('Delete button takes up at most half of the chip', (WidgetTester tester) async { testWidgets('Material3 - Delete button drawer works as expected on RawChip', (WidgetTester tester) async {
final UniqueKey chipKey = UniqueKey(); const Key labelKey = Key('label');
bool chipPressed = false; const Key deleteButtonKey = Key('delete');
bool deletePressed = false; bool wasDeleted = false;
Future<void> pushChip({ bool deletable = false }) async {
return tester.pumpWidget(
wrapForChip(
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return RawChip(
onDeleted: deletable
? () {
setState(() {
wasDeleted = true;
});
}
: null,
deleteIcon: Container(width: 40.0, height: 40.0, color: Colors.blue, key: deleteButtonKey),
label: const Text('Chip', key: labelKey),
shape: const StadiumBorder(),
);
}),
],
),
),
);
}
await tester.pumpWidget( // No delete button
wrapForChip( await pushChip();
child: Wrap( expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.01));
children: <Widget>[ expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
RawChip(
key: chipKey, // Add a delete button
await pushChip(deletable: true);
// Delete button drawer should start out closed.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.01));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(
find.byKey(deleteButtonKey)),
offsetMoreOrLessEquals(const Offset(61.4, 14.0), epsilon: 0.01),
);
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
await tester.pump(const Duration(milliseconds: 20));
// Delete button drawer should start expanding.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(91.3, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(62.3, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(95.9, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(66.9, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(102.6, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(73.6, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(106.6, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(77.6, epsilon: 0.1));
// Wait for being done with animation, and make sure it didn't change
// height.
await tester.pumpAndSettle(const Duration(milliseconds: 200));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(110.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(
tester.getTopLeft(find.byKey(deleteButtonKey)),
offsetMoreOrLessEquals(const Offset(81.4, 14.0), epsilon: 0.01),
);
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
// Test the tap work for the delete button, but not the rest of the chip.
expect(wasDeleted, isFalse);
await tester.tap(find.byKey(labelKey));
expect(wasDeleted, isFalse);
await tester.tap(find.byKey(deleteButtonKey));
expect(wasDeleted, isTrue);
// Remove the delete button again
await pushChip();
// Delete button drawer should start out open.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(110.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(
tester.getTopLeft(find.byKey(deleteButtonKey)),
offsetMoreOrLessEquals(const Offset(81.4, 14.0), epsilon: 0.01),
);
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
await tester.pump(const Duration(milliseconds: 20));
// Delete button drawer should start contracting.
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(110.1, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(81.1, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(109.4, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(80.4, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(107.9, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(78.9, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 20));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(104.9, epsilon: 0.1));
expect(tester.getSize(find.byKey(deleteButtonKey)), equals(const Size(20.0, 20.0)));
expect(tester.getTopLeft(find.byKey(deleteButtonKey)).dx, moreOrLessEquals(75.9, epsilon: 0.1));
// Wait for being done with animation, make sure it didn't change
// height, and make sure that the delete button is no longer drawn.
await tester.pumpAndSettle(const Duration(milliseconds: 200));
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.1));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
expect(tester.getTopLeft(find.byKey(labelKey)), equals(const Offset(17.0, 14.0)));
expect(find.byKey(deleteButtonKey), findsNothing);
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Delete button takes up at most half of the chip', (WidgetTester tester) async {
final UniqueKey chipKey = UniqueKey();
bool chipPressed = false;
bool deletePressed = false;
await tester.pumpWidget(
wrapForChip(
child: Wrap(
children: <Widget>[
RawChip(
key: chipKey,
onPressed: () { onPressed: () {
chipPressed = true; chipPressed = true;
}, },
...@@ -1209,7 +1643,7 @@ void main() { ...@@ -1209,7 +1643,7 @@ void main() {
deletePressed = true; deletePressed = true;
}, },
label: const Text(''), label: const Text(''),
), ),
], ],
), ),
), ),
...@@ -1227,13 +1661,13 @@ void main() { ...@@ -1227,13 +1661,13 @@ void main() {
expect(deletePressed, isTrue); expect(deletePressed, isTrue);
}); });
testWidgets('Chip creates centered, unique ripple when label is tapped', (WidgetTester tester) async { testWidgets('Material2 - Chip creates centered, unique ripple when label is tapped', (WidgetTester tester) async {
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
final UniqueKey deleteButtonKey = UniqueKey(); final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
chipWithOptionalDeleteButton( chipWithOptionalDeleteButton(
useMaterial3: false, themeData: ThemeData(useMaterial3: false),
labelKey: labelKey, labelKey: labelKey,
deleteButtonKey: deleteButtonKey, deleteButtonKey: deleteButtonKey,
deletable: true, deletable: true,
...@@ -1277,6 +1711,51 @@ void main() { ...@@ -1277,6 +1711,51 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('Material3 - Chip creates centered, unique sparkle when label is tapped', (WidgetTester tester) async {
final UniqueKey labelKey = UniqueKey();
final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget(
chipWithOptionalDeleteButton(
labelKey: labelKey,
deleteButtonKey: deleteButtonKey,
deletable: true,
),
);
// Taps at a location close to the center of the label.
final Offset centerOfLabel = tester.getCenter(find.byKey(labelKey));
final Offset tapLocationOfLabel = centerOfLabel + const Offset(-10, -10);
final TestGesture gesture = await tester.startGesture(tapLocationOfLabel);
await tester.pump();
// Waits for 100 ms.
await tester.pump(const Duration(milliseconds: 100));
// There should be one unique, centered ink sparkle.
await expectLater(find.byType(RawChip), matchesGoldenFile('chip.label_tapped.ink_sparkle.0.png'));
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for 100 ms again.
await tester.pump(const Duration(milliseconds: 100));
// The sparkle should grow, with the same center.
await expectLater(find.byType(RawChip), matchesGoldenFile('chip.label_tapped.ink_sparkle.1.png'));
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for a very long time.
await tester.pumpAndSettle();
// There should still be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
await gesture.up();
});
testWidgets('Delete button is focusable', (WidgetTester tester) async { testWidgets('Delete button is focusable', (WidgetTester tester) async {
final GlobalKey labelKey = GlobalKey(); final GlobalKey labelKey = GlobalKey();
final GlobalKey deleteButtonKey = GlobalKey(); final GlobalKey deleteButtonKey = GlobalKey();
...@@ -1310,13 +1789,13 @@ void main() { ...@@ -1310,13 +1789,13 @@ void main() {
expect(Focus.of(labelKey.currentContext!).hasPrimaryFocus, isTrue); expect(Focus.of(labelKey.currentContext!).hasPrimaryFocus, isTrue);
}); });
testWidgets('Delete button creates non-centered, unique ripple when tapped', (WidgetTester tester) async { testWidgets('Material2 - Delete button creates non-centered, unique ripple when tapped', (WidgetTester tester) async {
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
final UniqueKey deleteButtonKey = UniqueKey(); final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
chipWithOptionalDeleteButton( chipWithOptionalDeleteButton(
useMaterial3: false, themeData: ThemeData(useMaterial3: false),
labelKey: labelKey, labelKey: labelKey,
deleteButtonKey: deleteButtonKey, deleteButtonKey: deleteButtonKey,
deletable: true, deletable: true,
...@@ -1364,13 +1843,62 @@ void main() { ...@@ -1364,13 +1843,62 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('Delete button in a chip with null onPressed creates ripple when tapped', (WidgetTester tester) async { testWidgets('Material3 - Delete button creates non-centered, unique sparkle when tapped', (WidgetTester tester) async {
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
final UniqueKey deleteButtonKey = UniqueKey(); final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
chipWithOptionalDeleteButton( chipWithOptionalDeleteButton(
useMaterial3: false, labelKey: labelKey,
deleteButtonKey: deleteButtonKey,
deletable: true,
),
);
// Taps at a location close to the center of the delete icon.
final Offset centerOfDeleteButton = tester.getCenter(find.byKey(deleteButtonKey));
final Offset tapLocationOfDeleteButton = centerOfDeleteButton + const Offset(-10, -10);
final TestGesture gesture = await tester.startGesture(tapLocationOfDeleteButton);
await tester.pump();
// Waits for 200 ms.
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 100));
// There should be one unique ink sparkle.
await expectLater(find.byType(RawChip), matchesGoldenFile('chip.delete_button_tapped.ink_sparkle.0.png'));
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for 200 ms again.
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 100));
// The sparkle should grow, but the center should move,
// towards the center of the delete icon.
await expectLater(find.byType(RawChip), matchesGoldenFile('chip.delete_button_tapped.ink_sparkle.1.png'));
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for a very long time.
// This is pressing and holding the delete button.
await tester.pumpAndSettle();
// There should be a tooltip.
expect(findTooltipContainer('Delete'), findsOneWidget);
await gesture.up();
});
testWidgets('Material2 - Delete button in a chip with null onPressed creates ripple when tapped', (WidgetTester tester) async {
final UniqueKey labelKey = UniqueKey();
final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget(
chipWithOptionalDeleteButton(
themeData: ThemeData(useMaterial3: false),
labelKey: labelKey, labelKey: labelKey,
onPressed: null, onPressed: null,
deleteButtonKey: deleteButtonKey, deleteButtonKey: deleteButtonKey,
...@@ -1419,6 +1947,62 @@ void main() { ...@@ -1419,6 +1947,62 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('Material3 - Delete button in a chip with null onPressed creates sparkle when tapped', (WidgetTester tester) async {
final UniqueKey labelKey = UniqueKey();
final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget(
chipWithOptionalDeleteButton(
labelKey: labelKey,
onPressed: null,
deleteButtonKey: deleteButtonKey,
deletable: true,
),
);
// Taps at a location close to the center of the delete icon.
final Offset centerOfDeleteButton = tester.getCenter(find.byKey(deleteButtonKey));
final Offset tapLocationOfDeleteButton = centerOfDeleteButton + const Offset(-10, -10);
final TestGesture gesture = await tester.startGesture(tapLocationOfDeleteButton);
await tester.pump();
// Waits for 200 ms.
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 100));
// There should be one unique ink sparkle.
await expectLater(
find.byType(RawChip),
matchesGoldenFile('chip.delete_button_tapped.disabled.ink_sparkle.0.png'),
);
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for 200 ms again.
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 100));
// The sparkle should grow, but the center should move,
// towards the center of the delete icon.
await expectLater(
find.byType(RawChip),
matchesGoldenFile('chip.delete_button_tapped.disabled.ink_sparkle.1.png'),
);
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for a very long time.
// This is pressing and holding the delete button.
await tester.pumpAndSettle();
// There should be a tooltip.
expect(findTooltipContainer('Delete'), findsOneWidget);
await gesture.up();
});
testWidgets('RTL delete button responds to tap on the left of the chip', (WidgetTester tester) async { testWidgets('RTL delete button responds to tap on the left of the chip', (WidgetTester tester) async {
// Creates an RTL chip with a delete button. // Creates an RTL chip with a delete button.
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
...@@ -1449,13 +2033,13 @@ void main() { ...@@ -1449,13 +2033,13 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('Chip without delete button creates correct ripple', (WidgetTester tester) async { testWidgets('Material2 - Chip without delete button creates correct ripple', (WidgetTester tester) async {
// Creates a chip with a delete button. // Creates a chip with a delete button.
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
chipWithOptionalDeleteButton( chipWithOptionalDeleteButton(
useMaterial3: false, themeData: ThemeData(useMaterial3: false),
labelKey: labelKey, labelKey: labelKey,
deletable: false, deletable: false,
), ),
...@@ -1504,13 +2088,69 @@ void main() { ...@@ -1504,13 +2088,69 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('Selection with avatar works as expected on RawChip', (WidgetTester tester) async { testWidgets('Material3 - Chip without delete button creates correct sparkle', (WidgetTester tester) async {
// Creates a chip with a delete button.
final UniqueKey labelKey = UniqueKey();
await tester.pumpWidget(
chipWithOptionalDeleteButton(
labelKey: labelKey,
deletable: false,
),
);
// Taps at a location close to the bottom-right corner of the chip.
final Offset bottomRightOfInkWell = tester.getBottomRight(find.byType(InkWell));
final Offset tapLocation = bottomRightOfInkWell + const Offset(-10, -10);
final TestGesture gesture = await tester.startGesture(tapLocation);
await tester.pump();
// Waits for 100 ms.
await tester.pump(const Duration(milliseconds: 100));
// There should be exactly one ink-creating widget.
expect(find.byType(InkWell), findsOneWidget);
expect(find.byType(InkResponse), findsNothing);
// There should be one unique, centered ink sparkle.
await expectLater(
find.byType(RawChip),
matchesGoldenFile('chip.without_delete_button.ink_sparkle.0.png'),
);
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for 100 ms again.
await tester.pump(const Duration(milliseconds: 100));
// The sparkle should grow, with the same center.
// This indicates that the tap is not on a delete icon.
await expectLater(
find.byType(RawChip),
matchesGoldenFile('chip.without_delete_button.ink_sparkle.1.png'),
);
// There should be no tooltip.
expect(findTooltipContainer('Delete'), findsNothing);
// Waits for a very long time.
await tester.pumpAndSettle();
// There should still be no tooltip.
// This indicates that the tap is not on a delete icon.
expect(findTooltipContainer('Delete'), findsNothing);
await gesture.up();
});
testWidgets('Material2 - Selection with avatar works as expected on RawChip', (WidgetTester tester) async {
bool selected = false; bool selected = false;
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ Widget? avatar, bool selectable = false }) async { Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
return tester.pumpWidget( return tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) { StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
...@@ -1548,6 +2188,8 @@ void main() { ...@@ -1548,6 +2188,8 @@ void main() {
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
// Simulate a tap on the label to select the chip. // Simulate a tap on the label to select the chip.
await tester.tap(find.byKey(labelKey)); await tester.tap(find.byKey(labelKey));
expect(selected, equals(true)); expect(selected, equals(true));
...@@ -1566,6 +2208,7 @@ void main() { ...@@ -1566,6 +2208,7 @@ void main() {
expect(getAvatarDrawerProgress(tester), equals(1.0)); expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0)); expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Simulate another tap on the label to deselect the chip. // Simulate another tap on the label to deselect the chip.
await tester.tap(find.byKey(labelKey)); await tester.tap(find.byKey(labelKey));
expect(selected, equals(false)); expect(selected, equals(false));
...@@ -1585,17 +2228,17 @@ void main() { ...@@ -1585,17 +2228,17 @@ void main() {
expect(getDeleteDrawerProgress(tester), equals(0.0)); expect(getDeleteDrawerProgress(tester), equals(0.0));
}); });
testWidgets('Selection without avatar works as expected on RawChip', (WidgetTester tester) async { testWidgets('Material3 - Selection with avatar works as expected on RawChip', (WidgetTester tester) async {
bool selected = false; bool selected = false;
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ bool selectable = false }) async { Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
return tester.pumpWidget( return tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false,
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) { StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return RawChip( return RawChip(
avatar: avatar,
onSelected: selectable onSelected: selectable
? (bool value) { ? (bool value) {
setState(() { setState(() {
...@@ -1614,11 +2257,94 @@ void main() { ...@@ -1614,11 +2257,94 @@ void main() {
); );
} }
// Without avatar, but not selectable. // With avatar, but not selectable.
await pushChip(); final UniqueKey avatarKey = UniqueKey();
expect(tester.getSize(find.byType(RawChip)), equals(const Size(234.0, 48.0))); await pushChip(
avatar: SizedBox(width: 40.0, height: 40.0, key: avatarKey),
// Turn on selection. );
expect(tester.getSize(find.byType(RawChip)), equals(const Size(265.5, 48.0)));
// Turn on selection.
await pushChip(
avatar: SizedBox(width: 40.0, height: 40.0, key: avatarKey),
selectable: true,
);
await tester.pumpAndSettle();
expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
// Simulate a tap on the label to select the chip.
await tester.tap(find.byKey(labelKey));
expect(selected, equals(true));
expect(SchedulerBinding.instance.transientCallbackCount, equals(kIsWeb && isCanvasKit ? 3 : 1));
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));
expect(getSelectProgress(tester), moreOrLessEquals(0.002, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 50));
expect(getSelectProgress(tester), moreOrLessEquals(0.54, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 100));
expect(getSelectProgress(tester), equals(1.0));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pumpAndSettle();
// Simulate another tap on the label to deselect the chip.
await tester.tap(find.byKey(labelKey));
expect(selected, equals(false));
expect(SchedulerBinding.instance.transientCallbackCount, equals(kIsWeb && isCanvasKit ? 3 : 1));
await tester.pump();
await tester.pump(const Duration(milliseconds: 20));
expect(getSelectProgress(tester), moreOrLessEquals(0.875, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 20));
expect(getSelectProgress(tester), moreOrLessEquals(0.13, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 100));
expect(getSelectProgress(tester), equals(0.0));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Material2 - Selection without avatar works as expected on RawChip', (WidgetTester tester) async {
bool selected = false;
final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ bool selectable = false }) async {
return tester.pumpWidget(
wrapForChip(
theme: ThemeData(useMaterial3: false),
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return RawChip(
onSelected: selectable
? (bool value) {
setState(() {
selected = value;
});
}
: null,
selected: selected,
label: Text('Long Chip Label', key: labelKey),
shape: const StadiumBorder(),
);
}),
],
),
),
);
}
// Without avatar, but not selectable.
await pushChip();
expect(tester.getSize(find.byType(RawChip)), equals(const Size(234.0, 48.0)));
// Turn on selection.
await pushChip(selectable: true); await pushChip(selectable: true);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -1639,7 +2365,9 @@ void main() { ...@@ -1639,7 +2365,9 @@ void main() {
expect(getSelectProgress(tester), equals(1.0)); expect(getSelectProgress(tester), equals(1.0));
expect(getAvatarDrawerProgress(tester), equals(1.0)); expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0)); expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Simulate another tap on the label to deselect the chip. // Simulate another tap on the label to deselect the chip.
await tester.tap(find.byKey(labelKey)); await tester.tap(find.byKey(labelKey));
expect(selected, equals(false)); expect(selected, equals(false));
...@@ -1659,13 +2387,90 @@ void main() { ...@@ -1659,13 +2387,90 @@ void main() {
expect(getDeleteDrawerProgress(tester), equals(0.0)); expect(getDeleteDrawerProgress(tester), equals(0.0));
}); });
testWidgets('Activation works as expected on RawChip', (WidgetTester tester) async { testWidgets('Material3 - Selection without avatar works as expected on RawChip', (WidgetTester tester) async {
bool selected = false;
final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ bool selectable = false }) async {
return tester.pumpWidget(
wrapForChip(
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return RawChip(
onSelected: selectable
? (bool value) {
setState(() {
selected = value;
});
}
: null,
selected: selected,
label: Text('Long Chip Label', key: labelKey),
shape: const StadiumBorder(),
);
}),
],
),
),
);
}
// Without avatar, but not selectable.
await pushChip();
expect(tester.getSize(find.byType(RawChip)), equals(const Size(245.5, 48.0)));
// Turn on selection.
await pushChip(selectable: true);
await tester.pumpAndSettle();
expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
// Simulate a tap on the label to select the chip.
await tester.tap(find.byKey(labelKey));
expect(selected, equals(true));
expect(SchedulerBinding.instance.transientCallbackCount, equals(kIsWeb && isCanvasKit ? 3 : 1));
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));
expect(getSelectProgress(tester), moreOrLessEquals(0.002, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), moreOrLessEquals(0.459, epsilon: 0.01));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 50));
expect(getSelectProgress(tester), moreOrLessEquals(0.54, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), moreOrLessEquals(0.92, epsilon: 0.01));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 100));
expect(getSelectProgress(tester), equals(1.0));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pumpAndSettle();
// Simulate another tap on the label to deselect the chip.
await tester.tap(find.byKey(labelKey));
expect(selected, equals(false));
expect(SchedulerBinding.instance.transientCallbackCount, equals(kIsWeb && isCanvasKit ? 3 : 1));
await tester.pump();
await tester.pump(const Duration(milliseconds: 20));
expect(getSelectProgress(tester), moreOrLessEquals(0.875, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), moreOrLessEquals(0.96, epsilon: 0.01));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 20));
expect(getSelectProgress(tester), moreOrLessEquals(0.13, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), moreOrLessEquals(0.75, epsilon: 0.01));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 100));
expect(getSelectProgress(tester), equals(0.0));
expect(getAvatarDrawerProgress(tester), equals(0.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Material2 - Activation works as expected on RawChip', (WidgetTester tester) async {
bool selected = false; bool selected = false;
final UniqueKey labelKey = UniqueKey(); final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ Widget? avatar, bool selectable = false }) async { Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
return tester.pumpWidget( return tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) { StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
...@@ -1716,26 +2521,80 @@ void main() { ...@@ -1716,26 +2521,80 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('Chip uses ThemeData chip theme if present', (WidgetTester tester) async { testWidgets('Material3 - Activation works as expected on RawChip', (WidgetTester tester) async {
final ThemeData theme = ThemeData( bool selected = false;
useMaterial3: false, final UniqueKey labelKey = UniqueKey();
platform: TargetPlatform.android, Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
primarySwatch: Colors.red, return tester.pumpWidget(
wrapForChip(
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return RawChip(
avatar: avatar,
onSelected: selectable
? (bool value) {
setState(() {
selected = value;
});
}
: null,
selected: selected,
label: Text('Long Chip Label', key: labelKey),
shape: const StadiumBorder(),
showCheckmark: false,
);
}),
],
),
),
);
}
final UniqueKey avatarKey = UniqueKey();
await pushChip(
avatar: SizedBox(width: 40.0, height: 40.0, key: avatarKey),
selectable: true,
); );
final ChipThemeData chipTheme = theme.chipTheme; await tester.pumpAndSettle();
expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
await tester.tap(find.byKey(labelKey));
expect(selected, equals(true));
expect(SchedulerBinding.instance.transientCallbackCount, equals(kIsWeb && isCanvasKit ? 3 : 1));
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));
expect(getSelectProgress(tester), moreOrLessEquals(0.002, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 50));
expect(getSelectProgress(tester), moreOrLessEquals(0.54, epsilon: 0.01));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pump(const Duration(milliseconds: 100));
expect(getSelectProgress(tester), equals(1.0));
expect(getAvatarDrawerProgress(tester), equals(1.0));
expect(getDeleteDrawerProgress(tester), equals(0.0));
await tester.pumpAndSettle();
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Chip uses ThemeData chip theme if present', (WidgetTester tester) async {
final ThemeData theme = ThemeData(chipTheme: const ChipThemeData(backgroundColor: Color(0xffff0000)));
Widget buildChip(ChipThemeData data) { Widget buildChip() {
return wrapForChip( return wrapForChip(
child: Theme( child: Theme(
data: theme, data: theme,
child: const InputChip( child: InputChip(
label: Text('Label'), label: const Text('Label'),
onPressed: () {},
), ),
), ),
); );
} }
await tester.pumpWidget(buildChip(chipTheme)); await tester.pumpWidget(buildChip());
final RenderBox materialBox = tester.firstRenderObject<RenderBox>( final RenderBox materialBox = tester.firstRenderObject<RenderBox>(
find.descendant( find.descendant(
...@@ -1744,7 +2603,7 @@ void main() { ...@@ -1744,7 +2603,7 @@ void main() {
), ),
); );
expect(materialBox, paints..rrect(color: chipTheme.disabledColor)); expect(materialBox, paints..rrect(color: theme.chipTheme.backgroundColor));
}); });
testWidgets('Chip merges ChipThemeData label style with the provided label style', (WidgetTester tester) async { testWidgets('Chip merges ChipThemeData label style with the provided label style', (WidgetTester tester) async {
...@@ -1817,7 +2676,7 @@ void main() { ...@@ -1817,7 +2676,7 @@ void main() {
expect(labelStyle.fontWeight, FontWeight.w200); expect(labelStyle.fontWeight, FontWeight.w200);
}); });
testWidgets('Chip size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async { testWidgets('Material2 - Chip size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
final Key key1 = UniqueKey(); final Key key1 = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
...@@ -1853,6 +2712,44 @@ void main() { ...@@ -1853,6 +2712,44 @@ void main() {
expect(tester.getSize(find.byKey(key2)), const Size(80.0, 32.0)); expect(tester.getSize(find.byKey(key2)), const Size(80.0, 32.0));
}); });
testWidgets('Material3 - Chip size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
final Key key1 = UniqueKey();
await tester.pumpWidget(
wrapForChip(
child: Theme(
data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.padded),
child: Center(
child: RawChip(
key: key1,
label: const Text('test'),
),
),
),
),
);
expect(tester.getSize(find.byKey(key1)).width, moreOrLessEquals(90.4, epsilon: 0.1));
expect(tester.getSize(find.byKey(key1)).height, equals(48.0));
final Key key2 = UniqueKey();
await tester.pumpWidget(
wrapForChip(
child: Theme(
data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
child: Center(
child: RawChip(
key: key2,
label: const Text('test'),
),
),
),
),
);
expect(tester.getSize(find.byKey(key2)).width, moreOrLessEquals(90.4, epsilon: 0.1));
expect(tester.getSize(find.byKey(key2)).height, equals(38.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Chip uses the right theme colors for the right components', (WidgetTester tester) async { testWidgets('Chip uses the right theme colors for the right components', (WidgetTester tester) async {
final ThemeData themeData = ThemeData( final ThemeData themeData = ThemeData(
platform: TargetPlatform.android, platform: TargetPlatform.android,
...@@ -2494,18 +3391,16 @@ void main() { ...@@ -2494,18 +3391,16 @@ void main() {
expect(tester.takeException(), null); expect(tester.takeException(), null);
}); });
testWidgets('Chip elevation and shadow color work correctly', (WidgetTester tester) async { testWidgets('Material2 - Chip elevation and shadow color work correctly', (WidgetTester tester) async {
final ThemeData theme = ThemeData( final ThemeData theme = ThemeData(
useMaterial3: false, useMaterial3: false,
platform: TargetPlatform.android, platform: TargetPlatform.android,
primarySwatch: Colors.red, primarySwatch: Colors.red,
); );
final ChipThemeData chipTheme = theme.chipTheme;
InputChip inputChip = const InputChip(label: Text('Label')); InputChip inputChip = const InputChip(label: Text('Label'));
Widget buildChip(ChipThemeData data) { Widget buildChip() {
return wrapForChip( return wrapForChip(
child: Theme( child: Theme(
data: theme, data: theme,
...@@ -2514,7 +3409,7 @@ void main() { ...@@ -2514,7 +3409,7 @@ void main() {
); );
} }
await tester.pumpWidget(buildChip(chipTheme)); await tester.pumpWidget(buildChip());
Material material = getMaterial(tester); Material material = getMaterial(tester);
expect(material.elevation, 0.0); expect(material.elevation, 0.0);
expect(material.shadowColor, Colors.black); expect(material.shadowColor, Colors.black);
...@@ -2526,7 +3421,7 @@ void main() { ...@@ -2526,7 +3421,7 @@ void main() {
selectedShadowColor: Colors.blue, selectedShadowColor: Colors.blue,
); );
await tester.pumpWidget(buildChip(chipTheme)); await tester.pumpWidget(buildChip());
await tester.pumpAndSettle(); await tester.pumpAndSettle();
material = getMaterial(tester); material = getMaterial(tester);
expect(material.elevation, 4.0); expect(material.elevation, 4.0);
...@@ -2539,13 +3434,56 @@ void main() { ...@@ -2539,13 +3434,56 @@ void main() {
selectedShadowColor: Colors.blue, selectedShadowColor: Colors.blue,
); );
await tester.pumpWidget(buildChip(chipTheme)); await tester.pumpWidget(buildChip());
await tester.pumpAndSettle(); await tester.pumpAndSettle();
material = getMaterial(tester); material = getMaterial(tester);
expect(material.shadowColor, Colors.blue); expect(material.shadowColor, Colors.blue);
}); });
testWidgets('can be tapped outside of chip body', (WidgetTester tester) async { testWidgets('Material3 - Chip elevation and shadow color work correctly', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
InputChip inputChip = const InputChip(label: Text('Label'));
Widget buildChip() {
return wrapForChip(
theme: theme,
child: inputChip,
);
}
await tester.pumpWidget(buildChip());
Material material = getMaterial(tester);
expect(material.elevation, 0.0);
expect(material.shadowColor, Colors.transparent);
inputChip = const InputChip(
label: Text('Label'),
elevation: 4.0,
shadowColor: Colors.green,
selectedShadowColor: Colors.blue,
);
await tester.pumpWidget(buildChip());
await tester.pumpAndSettle();
material = getMaterial(tester);
expect(material.elevation, 4.0);
expect(material.shadowColor, Colors.green);
inputChip = const InputChip(
label: Text('Label'),
selected: true,
shadowColor: Colors.green,
selectedShadowColor: Colors.blue,
);
await tester.pumpWidget(buildChip());
await tester.pumpAndSettle();
material = getMaterial(tester);
expect(material.shadowColor, Colors.blue);
});
testWidgets('can be tapped outside of chip body', (WidgetTester tester) async {
bool pressed = false; bool pressed = false;
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
...@@ -2600,10 +3538,10 @@ void main() { ...@@ -2600,10 +3538,10 @@ void main() {
checkChipMaterialClipBehavior(tester, Clip.antiAlias); checkChipMaterialClipBehavior(tester, Clip.antiAlias);
}); });
testWidgets('selected chip and avatar draw darkened layer within avatar circle', (WidgetTester tester) async { testWidgets('Material2 - selected chip and avatar draw darkened layer within avatar circle', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: const FilterChip( child: const FilterChip(
avatar: CircleAvatar(child: Text('t')), avatar: CircleAvatar(child: Text('t')),
label: Text('test'), label: Text('test'),
...@@ -2628,6 +3566,33 @@ void main() { ...@@ -2628,6 +3566,33 @@ void main() {
])); ]));
}); });
testWidgets('Material3 - selected chip and avatar draw darkened layer within avatar circle', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
child: const FilterChip(
avatar: CircleAvatar(child: Text('t')),
label: Text('test'),
selected: true,
onSelected: null,
),
),
);
final RenderBox rawChip = tester.firstRenderObject<RenderBox>(
find.descendant(
of: find.byType(RawChip),
matching: find.byWidgetPredicate((Widget widget) {
return widget.runtimeType.toString() == '_ChipRenderWidget';
}),
),
);
const Color selectScrimColor = Color(0x60191919);
expect(rawChip, paints..path(color: selectScrimColor, includes: <Offset>[
const Offset(11, 11),
], excludes: <Offset>[
const Offset(4, 4),
]));
});
testWidgets('Chips should use InkWell instead of InkResponse.', (WidgetTester tester) async { testWidgets('Chips should use InkWell instead of InkResponse.', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/28646 // Regression test for https://github.com/flutter/flutter/issues/28646
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2732,7 +3697,7 @@ void main() { ...@@ -2732,7 +3697,7 @@ void main() {
expect(textColor(), disabledColor); expect(textColor(), disabledColor);
}); });
testWidgets('Chip uses stateful border side color in different states', (WidgetTester tester) async { testWidgets('Material2 - Chip uses stateful border side color in different states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose); addTearDown(focusNode.dispose);
...@@ -2745,7 +3710,85 @@ void main() { ...@@ -2745,7 +3710,85 @@ void main() {
BorderSide getBorderSide(Set<MaterialState> states) { BorderSide getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor; Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) {
sideColor = pressedColor;
} else if (states.contains(MaterialState.hovered)) {
sideColor = hoverColor;
} else if (states.contains(MaterialState.focused)) {
sideColor = focusedColor;
} else if (states.contains(MaterialState.selected)) {
sideColor = selectedColor;
}
return BorderSide(color: sideColor);
}
Widget chipWidget({ bool enabled = true, bool selected = false }) {
return MaterialApp(
theme: ThemeData(useMaterial3: false),
home: Scaffold(
body: Focus(
focusNode: focusNode,
child: ChoiceChip(
label: const Text('Chip'),
selected: selected,
onSelected: enabled ? (_) {} : null,
side: _MaterialStateBorderSide(getBorderSide),
),
),
),
);
}
// Default, not disabled.
await tester.pumpWidget(chipWidget());
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
// Selected.
await tester.pumpWidget(chipWidget(selected: true));
expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
// Focused.
final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus();
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor));
// Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor));
// Pressed.
await gesture.down(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor));
// Disabled.
await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
});
testWidgets('Material3 - Chip uses stateful border side color in different states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
const Color pressedColor = Color(0x00000001);
const Color hoverColor = Color(0x00000002);
const Color focusedColor = Color(0x00000003);
const Color defaultColor = Color(0x00000004);
const Color selectedColor = Color(0x00000005);
const Color disabledColor = Color(0x00000006);
BorderSide getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) { if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor; sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) { } else if (states.contains(MaterialState.pressed)) {
...@@ -2757,7 +3800,84 @@ void main() { ...@@ -2757,7 +3800,84 @@ void main() {
} else if (states.contains(MaterialState.selected)) { } else if (states.contains(MaterialState.selected)) {
sideColor = selectedColor; sideColor = selectedColor;
} }
return BorderSide(color: sideColor);
}
Widget chipWidget({ bool enabled = true, bool selected = false }) {
return MaterialApp(
home: Scaffold(
body: Focus(
focusNode: focusNode,
child: ChoiceChip(
label: const Text('Chip'),
selected: selected,
onSelected: enabled ? (_) {} : null,
side: _MaterialStateBorderSide(getBorderSide),
),
),
),
);
}
// Default, not disabled.
await tester.pumpWidget(chipWidget());
expect(find.byType(RawChip), paints..drrect(color: defaultColor));
// Selected.
await tester.pumpWidget(chipWidget(selected: true));
expect(find.byType(RawChip), paints..drrect(color: selectedColor));
// Focused.
final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus();
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..drrect(color: focusedColor));
// Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..drrect(color: hoverColor));
// Pressed.
await gesture.down(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..drrect(color: pressedColor));
// Disabled.
await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..drrect(color: disabledColor));
});
testWidgets('Material2 - Chip uses stateful border side color from resolveWith', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
const Color pressedColor = Color(0x00000001);
const Color hoverColor = Color(0x00000002);
const Color focusedColor = Color(0x00000003);
const Color defaultColor = Color(0x00000004);
const Color selectedColor = Color(0x00000005);
const Color disabledColor = Color(0x00000006);
BorderSide getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) {
sideColor = pressedColor;
} else if (states.contains(MaterialState.hovered)) {
sideColor = hoverColor;
} else if (states.contains(MaterialState.focused)) {
sideColor = focusedColor;
} else if (states.contains(MaterialState.selected)) {
sideColor = selectedColor;
}
return BorderSide(color: sideColor); return BorderSide(color: sideColor);
} }
...@@ -2771,7 +3891,7 @@ void main() { ...@@ -2771,7 +3891,7 @@ void main() {
label: const Text('Chip'), label: const Text('Chip'),
selected: selected, selected: selected,
onSelected: enabled ? (_) {} : null, onSelected: enabled ? (_) {} : null,
side: _MaterialStateBorderSide(getBorderSide), side: MaterialStateBorderSide.resolveWith(getBorderSide),
), ),
), ),
), ),
...@@ -2784,13 +3904,91 @@ void main() { ...@@ -2784,13 +3904,91 @@ void main() {
// Selected. // Selected.
await tester.pumpWidget(chipWidget(selected: true)); await tester.pumpWidget(chipWidget(selected: true));
expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor)); expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
// Focused.
final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus();
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor));
// Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor));
// Pressed.
await gesture.down(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor));
// Disabled.
await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
});
testWidgets('Material3 - Chip uses stateful border side color from resolveWith', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
const Color pressedColor = Color(0x00000001);
const Color hoverColor = Color(0x00000002);
const Color focusedColor = Color(0x00000003);
const Color defaultColor = Color(0x00000004);
const Color selectedColor = Color(0x00000005);
const Color disabledColor = Color(0x00000006);
BorderSide getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) {
sideColor = pressedColor;
} else if (states.contains(MaterialState.hovered)) {
sideColor = hoverColor;
} else if (states.contains(MaterialState.focused)) {
sideColor = focusedColor;
} else if (states.contains(MaterialState.selected)) {
sideColor = selectedColor;
}
return BorderSide(color: sideColor);
}
Widget chipWidget({ bool enabled = true, bool selected = false }) {
return MaterialApp(
home: Scaffold(
body: Focus(
focusNode: focusNode,
child: ChoiceChip(
label: const Text('Chip'),
selected: selected,
onSelected: enabled ? (_) {} : null,
side: MaterialStateBorderSide.resolveWith(getBorderSide),
),
),
),
);
}
// Default, not disabled.
await tester.pumpWidget(chipWidget());
expect(find.byType(RawChip), paints..drrect(color: defaultColor));
// Selected.
await tester.pumpWidget(chipWidget(selected: true));
expect(find.byType(RawChip), paints..drrect(color: selectedColor));
// Focused. // Focused.
final FocusNode chipFocusNode = focusNode.children.first; final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus(); chipFocusNode.requestFocus();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor)); expect(find.byType(RawChip), paints..drrect(color: focusedColor));
// Hovered. // Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip)); final Offset center = tester.getCenter(find.byType(ChoiceChip));
...@@ -2800,20 +3998,20 @@ void main() { ...@@ -2800,20 +3998,20 @@ void main() {
await gesture.addPointer(); await gesture.addPointer();
await gesture.moveTo(center); await gesture.moveTo(center);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor)); expect(find.byType(RawChip), paints..drrect(color: hoverColor));
// Pressed. // Pressed.
await gesture.down(center); await gesture.down(center);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor)); expect(find.byType(RawChip), paints..drrect(color: pressedColor));
// Disabled. // Disabled.
await tester.pumpWidget(chipWidget(enabled: false)); await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor)); expect(find.byType(RawChip), paints..drrect(color: disabledColor));
}); });
testWidgets('Chip uses stateful border side color from resolveWith', (WidgetTester tester) async { testWidgets('Material2 - Chip uses stateful nullable border side color from resolveWith', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose); addTearDown(focusNode.dispose);
...@@ -2821,12 +4019,13 @@ void main() { ...@@ -2821,12 +4019,13 @@ void main() {
const Color hoverColor = Color(0x00000002); const Color hoverColor = Color(0x00000002);
const Color focusedColor = Color(0x00000003); const Color focusedColor = Color(0x00000003);
const Color defaultColor = Color(0x00000004); const Color defaultColor = Color(0x00000004);
const Color selectedColor = Color(0x00000005);
const Color disabledColor = Color(0x00000006); const Color disabledColor = Color(0x00000006);
BorderSide getBorderSide(Set<MaterialState> states) { const Color fallbackThemeColor = Color(0x00000007);
Color sideColor = defaultColor; const BorderSide defaultBorderSide = BorderSide(color: fallbackThemeColor, width: 10.0);
BorderSide? getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) { if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor; sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) { } else if (states.contains(MaterialState.pressed)) {
...@@ -2836,9 +4035,8 @@ void main() { ...@@ -2836,9 +4035,8 @@ void main() {
} else if (states.contains(MaterialState.focused)) { } else if (states.contains(MaterialState.focused)) {
sideColor = focusedColor; sideColor = focusedColor;
} else if (states.contains(MaterialState.selected)) { } else if (states.contains(MaterialState.selected)) {
sideColor = selectedColor; return null;
} }
return BorderSide(color: sideColor); return BorderSide(color: sideColor);
} }
...@@ -2848,11 +4046,16 @@ void main() { ...@@ -2848,11 +4046,16 @@ void main() {
home: Scaffold( home: Scaffold(
body: Focus( body: Focus(
focusNode: focusNode, focusNode: focusNode,
child: ChoiceChip( child: ChipTheme(
label: const Text('Chip'), data: ThemeData.light().chipTheme.copyWith(
selected: selected, side: defaultBorderSide,
onSelected: enabled ? (_) {} : null, ),
side: MaterialStateBorderSide.resolveWith(getBorderSide), child: ChoiceChip(
label: const Text('Chip'),
selected: selected,
onSelected: enabled ? (_) {} : null,
side: MaterialStateBorderSide.resolveWith(getBorderSide),
),
), ),
), ),
), ),
...@@ -2865,7 +4068,9 @@ void main() { ...@@ -2865,7 +4068,9 @@ void main() {
// Selected. // Selected.
await tester.pumpWidget(chipWidget(selected: true)); await tester.pumpWidget(chipWidget(selected: true));
expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor)); // Because the resolver returns `null` for this value, we should fall back
// to the theme.
expect(find.byType(RawChip), paints..rrect()..rrect(color: fallbackThemeColor));
// Focused. // Focused.
final FocusNode chipFocusNode = focusNode.children.first; final FocusNode chipFocusNode = focusNode.children.first;
...@@ -2892,10 +4097,9 @@ void main() { ...@@ -2892,10 +4097,9 @@ void main() {
await tester.pumpWidget(chipWidget(enabled: false)); await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor)); expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
}); });
testWidgets('Chip uses stateful nullable border side color from resolveWith', (WidgetTester tester) async { testWidgets('Material3 - Chip uses stateful nullable border side color from resolveWith', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose); addTearDown(focusNode.dispose);
...@@ -2910,7 +4114,6 @@ void main() { ...@@ -2910,7 +4114,6 @@ void main() {
BorderSide? getBorderSide(Set<MaterialState> states) { BorderSide? getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor; Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) { if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor; sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) { } else if (states.contains(MaterialState.pressed)) {
...@@ -2922,13 +4125,11 @@ void main() { ...@@ -2922,13 +4125,11 @@ void main() {
} else if (states.contains(MaterialState.selected)) { } else if (states.contains(MaterialState.selected)) {
return null; return null;
} }
return BorderSide(color: sideColor); return BorderSide(color: sideColor);
} }
Widget chipWidget({ bool enabled = true, bool selected = false }) { Widget chipWidget({ bool enabled = true, bool selected = false }) {
return MaterialApp( return MaterialApp(
theme: ThemeData(useMaterial3: false),
home: Scaffold( home: Scaffold(
body: Focus( body: Focus(
focusNode: focusNode, focusNode: focusNode,
...@@ -2950,19 +4151,19 @@ void main() { ...@@ -2950,19 +4151,19 @@ void main() {
// Default, not disabled. // Default, not disabled.
await tester.pumpWidget(chipWidget()); await tester.pumpWidget(chipWidget());
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor)); expect(find.byType(RawChip), paints..drrect(color: defaultColor));
// Selected. // Selected.
await tester.pumpWidget(chipWidget(selected: true)); await tester.pumpWidget(chipWidget(selected: true));
// Because the resolver returns `null` for this value, we should fall back // Because the resolver returns `null` for this value, we should fall back
// to the theme // to the theme
expect(find.byType(RawChip), paints..rrect()..rrect(color: fallbackThemeColor)); expect(find.byType(RawChip), paints..drrect(color: fallbackThemeColor));
// Focused. // Focused.
final FocusNode chipFocusNode = focusNode.children.first; final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus(); chipFocusNode.requestFocus();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor)); expect(find.byType(RawChip), paints..drrect(color: focusedColor));
// Hovered. // Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip)); final Offset center = tester.getCenter(find.byType(ChoiceChip));
...@@ -2972,24 +4173,24 @@ void main() { ...@@ -2972,24 +4173,24 @@ void main() {
await gesture.addPointer(); await gesture.addPointer();
await gesture.moveTo(center); await gesture.moveTo(center);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor)); expect(find.byType(RawChip), paints..drrect(color: hoverColor));
// Pressed. // Pressed.
await gesture.down(center); await gesture.down(center);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor)); expect(find.byType(RawChip), paints..drrect(color: pressedColor));
// Disabled. // Disabled.
await tester.pumpWidget(chipWidget(enabled: false)); await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor)); expect(find.byType(RawChip), paints..drrect(color: disabledColor));
}); });
testWidgets('Chip uses stateful shape in different states', (WidgetTester tester) async { testWidgets('Material2 - Chip uses stateful shape in different states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose); addTearDown(focusNode.dispose);
OutlinedBorder? getShape(Set<MaterialState> states) {
OutlinedBorder? getShape(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) { if (states.contains(MaterialState.disabled)) {
return const BeveledRectangleBorder(); return const BeveledRectangleBorder();
} else if (states.contains(MaterialState.pressed)) { } else if (states.contains(MaterialState.pressed)) {
...@@ -3001,7 +4202,6 @@ void main() { ...@@ -3001,7 +4202,6 @@ void main() {
} else if (states.contains(MaterialState.selected)) { } else if (states.contains(MaterialState.selected)) {
return const BeveledRectangleBorder(); return const BeveledRectangleBorder();
} }
return null; return null;
} }
...@@ -3057,7 +4257,77 @@ void main() { ...@@ -3057,7 +4257,77 @@ void main() {
expect(getMaterial(tester).shape, isA<BeveledRectangleBorder>()); expect(getMaterial(tester).shape, isA<BeveledRectangleBorder>());
}); });
testWidgets('Chip defers to theme, if shape and side resolves to null', (WidgetTester tester) async { testWidgets('Material3 - Chip uses stateful shape in different states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
OutlinedBorder? getShape(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return const BeveledRectangleBorder();
} else if (states.contains(MaterialState.pressed)) {
return const CircleBorder();
} else if (states.contains(MaterialState.hovered)) {
return const ContinuousRectangleBorder();
} else if (states.contains(MaterialState.focused)) {
return const RoundedRectangleBorder();
} else if (states.contains(MaterialState.selected)) {
return const BeveledRectangleBorder();
}
return null;
}
Widget chipWidget({ bool enabled = true, bool selected = false }) {
return MaterialApp(
home: Scaffold(
body: Focus(
focusNode: focusNode,
child: ChoiceChip(
selected: selected,
label: const Text('Chip'),
shape: _MaterialStateOutlinedBorder(getShape),
onSelected: enabled ? (_) {} : null,
),
),
),
);
}
// Default, not disabled. Defers to default shape.
await tester.pumpWidget(chipWidget());
expect(getMaterial(tester).shape, isA<RoundedRectangleBorder>());
// Selected.
await tester.pumpWidget(chipWidget(selected: true));
expect(getMaterial(tester).shape, isA<BeveledRectangleBorder>());
// Focused.
final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus();
await tester.pumpAndSettle();
expect(getMaterial(tester).shape, isA<RoundedRectangleBorder>());
// Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getMaterial(tester).shape, isA<ContinuousRectangleBorder>());
// Pressed.
await gesture.down(center);
await tester.pumpAndSettle();
expect(getMaterial(tester).shape, isA<CircleBorder>());
// Disabled.
await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle();
expect(getMaterial(tester).shape, isA<BeveledRectangleBorder>());
});
testWidgets('Material2 - Chip defers to theme, if shape and side resolves to null', (WidgetTester tester) async {
const OutlinedBorder themeShape = StadiumBorder(); const OutlinedBorder themeShape = StadiumBorder();
const OutlinedBorder selectedShape = RoundedRectangleBorder(); const OutlinedBorder selectedShape = RoundedRectangleBorder();
const BorderSide themeBorderSide = BorderSide(color: Color(0x00000001)); const BorderSide themeBorderSide = BorderSide(color: Color(0x00000001));
...@@ -3109,7 +4379,58 @@ void main() { ...@@ -3109,7 +4379,58 @@ void main() {
expect(find.byType(RawChip), paints..rect()..drrect(color: selectedBorderSide.color)); expect(find.byType(RawChip), paints..rect()..drrect(color: selectedBorderSide.color));
}); });
testWidgets('Chip responds to density changes.', (WidgetTester tester) async { testWidgets('Chip defers to theme, if shape and side resolves to null', (WidgetTester tester) async {
const OutlinedBorder themeShape = StadiumBorder();
const OutlinedBorder selectedShape = RoundedRectangleBorder();
const BorderSide themeBorderSide = BorderSide(color: Color(0x00000001));
const BorderSide selectedBorderSide = BorderSide(color: Color(0x00000002));
OutlinedBorder? getShape(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return selectedShape;
}
return null;
}
BorderSide? getBorderSide(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return selectedBorderSide;
}
return null;
}
Widget chipWidget({ bool enabled = true, bool selected = false }) {
return MaterialApp(
theme: ThemeData(
chipTheme: ThemeData.light().chipTheme.copyWith(
shape: themeShape,
side: themeBorderSide,
),
),
home: Scaffold(
body: ChoiceChip(
selected: selected,
label: const Text('Chip'),
shape: _MaterialStateOutlinedBorder(getShape),
side: _MaterialStateBorderSide(getBorderSide),
onSelected: enabled ? (_) {} : null,
),
),
);
}
// Default, not disabled. Defer to theme.
await tester.pumpWidget(chipWidget());
expect(getMaterial(tester).shape, isA<StadiumBorder>());
expect(find.byType(RawChip), paints..rrect()..rrect(color: themeBorderSide.color));
// Selected.
await tester.pumpWidget(chipWidget(selected: true));
expect(getMaterial(tester).shape, isA<RoundedRectangleBorder>());
expect(find.byType(RawChip), paints..rect()..drrect(color: selectedBorderSide.color));
});
testWidgets('Material2 - Chip responds to density changes', (WidgetTester tester) async {
const Key key = Key('test'); const Key key = Key('test');
const Key textKey = Key('test text'); const Key textKey = Key('test text');
const Key iconKey = Key('test icon'); const Key iconKey = Key('test icon');
...@@ -3215,6 +4536,121 @@ void main() { ...@@ -3215,6 +4536,121 @@ void main() {
expect(box.size, equals(const Size(128, 24.0 + 16.0))); expect(box.size, equals(const Size(128, 24.0 + 16.0)));
}); });
testWidgets('Material3 - Chip responds to density changes', (WidgetTester tester) async {
const Key key = Key('test');
const Key textKey = Key('test text');
const Key iconKey = Key('test icon');
const Key avatarKey = Key('test avatar');
Future<void> buildTest(VisualDensity visualDensity) async {
return tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: Column(
children: <Widget>[
InputChip(
visualDensity: visualDensity,
key: key,
onPressed: () {},
onDeleted: () {},
label: const Text('Test', key: textKey),
deleteIcon: const Icon(Icons.delete, key: iconKey),
avatar: const Icon(Icons.play_arrow, key: avatarKey),
),
],
),
),
),
),
);
}
// The Chips only change in size vertically in response to density, so
// horizontal changes aren't expected.
await buildTest(VisualDensity.standard);
Rect box = tester.getRect(find.byKey(key));
Rect textBox = tester.getRect(find.byKey(textKey));
Rect iconBox = tester.getRect(find.byKey(iconKey));
Rect avatarBox = tester.getRect(find.byKey(avatarKey));
expect(box.size.width, moreOrLessEquals(130.4, epsilon: 0.1));
expect(box.size.height, equals(32.0 + 16.0));
expect(textBox.size.width, moreOrLessEquals(56.4, epsilon: 0.1));
expect(textBox.size.height, equals(20.0));
expect(iconBox.size, equals(const Size(20, 20)));
expect(avatarBox.size, equals(const Size(18, 18)));
expect(textBox.top, equals(14));
expect(box.bottom - textBox.bottom, equals(14));
expect(textBox.left, moreOrLessEquals(371.79, epsilon: 0.1));
expect(box.right - textBox.right, equals(37));
// Try decreasing density (with higher density numbers).
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0));
box = tester.getRect(find.byKey(key));
textBox = tester.getRect(find.byKey(textKey));
iconBox = tester.getRect(find.byKey(iconKey));
avatarBox = tester.getRect(find.byKey(avatarKey));
expect(box.size.width, moreOrLessEquals(130.4, epsilon: 0.1));
expect(box.size.height, equals(60));
expect(textBox.size.width, moreOrLessEquals(56.4, epsilon: 0.1));
expect(textBox.size.height, equals(20.0));
expect(iconBox.size, equals(const Size(20, 20)));
expect(avatarBox.size, equals(const Size(18, 18)));
expect(textBox.top, equals(20));
expect(box.bottom - textBox.bottom, equals(20));
expect(textBox.left, moreOrLessEquals(371.79, epsilon: 0.1));
expect(box.right - textBox.right, equals(37));
// Try increasing density (with lower density numbers).
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0));
box = tester.getRect(find.byKey(key));
textBox = tester.getRect(find.byKey(textKey));
iconBox = tester.getRect(find.byKey(iconKey));
avatarBox = tester.getRect(find.byKey(avatarKey));
expect(box.size.width, moreOrLessEquals(130.4, epsilon: 0.1));
expect(box.size.height, equals(36));
expect(textBox.size.width, moreOrLessEquals(56.4, epsilon: 0.1));
expect(textBox.size.height, equals(20.0));
expect(iconBox.size, equals(const Size(20, 20)));
expect(avatarBox.size, equals(const Size(18, 18)));
expect(textBox.top, equals(8));
expect(box.bottom - textBox.bottom, equals(8));
expect(textBox.left, moreOrLessEquals(371.79, epsilon: 0.1));
expect(box.right - textBox.right, equals(37));
// Now test that horizontal and vertical are wired correctly. Negating the
// horizontal should have no change over what's above.
await buildTest(const VisualDensity(horizontal: 3.0, vertical: -3.0));
await tester.pumpAndSettle();
box = tester.getRect(find.byKey(key));
textBox = tester.getRect(find.byKey(textKey));
iconBox = tester.getRect(find.byKey(iconKey));
avatarBox = tester.getRect(find.byKey(avatarKey));
expect(box.size.width, moreOrLessEquals(130.4, epsilon: 0.1));
expect(box.size.height, equals(36));
expect(textBox.size.width, moreOrLessEquals(56.4, epsilon: 0.1));
expect(textBox.size.height, equals(20.0));
expect(iconBox.size, equals(const Size(20, 20)));
expect(avatarBox.size, equals(const Size(18, 18)));
expect(textBox.top, equals(8));
expect(box.bottom - textBox.bottom, equals(8));
expect(textBox.left, moreOrLessEquals(371.79, epsilon: 0.1));
expect(box.right - textBox.right, equals(37));
// Make sure the "Comfortable" setting is the spec'd size
await buildTest(VisualDensity.comfortable);
await tester.pumpAndSettle();
box = tester.getRect(find.byKey(key));
expect(box.size.width, moreOrLessEquals(130.4, epsilon: 0.1));
expect(box.size.height, equals(28.0 + 16.0));
// Make sure the "Compact" setting is the spec'd size
await buildTest(VisualDensity.compact);
await tester.pumpAndSettle();
box = tester.getRect(find.byKey(key));
expect(box.size.width, moreOrLessEquals(130.4, epsilon: 0.1));
expect(box.size.height, equals(24.0 + 16.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('Chip delete button tooltip is disabled if deleteButtonTooltipMessage is empty', (WidgetTester tester) async { testWidgets('Chip delete button tooltip is disabled if deleteButtonTooltipMessage is empty', (WidgetTester tester) async {
final UniqueKey deleteButtonKey = UniqueKey(); final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -3307,12 +4743,12 @@ void main() { ...@@ -3307,12 +4743,12 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
testWidgets('Chip background color and shape are drawn on Ink', (WidgetTester tester) async { testWidgets('Material2 - Chip background color and shape are drawn on Ink', (WidgetTester tester) async {
const Color backgroundColor = Color(0xff00ff00); const Color backgroundColor = Color(0xff00ff00);
const OutlinedBorder shape = ContinuousRectangleBorder(); const OutlinedBorder shape = ContinuousRectangleBorder();
await tester.pumpWidget(wrapForChip( await tester.pumpWidget(wrapForChip(
useMaterial3: false, theme: ThemeData(useMaterial3: false),
child: const RawChip( child: const RawChip(
label: Text('text'), label: Text('text'),
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
...@@ -3329,6 +4765,29 @@ void main() { ...@@ -3329,6 +4765,29 @@ void main() {
expect(decoration.shape, shape); expect(decoration.shape, shape);
}); });
testWidgets('Material3 - Chip background color and shape are drawn on Ink', (WidgetTester tester) async {
const Color backgroundColor = Color(0xff00ff00);
const OutlinedBorder shape = ContinuousRectangleBorder();
final ThemeData theme = ThemeData();
await tester.pumpWidget(wrapForChip(
theme: theme,
child: const RawChip(
label: Text('text'),
backgroundColor: backgroundColor,
shape: shape,
),
));
final Ink ink = tester.widget(find.descendant(
of: find.byType(RawChip),
matching: find.byType(Ink),
));
final ShapeDecoration decoration = ink.decoration! as ShapeDecoration;
expect(decoration.color, backgroundColor);
expect(decoration.shape, shape.copyWith(side: BorderSide(color: theme.colorScheme.outline)));
});
testWidgets('Chip highlight color is drawn on top of the backgroundColor', (WidgetTester tester) async { testWidgets('Chip highlight color is drawn on top of the backgroundColor', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'RawChip'); final FocusNode focusNode = FocusNode(debugLabel: 'RawChip');
addTearDown(focusNode.dispose); addTearDown(focusNode.dispose);
...@@ -3365,7 +4824,6 @@ void main() { ...@@ -3365,7 +4824,6 @@ void main() {
const Color selectedColor = Color(0xffff0000); const Color selectedColor = Color(0xffff0000);
Widget buildApp({ required bool enabled, required bool selected }) { Widget buildApp({ required bool enabled, required bool selected }) {
return wrapForChip( return wrapForChip(
useMaterial3: true,
child: RawChip( child: RawChip(
isEnabled: enabled, isEnabled: enabled,
selected: selected, selected: selected,
...@@ -3420,7 +4878,6 @@ void main() { ...@@ -3420,7 +4878,6 @@ void main() {
const Color selectedColor = Color(0xffff0000); const Color selectedColor = Color(0xffff0000);
Widget buildApp({ required bool enabled, required bool selected }) { Widget buildApp({ required bool enabled, required bool selected }) {
return wrapForChip( return wrapForChip(
useMaterial3: true,
child: RawChip( child: RawChip(
isEnabled: enabled, isEnabled: enabled,
selected: selected, selected: selected,
...@@ -3497,7 +4954,7 @@ void main() { ...@@ -3497,7 +4954,7 @@ void main() {
// This is a regression test for https://github.com/flutter/flutter/pull/133615. // This is a regression test for https://github.com/flutter/flutter/pull/133615.
testWidgets('Material3 - Custom shape without provided side uses default side', (WidgetTester tester) async { testWidgets('Material3 - Custom shape without provided side uses default side', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true); final ThemeData theme = ThemeData();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: theme, theme: theme,
...@@ -3523,7 +4980,6 @@ void main() { ...@@ -3523,7 +4980,6 @@ void main() {
testWidgets("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async { testWidgets("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async {
Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) { Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) {
return MaterialApp( return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: Material( home: Material(
child: Center( child: Center(
child: RawChip( child: RawChip(
...@@ -3577,7 +5033,6 @@ void main() { ...@@ -3577,7 +5033,6 @@ void main() {
testWidgets('Material3 - Chip.iconTheme respects default iconTheme.size', (WidgetTester tester) async { testWidgets('Material3 - Chip.iconTheme respects default iconTheme.size', (WidgetTester tester) async {
Widget buildChip({ IconThemeData? iconTheme }) { Widget buildChip({ IconThemeData? iconTheme }) {
return MaterialApp( return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: Directionality( home: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Material( child: Material(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment