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 @@
@Tags(<String>['reduced-test-set'])
library;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
......@@ -76,11 +77,10 @@ Widget wrapForChip({
required Widget child,
TextDirection textDirection = TextDirection.ltr,
double textScaleFactor = 1.0,
Brightness brightness = Brightness.light,
bool? useMaterial3,
ThemeData? theme,
}) {
return MaterialApp(
theme: ThemeData(brightness: brightness, useMaterial3: useMaterial3),
theme: theme,
home: Directionality(
textDirection: textDirection,
child: MediaQuery(
......@@ -145,11 +145,11 @@ Widget chipWithOptionalDeleteButton({
String? chipTooltip,
String? deleteButtonTooltipMessage,
VoidCallback? onPressed = doNothing,
bool? useMaterial3,
ThemeData? themeData,
}) {
return wrapForChip(
textDirection: textDirection,
useMaterial3: useMaterial3,
theme: themeData,
child: Wrap(
children: <Widget>[
RawChip(
......@@ -298,8 +298,8 @@ void main() {
testWidgets('M3 Chip defaults', (WidgetTester tester) async {
late TextTheme textTheme;
final ThemeData lightTheme = ThemeData.light(useMaterial3: true);
final ThemeData darkTheme = ThemeData.dark(useMaterial3: true);
final ThemeData lightTheme = ThemeData.light();
final ThemeData darkTheme = ThemeData.dark();
Widget buildFrame(ThemeData theme) {
return MaterialApp(
......@@ -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);
await tester.pumpWidget(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: const Row(
children: <Widget>[
Chip(label: Text('Test'), labelStyle: style),
......@@ -617,28 +617,93 @@ void main() {
expect(tester.getSize(find.byType(Chip)), const Size(800.0, 48.0));
});
testWidgets('Chip responds to materialTapTargetSize', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
useMaterial3: false,
child: const Column(
children: <Widget>[
Chip(
label: Text('X'),
materialTapTargetSize: MaterialTapTargetSize.padded,
),
Chip(
label: Text('X'),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
testWidgets('Material3 - Chip in row works ok', (WidgetTester tester) async {
const TextStyle style = TextStyle(fontSize: 10.0);
await tester.pumpWidget(
wrapForChip(
child: const Row(
children: <Widget>[
Chip(label: Text('Test'), labelStyle: style),
],
),
);
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 {
final UniqueKey deleteKey = UniqueKey();
......@@ -739,10 +804,10 @@ void main() {
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(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: const Column(
children: <Widget>[
Chip(
......@@ -816,12 +881,90 @@ void main() {
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 keyB = GlobalKey();
await tester.pumpWidget(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: Column(
children: <Widget>[
Chip(
......@@ -843,6 +986,34 @@ void main() {
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 {
final Key keyA = GlobalKey();
await tester.pumpWidget(
......@@ -961,12 +1132,12 @@ void main() {
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();
Future<void> pushChip({ Widget? avatar }) async {
return tester.pumpWidget(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: Wrap(
children: <Widget>[
RawChip(
......@@ -1003,24 +1174,28 @@ void main() {
await tester.pump(const Duration(milliseconds: 20));
// Avatar drawer should start expanding.
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.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(-18.8, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(13.2, epsilon: 0.1));
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)).height, equals(48.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(labelKey)).dx, moreOrLessEquals(18.6, epsilon: 0.1));
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)).height, equals(48.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(labelKey)).dx, moreOrLessEquals(26.7, epsilon: 0.1));
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)).height, equals(48.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(labelKey)).dx, moreOrLessEquals(31.5, epsilon: 0.1));
......@@ -1029,6 +1204,7 @@ void main() {
// height.
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)).height, equals(48.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(labelKey)), equals(const Offset(36.0, 17.0)));
......@@ -1044,24 +1220,28 @@ void main() {
await tester.pump(const Duration(milliseconds: 20));
// Avatar drawer should start contracting.
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.getTopLeft(find.byKey(avatarKey)).dx, moreOrLessEquals(2.9, epsilon: 0.1));
expect(tester.getTopLeft(find.byKey(labelKey)).dx, moreOrLessEquals(34.9, epsilon: 0.1));
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)).height, equals(48.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(labelKey)).dx, moreOrLessEquals(30.0, epsilon: 0.1));
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)).height, equals(48.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(labelKey)).dx, moreOrLessEquals(16.1, epsilon: 0.1));
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)).height, equals(48.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(labelKey)).dx, moreOrLessEquals(12.0, epsilon: 0.1));
......@@ -1074,14 +1254,138 @@ void main() {
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 deleteButtonKey = Key('delete');
bool wasDeleted = false;
Future<void> pushChip({ bool deletable = false }) async {
return tester.pumpWidget(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
......@@ -1191,17 +1495,147 @@ void main() {
expect(find.byKey(deleteButtonKey), findsNothing);
});
testWidgets('Delete button takes up at most half of the chip', (WidgetTester tester) async {
final UniqueKey chipKey = UniqueKey();
bool chipPressed = false;
bool deletePressed = false;
testWidgets('Material3 - Delete button drawer works as expected on RawChip', (WidgetTester tester) async {
const Key labelKey = Key('label');
const Key deleteButtonKey = Key('delete');
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(
wrapForChip(
child: Wrap(
children: <Widget>[
RawChip(
key: chipKey,
// No delete button
await pushChip();
expect(tester.getSize(find.byType(RawChip)).width, moreOrLessEquals(90.4, epsilon: 0.01));
expect(tester.getSize(find.byType(RawChip)).height, equals(48.0));
// 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: () {
chipPressed = true;
},
......@@ -1209,7 +1643,7 @@ void main() {
deletePressed = true;
},
label: const Text(''),
),
),
],
),
),
......@@ -1227,13 +1661,13 @@ void main() {
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 deleteButtonKey = UniqueKey();
await tester.pumpWidget(
chipWithOptionalDeleteButton(
useMaterial3: false,
themeData: ThemeData(useMaterial3: false),
labelKey: labelKey,
deleteButtonKey: deleteButtonKey,
deletable: true,
......@@ -1277,6 +1711,51 @@ void main() {
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 {
final GlobalKey labelKey = GlobalKey();
final GlobalKey deleteButtonKey = GlobalKey();
......@@ -1310,13 +1789,13 @@ void main() {
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 deleteButtonKey = UniqueKey();
await tester.pumpWidget(
chipWithOptionalDeleteButton(
useMaterial3: false,
themeData: ThemeData(useMaterial3: false),
labelKey: labelKey,
deleteButtonKey: deleteButtonKey,
deletable: true,
......@@ -1364,13 +1843,62 @@ void main() {
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 deleteButtonKey = UniqueKey();
await tester.pumpWidget(
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,
onPressed: null,
deleteButtonKey: deleteButtonKey,
......@@ -1419,6 +1947,62 @@ void main() {
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 {
// Creates an RTL chip with a delete button.
final UniqueKey labelKey = UniqueKey();
......@@ -1449,13 +2033,13 @@ void main() {
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.
final UniqueKey labelKey = UniqueKey();
await tester.pumpWidget(
chipWithOptionalDeleteButton(
useMaterial3: false,
themeData: ThemeData(useMaterial3: false),
labelKey: labelKey,
deletable: false,
),
......@@ -1504,13 +2088,69 @@ void main() {
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;
final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
return tester.pumpWidget(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
......@@ -1548,6 +2188,8 @@ void main() {
);
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));
......@@ -1566,6 +2208,7 @@ void main() {
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));
......@@ -1585,17 +2228,17 @@ void main() {
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;
final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ bool selectable = false }) async {
Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
return tester.pumpWidget(
wrapForChip(
useMaterial3: false,
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return RawChip(
avatar: avatar,
onSelected: selectable
? (bool value) {
setState(() {
......@@ -1614,11 +2257,94 @@ void main() {
);
}
// Without avatar, but not selectable.
await pushChip();
expect(tester.getSize(find.byType(RawChip)), equals(const Size(234.0, 48.0)));
// Turn on selection.
// With avatar, but not selectable.
final UniqueKey avatarKey = UniqueKey();
await pushChip(
avatar: SizedBox(width: 40.0, height: 40.0, key: avatarKey),
);
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 tester.pumpAndSettle();
......@@ -1639,7 +2365,9 @@ void main() {
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));
......@@ -1659,13 +2387,90 @@ void main() {
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;
final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
return tester.pumpWidget(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: Wrap(
children: <Widget>[
StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
......@@ -1716,26 +2521,80 @@ void main() {
await tester.pumpAndSettle();
});
testWidgets('Chip uses ThemeData chip theme if present', (WidgetTester tester) async {
final ThemeData theme = ThemeData(
useMaterial3: false,
platform: TargetPlatform.android,
primarySwatch: Colors.red,
testWidgets('Material3 - Activation works as expected on RawChip', (WidgetTester tester) async {
bool selected = false;
final UniqueKey labelKey = UniqueKey();
Future<void> pushChip({ Widget? avatar, bool selectable = false }) async {
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(
child: Theme(
data: theme,
child: const InputChip(
label: Text('Label'),
child: InputChip(
label: const Text('Label'),
onPressed: () {},
),
),
);
}
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpWidget(buildChip());
final RenderBox materialBox = tester.firstRenderObject<RenderBox>(
find.descendant(
......@@ -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 {
......@@ -1817,7 +2676,7 @@ void main() {
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();
await tester.pumpWidget(
wrapForChip(
......@@ -1853,6 +2712,44 @@ void main() {
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 {
final ThemeData themeData = ThemeData(
platform: TargetPlatform.android,
......@@ -2494,18 +3391,16 @@ void main() {
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(
useMaterial3: false,
platform: TargetPlatform.android,
primarySwatch: Colors.red,
);
final ChipThemeData chipTheme = theme.chipTheme;
InputChip inputChip = const InputChip(label: Text('Label'));
Widget buildChip(ChipThemeData data) {
Widget buildChip() {
return wrapForChip(
child: Theme(
data: theme,
......@@ -2514,7 +3409,7 @@ void main() {
);
}
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpWidget(buildChip());
Material material = getMaterial(tester);
expect(material.elevation, 0.0);
expect(material.shadowColor, Colors.black);
......@@ -2526,7 +3421,7 @@ void main() {
selectedShadowColor: Colors.blue,
);
await tester.pumpWidget(buildChip(chipTheme));
await tester.pumpWidget(buildChip());
await tester.pumpAndSettle();
material = getMaterial(tester);
expect(material.elevation, 4.0);
......@@ -2539,13 +3434,56 @@ void main() {
selectedShadowColor: Colors.blue,
);
await tester.pumpWidget(buildChip(chipTheme));
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 {
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;
await tester.pumpWidget(
wrapForChip(
......@@ -2600,10 +3538,10 @@ void main() {
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(
wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: const FilterChip(
avatar: CircleAvatar(child: Text('t')),
label: Text('test'),
......@@ -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 {
// Regression test for https://github.com/flutter/flutter/issues/28646
await tester.pumpWidget(
......@@ -2732,7 +3697,7 @@ void main() {
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();
addTearDown(focusNode.dispose);
......@@ -2745,7 +3710,85 @@ void main() {
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(
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)) {
sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) {
......@@ -2757,7 +3800,84 @@ void main() {
} 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(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);
}
......@@ -2771,7 +3891,7 @@ void main() {
label: const Text('Chip'),
selected: selected,
onSelected: enabled ? (_) {} : null,
side: _MaterialStateBorderSide(getBorderSide),
side: MaterialStateBorderSide.resolveWith(getBorderSide),
),
),
),
......@@ -2784,13 +3904,91 @@ void main() {
// Selected.
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.
final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus();
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor));
expect(find.byType(RawChip), paints..drrect(color: focusedColor));
// Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip));
......@@ -2800,20 +3998,20 @@ void main() {
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor));
expect(find.byType(RawChip), paints..drrect(color: hoverColor));
// Pressed.
await gesture.down(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor));
expect(find.byType(RawChip), paints..drrect(color: pressedColor));
// Disabled.
await tester.pumpWidget(chipWidget(enabled: false));
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();
addTearDown(focusNode.dispose);
......@@ -2821,12 +4019,13 @@ void main() {
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;
const Color fallbackThemeColor = Color(0x00000007);
const BorderSide defaultBorderSide = BorderSide(color: fallbackThemeColor, width: 10.0);
BorderSide? getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) {
......@@ -2836,9 +4035,8 @@ void main() {
} else if (states.contains(MaterialState.focused)) {
sideColor = focusedColor;
} else if (states.contains(MaterialState.selected)) {
sideColor = selectedColor;
return null;
}
return BorderSide(color: sideColor);
}
......@@ -2848,11 +4046,16 @@ void main() {
home: Scaffold(
body: Focus(
focusNode: focusNode,
child: ChoiceChip(
label: const Text('Chip'),
selected: selected,
onSelected: enabled ? (_) {} : null,
side: MaterialStateBorderSide.resolveWith(getBorderSide),
child: ChipTheme(
data: ThemeData.light().chipTheme.copyWith(
side: defaultBorderSide,
),
child: ChoiceChip(
label: const Text('Chip'),
selected: selected,
onSelected: enabled ? (_) {} : null,
side: MaterialStateBorderSide.resolveWith(getBorderSide),
),
),
),
),
......@@ -2865,7 +4068,9 @@ void main() {
// Selected.
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.
final FocusNode chipFocusNode = focusNode.children.first;
......@@ -2892,10 +4097,9 @@ void main() {
await tester.pumpWidget(chipWidget(enabled: false));
await tester.pumpAndSettle();
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();
addTearDown(focusNode.dispose);
......@@ -2910,7 +4114,6 @@ void main() {
BorderSide? getBorderSide(Set<MaterialState> states) {
Color sideColor = defaultColor;
if (states.contains(MaterialState.disabled)) {
sideColor = disabledColor;
} else if (states.contains(MaterialState.pressed)) {
......@@ -2922,13 +4125,11 @@ void main() {
} else if (states.contains(MaterialState.selected)) {
return null;
}
return BorderSide(color: sideColor);
}
Widget chipWidget({ bool enabled = true, bool selected = false }) {
return MaterialApp(
theme: ThemeData(useMaterial3: false),
home: Scaffold(
body: Focus(
focusNode: focusNode,
......@@ -2950,19 +4151,19 @@ void main() {
// Default, not disabled.
await tester.pumpWidget(chipWidget());
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
expect(find.byType(RawChip), paints..drrect(color: defaultColor));
// Selected.
await tester.pumpWidget(chipWidget(selected: true));
// Because the resolver returns `null` for this value, we should fall back
// to the theme
expect(find.byType(RawChip), paints..rrect()..rrect(color: fallbackThemeColor));
expect(find.byType(RawChip), paints..drrect(color: fallbackThemeColor));
// Focused.
final FocusNode chipFocusNode = focusNode.children.first;
chipFocusNode.requestFocus();
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor));
expect(find.byType(RawChip), paints..drrect(color: focusedColor));
// Hovered.
final Offset center = tester.getCenter(find.byType(ChoiceChip));
......@@ -2972,24 +4173,24 @@ void main() {
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor));
expect(find.byType(RawChip), paints..drrect(color: hoverColor));
// Pressed.
await gesture.down(center);
await tester.pumpAndSettle();
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor));
expect(find.byType(RawChip), paints..drrect(color: pressedColor));
// Disabled.
await tester.pumpWidget(chipWidget(enabled: false));
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();
addTearDown(focusNode.dispose);
OutlinedBorder? getShape(Set<MaterialState> states) {
OutlinedBorder? getShape(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return const BeveledRectangleBorder();
} else if (states.contains(MaterialState.pressed)) {
......@@ -3001,7 +4202,6 @@ void main() {
} else if (states.contains(MaterialState.selected)) {
return const BeveledRectangleBorder();
}
return null;
}
......@@ -3057,7 +4257,77 @@ void main() {
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 selectedShape = RoundedRectangleBorder();
const BorderSide themeBorderSide = BorderSide(color: Color(0x00000001));
......@@ -3109,7 +4379,58 @@ void main() {
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 textKey = Key('test text');
const Key iconKey = Key('test icon');
......@@ -3215,6 +4536,121 @@ void main() {
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 {
final UniqueKey deleteButtonKey = UniqueKey();
await tester.pumpWidget(
......@@ -3307,12 +4743,12 @@ void main() {
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 OutlinedBorder shape = ContinuousRectangleBorder();
await tester.pumpWidget(wrapForChip(
useMaterial3: false,
theme: ThemeData(useMaterial3: false),
child: const RawChip(
label: Text('text'),
backgroundColor: backgroundColor,
......@@ -3329,6 +4765,29 @@ void main() {
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 {
final FocusNode focusNode = FocusNode(debugLabel: 'RawChip');
addTearDown(focusNode.dispose);
......@@ -3365,7 +4824,6 @@ void main() {
const Color selectedColor = Color(0xffff0000);
Widget buildApp({ required bool enabled, required bool selected }) {
return wrapForChip(
useMaterial3: true,
child: RawChip(
isEnabled: enabled,
selected: selected,
......@@ -3420,7 +4878,6 @@ void main() {
const Color selectedColor = Color(0xffff0000);
Widget buildApp({ required bool enabled, required bool selected }) {
return wrapForChip(
useMaterial3: true,
child: RawChip(
isEnabled: enabled,
selected: selected,
......@@ -3497,7 +4954,7 @@ void main() {
// 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 {
final ThemeData theme = ThemeData(useMaterial3: true);
final ThemeData theme = ThemeData();
await tester.pumpWidget(
MaterialApp(
theme: theme,
......@@ -3523,7 +4980,6 @@ void main() {
testWidgets("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async {
Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) {
return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: Material(
child: Center(
child: RawChip(
......@@ -3577,7 +5033,6 @@ void main() {
testWidgets('Material3 - Chip.iconTheme respects default iconTheme.size', (WidgetTester tester) async {
Widget buildChip({ IconThemeData? iconTheme }) {
return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: Directionality(
textDirection: TextDirection.ltr,
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