Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

742 lines
25 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
library;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
/// Adds the basic requirements for a Chip.
Widget wrapForChip({
required Widget child,
TextDirection textDirection = TextDirection.ltr,
double textScaleFactor = 1.0,
ThemeData? theme,
}) {
return MaterialApp(
theme: theme,
home: Directionality(
textDirection: textDirection,
child: MediaQuery.withClampedTextScaling(
minScaleFactor: textScaleFactor,
maxScaleFactor: textScaleFactor,
child: Material(child: child),
),
),
);
}
Widget selectedInputChip({Color? checkmarkColor, bool enabled = false}) {
return InputChip(
label: const Text('InputChip'),
selected: true,
isEnabled: enabled,
// When [enabled] is true we also need to provide one of the chip
// callbacks, otherwise the chip would have a 'disabled'
// [WidgetState], which is not the intention.
onSelected: enabled ? (_) {} : null,
showCheckmark: true,
checkmarkColor: checkmarkColor,
);
}
Future<void> pumpCheckmarkChip(
WidgetTester tester, {
required Widget chip,
Color? themeColor,
ThemeData? theme,
}) async {
await tester.pumpWidget(
wrapForChip(
theme: theme,
child: Builder(
builder: (BuildContext context) {
final ChipThemeData chipTheme = ChipTheme.of(context);
return ChipTheme(
data: themeColor == null ? chipTheme : chipTheme.copyWith(checkmarkColor: themeColor),
child: chip,
);
},
),
),
);
}
void expectCheckmarkColor(Finder finder, Color color) {
expect(
finder,
paints
// Physical model layer path
..path()
// The first layer that is painted is the selection overlay. We do not care
// how it is painted but it has to be added it to this pattern so that the
// check mark can be checked next.
..rrect()
// The second layer that is painted is the check mark.
..path(color: color),
);
}
RenderBox getMaterialBox(WidgetTester tester) {
return tester.firstRenderObject<RenderBox>(
find.descendant(of: find.byType(InputChip), matching: find.byType(CustomPaint)),
);
}
Material getMaterial(WidgetTester tester) {
return tester.widget<Material>(
find.descendant(of: find.byType(InputChip), matching: find.byType(Material)),
);
}
IconThemeData getIconData(WidgetTester tester) {
final IconTheme iconTheme = tester.firstWidget(
find.descendant(of: find.byType(RawChip), matching: find.byType(IconTheme)),
);
return iconTheme.data;
}
void checkChipMaterialClipBehavior(WidgetTester tester, Clip clipBehavior) {
final Iterable<Material> materials = tester.widgetList<Material>(find.byType(Material));
// There should be two Material widgets, first Material is from the "_wrapForChip" and
// last Material is from the "RawChip".
expect(materials.length, 2);
// The last Material from `RawChip` should have the clip behavior.
expect(materials.last.clipBehavior, clipBehavior);
}
// Finds any container of a tooltip.
Finder findTooltipContainer(String tooltipText) {
return find.ancestor(of: find.text(tooltipText), matching: find.byType(Container));
}
void main() {
testWidgets('InputChip.color resolves material states', (WidgetTester tester) async {
const disabledSelectedColor = Color(0xffffff00);
const disabledColor = Color(0xff00ff00);
const backgroundColor = Color(0xff0000ff);
const selectedColor = Color(0xffff0000);
Widget buildApp({required bool enabled, required bool selected}) {
return wrapForChip(
child: InputChip(
onSelected: enabled ? (bool value) {} : null,
selected: selected,
color: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled) && states.contains(WidgetState.selected)) {
return disabledSelectedColor;
}
if (states.contains(WidgetState.disabled)) {
return disabledColor;
}
if (states.contains(WidgetState.selected)) {
return selectedColor;
}
return backgroundColor;
}),
label: const Text('InputChip'),
),
);
}
// Test enabled chip.
await tester.pumpWidget(buildApp(enabled: true, selected: false));
// Enabled chip should have the provided backgroundColor.
expect(getMaterialBox(tester), paints..rrect(color: backgroundColor));
// Test disabled chip.
await tester.pumpWidget(buildApp(enabled: false, selected: false));
await tester.pumpAndSettle();
// Disabled chip should have the provided disabledColor.
expect(getMaterialBox(tester), paints..rrect(color: disabledColor));
// Test enabled & selected chip.
await tester.pumpWidget(buildApp(enabled: true, selected: true));
await tester.pumpAndSettle();
// Enabled & selected chip should have the provided selectedColor.
expect(getMaterialBox(tester), paints..rrect(color: selectedColor));
// Test disabled & selected chip.
await tester.pumpWidget(buildApp(enabled: false, selected: true));
await tester.pumpAndSettle();
// Disabled & selected chip should have the provided disabledSelectedColor.
expect(getMaterialBox(tester), paints..rrect(color: disabledSelectedColor));
});
testWidgets('InputChip uses provided state color properties', (WidgetTester tester) async {
const disabledColor = Color(0xff00ff00);
const backgroundColor = Color(0xff0000ff);
const selectedColor = Color(0xffff0000);
Widget buildApp({required bool enabled, required bool selected}) {
return wrapForChip(
child: InputChip(
onSelected: enabled ? (bool value) {} : null,
selected: selected,
disabledColor: disabledColor,
backgroundColor: backgroundColor,
selectedColor: selectedColor,
label: const Text('InputChip'),
),
);
}
// Test enabled chip.
await tester.pumpWidget(buildApp(enabled: true, selected: false));
// Enabled chip should have the provided backgroundColor.
expect(getMaterialBox(tester), paints..rrect(color: backgroundColor));
// Test disabled chip.
await tester.pumpWidget(buildApp(enabled: false, selected: false));
await tester.pumpAndSettle();
// Disabled chip should have the provided disabledColor.
expect(getMaterialBox(tester), paints..rrect(color: disabledColor));
// Test enabled & selected chip.
await tester.pumpWidget(buildApp(enabled: true, selected: true));
await tester.pumpAndSettle();
// Enabled & selected chip should have the provided selectedColor.
expect(getMaterialBox(tester), paints..rrect(color: selectedColor));
});
testWidgets('InputChip can be tapped', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(child: InputChip(label: Text('input chip'))),
),
);
await tester.tap(find.byType(InputChip));
expect(tester.takeException(), null);
});
testWidgets('loses focus when disabled', (WidgetTester tester) async {
final focusNode = FocusNode(debugLabel: 'InputChip');
await tester.pumpWidget(
wrapForChip(
child: InputChip(
focusNode: focusNode,
autofocus: true,
shape: const RoundedRectangleBorder(),
avatar: const CircleAvatar(child: Text('A')),
label: const Text('Chip A'),
onPressed: () {},
),
),
);
await tester.pump();
expect(focusNode.hasPrimaryFocus, isTrue);
await tester.pumpWidget(
wrapForChip(
child: InputChip(
focusNode: focusNode,
autofocus: true,
shape: const RoundedRectangleBorder(),
avatar: const CircleAvatar(child: Text('A')),
label: const Text('Chip A'),
),
),
);
await tester.pump();
expect(focusNode.hasPrimaryFocus, isFalse);
focusNode.dispose();
});
testWidgets('cannot be traversed to when disabled', (WidgetTester tester) async {
final focusNode1 = FocusNode(debugLabel: 'InputChip 1');
final focusNode2 = FocusNode(debugLabel: 'InputChip 2');
await tester.pumpWidget(
wrapForChip(
child: FocusScope(
child: Column(
children: <Widget>[
InputChip(
focusNode: focusNode1,
autofocus: true,
label: const Text('Chip A'),
onPressed: () {},
),
InputChip(focusNode: focusNode2, autofocus: true, label: const Text('Chip B')),
],
),
),
),
);
await tester.pump();
expect(focusNode1.hasPrimaryFocus, isTrue);
expect(focusNode2.hasPrimaryFocus, isFalse);
expect(focusNode1.nextFocus(), isFalse);
await tester.pump();
expect(focusNode1.hasPrimaryFocus, isTrue);
expect(focusNode2.hasPrimaryFocus, isFalse);
focusNode1.dispose();
focusNode2.dispose();
});
testWidgets(
'Material2 - Input chip disabled check mark color is determined by platform brightness when light',
(WidgetTester tester) async {
await pumpCheckmarkChip(
tester,
chip: selectedInputChip(),
theme: ThemeData(useMaterial3: false),
);
expectCheckmarkColor(find.byType(InputChip), Colors.black.withAlpha(0xde));
},
);
testWidgets(
'Material3 - Input chip disabled check mark color is determined by platform brightness when light',
(WidgetTester tester) async {
final theme = ThemeData();
await pumpCheckmarkChip(tester, chip: selectedInputChip(), theme: theme);
expectCheckmarkColor(find.byType(InputChip), theme.colorScheme.onSurface);
},
);
testWidgets(
'Material2 - Input chip disabled check mark color is determined by platform brightness when dark',
(WidgetTester tester) async {
await pumpCheckmarkChip(
tester,
chip: selectedInputChip(),
theme: ThemeData.dark(useMaterial3: false),
);
expectCheckmarkColor(find.byType(InputChip), Colors.white.withAlpha(0xde));
},
);
testWidgets(
'Material3 - Input chip disabled check mark color is determined by platform brightness when dark',
(WidgetTester tester) async {
final theme = ThemeData.dark();
await pumpCheckmarkChip(tester, chip: selectedInputChip(), theme: theme);
expectCheckmarkColor(find.byType(InputChip), theme.colorScheme.onSurface);
},
);
testWidgets('Input chip check mark color can be set by the chip theme', (
WidgetTester tester,
) async {
await pumpCheckmarkChip(tester, chip: selectedInputChip(), themeColor: const Color(0xff00ff00));
expectCheckmarkColor(find.byType(InputChip), const Color(0xff00ff00));
});
testWidgets('Input chip check mark color can be set by the chip constructor', (
WidgetTester tester,
) async {
await pumpCheckmarkChip(
tester,
chip: selectedInputChip(checkmarkColor: const Color(0xff00ff00)),
);
expectCheckmarkColor(find.byType(InputChip), const Color(0xff00ff00));
});
testWidgets(
'Input chip check mark color is set by chip constructor even when a theme color is specified',
(WidgetTester tester) async {
await pumpCheckmarkChip(
tester,
chip: selectedInputChip(checkmarkColor: const Color(0xffff0000)),
themeColor: const Color(0xff00ff00),
);
expectCheckmarkColor(find.byType(InputChip), const Color(0xffff0000));
},
);
testWidgets('InputChip clipBehavior properly passes through to the Material', (
WidgetTester tester,
) async {
const label = Text('label');
await tester.pumpWidget(wrapForChip(child: const InputChip(label: label)));
checkChipMaterialClipBehavior(tester, Clip.none);
await tester.pumpWidget(
wrapForChip(
child: const InputChip(label: label, clipBehavior: Clip.antiAlias),
),
);
checkChipMaterialClipBehavior(tester, Clip.antiAlias);
});
testWidgets('Material3 - Input chip has correct selected color when enabled', (
WidgetTester tester,
) async {
final theme = ThemeData();
await pumpCheckmarkChip(tester, chip: selectedInputChip(enabled: true), theme: theme);
final RenderBox materialBox = getMaterialBox(tester);
expect(materialBox, paints..rrect(color: theme.colorScheme.secondaryContainer));
});
testWidgets('Material3 - Input chip has correct selected color when disabled', (
WidgetTester tester,
) async {
final theme = ThemeData();
await pumpCheckmarkChip(tester, chip: selectedInputChip(), theme: theme);
final RenderBox materialBox = getMaterialBox(tester);
expect(materialBox, paints..path(color: theme.colorScheme.onSurface));
});
testWidgets('InputChip uses provided iconTheme', (WidgetTester tester) async {
final theme = ThemeData();
Widget buildChip({IconThemeData? iconTheme}) {
return MaterialApp(
theme: theme,
home: Material(
child: InputChip(
iconTheme: iconTheme,
avatar: const Icon(Icons.add),
label: const Text('Test'),
),
),
);
}
// Test default icon theme.
await tester.pumpWidget(buildChip());
expect(getIconData(tester).color, theme.colorScheme.onSurfaceVariant);
// Test provided icon theme.
await tester.pumpWidget(buildChip(iconTheme: const IconThemeData(color: Color(0xff00ff00))));
expect(getIconData(tester).color, const Color(0xff00ff00));
});
testWidgets('Delete button is visible on disabled InputChip', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
child: InputChip(isEnabled: false, label: const Text('Label'), onDeleted: () {}),
),
);
// Delete button should be visible.
await expectLater(
find.byType(RawChip),
matchesGoldenFile('input_chip.disabled.delete_button.png'),
);
});
testWidgets('Delete button tooltip is not shown on disabled InputChip', (
WidgetTester tester,
) async {
Widget buildChip({bool enabled = true}) {
return wrapForChip(
child: InputChip(isEnabled: enabled, label: const Text('Label'), onDeleted: () {}),
);
}
// Test enabled chip.
await tester.pumpWidget(buildChip());
final Offset deleteButtonLocation = tester.getCenter(find.byType(Icon));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.moveTo(deleteButtonLocation);
await tester.pump();
// Delete button tooltip should be visible.
expect(findTooltipContainer('Delete'), findsOneWidget);
// Test disabled chip.
await tester.pumpWidget(buildChip(enabled: false));
await tester.pump();
// Delete button tooltip should not be visible.
expect(findTooltipContainer('Delete'), findsNothing);
});
testWidgets('InputChip avatar layout constraints can be customized', (WidgetTester tester) async {
const border = 1.0;
const iconSize = 18.0;
const labelPadding = 8.0;
const padding = 8.0;
const labelSize = Size(100, 100);
Widget buildChip({BoxConstraints? avatarBoxConstraints}) {
return wrapForChip(
child: Center(
child: InputChip(
avatarBoxConstraints: avatarBoxConstraints,
avatar: const Icon(Icons.favorite),
label: Container(
width: labelSize.width,
height: labelSize.width,
color: const Color(0xFFFF0000),
),
),
),
);
}
// Test default avatar layout constraints.
await tester.pumpWidget(buildChip());
expect(tester.getSize(find.byType(InputChip)).width, equals(234.0));
expect(tester.getSize(find.byType(InputChip)).height, equals(118.0));
// Calculate the distance between avatar and chip edges.
Offset chipTopLeft = tester.getTopLeft(find.byWidget(getMaterial(tester)));
final Offset avatarCenter = tester.getCenter(find.byIcon(Icons.favorite));
expect(chipTopLeft.dx, avatarCenter.dx - (labelSize.width / 2) - padding - border);
expect(chipTopLeft.dy, avatarCenter.dy - (labelSize.width / 2) - padding - border);
// Calculate the distance between avatar and label.
Offset labelTopLeft = tester.getTopLeft(find.byType(Container));
expect(labelTopLeft.dx, avatarCenter.dx + (labelSize.width / 2) + labelPadding);
// Test custom avatar layout constraints.
await tester.pumpWidget(buildChip(avatarBoxConstraints: const BoxConstraints.tightForFinite()));
await tester.pump();
expect(tester.getSize(find.byType(InputChip)).width, equals(152.0));
expect(tester.getSize(find.byType(InputChip)).height, equals(118.0));
// Calculate the distance between avatar and chip edges.
chipTopLeft = tester.getTopLeft(find.byWidget(getMaterial(tester)));
expect(chipTopLeft.dx, avatarCenter.dx - (iconSize / 2) - padding - border);
expect(chipTopLeft.dy, avatarCenter.dy - (labelSize.width / 2) - padding - border);
// Calculate the distance between avatar and label.
labelTopLeft = tester.getTopLeft(find.byType(Container));
expect(labelTopLeft.dx, avatarCenter.dx + (iconSize / 2) + labelPadding);
});
testWidgets('InputChip delete icon layout constraints can be customized', (
WidgetTester tester,
) async {
const border = 1.0;
const iconSize = 18.0;
const labelPadding = 8.0;
const padding = 8.0;
const labelSize = Size(100, 100);
Widget buildChip({BoxConstraints? deleteIconBoxConstraints}) {
return wrapForChip(
child: Center(
child: InputChip(
deleteIconBoxConstraints: deleteIconBoxConstraints,
onDeleted: () {},
label: Container(
width: labelSize.width,
height: labelSize.width,
color: const Color(0xFFFF0000),
),
),
),
);
}
// Test default delete icon layout constraints.
await tester.pumpWidget(buildChip());
expect(tester.getSize(find.byType(InputChip)).width, equals(234.0));
expect(tester.getSize(find.byType(InputChip)).height, equals(118.0));
// Calculate the distance between delete icon and chip edges.
Offset chipTopRight = tester.getTopRight(find.byWidget(getMaterial(tester)));
final Offset deleteIconCenter = tester.getCenter(find.byIcon(Icons.clear));
expect(chipTopRight.dx, deleteIconCenter.dx + (labelSize.width / 2) + padding + border);
expect(chipTopRight.dy, deleteIconCenter.dy - (labelSize.width / 2) - padding - border);
// Calculate the distance between delete icon and label.
Offset labelTopRight = tester.getTopRight(find.byType(Container));
expect(labelTopRight.dx, deleteIconCenter.dx - (labelSize.width / 2) - labelPadding);
// Test custom avatar layout constraints.
await tester.pumpWidget(
buildChip(deleteIconBoxConstraints: const BoxConstraints.tightForFinite()),
);
await tester.pump();
expect(tester.getSize(find.byType(InputChip)).width, equals(152.0));
expect(tester.getSize(find.byType(InputChip)).height, equals(118.0));
// Calculate the distance between delete icon and chip edges.
chipTopRight = tester.getTopRight(find.byWidget(getMaterial(tester)));
expect(chipTopRight.dx, deleteIconCenter.dx + (iconSize / 2) + padding + border);
expect(chipTopRight.dy, deleteIconCenter.dy - (labelSize.width / 2) - padding - border);
// Calculate the distance between delete icon and label.
labelTopRight = tester.getTopRight(find.byType(Container));
expect(labelTopRight.dx, deleteIconCenter.dx - (iconSize / 2) - labelPadding);
});
testWidgets('InputChip.chipAnimationStyle is passed to RawChip', (WidgetTester tester) async {
final chipAnimationStyle = ChipAnimationStyle(
enableAnimation: const AnimationStyle(duration: Durations.short2),
selectAnimation: AnimationStyle.noAnimation,
);
await tester.pumpWidget(
wrapForChip(
child: Center(
child: InputChip(chipAnimationStyle: chipAnimationStyle, label: const Text('InputChip')),
),
),
);
expect(tester.widget<RawChip>(find.byType(RawChip)).chipAnimationStyle, chipAnimationStyle);
});
testWidgets('InputChip has expected default mouse cursor on hover', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
child: Center(
child: InputChip(label: const Text('Chip'), onPressed: () {}),
),
),
);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: const Offset(10, 10));
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.basic,
);
final Offset chip = tester.getCenter(find.text('Chip'));
await gesture.moveTo(chip);
await tester.pump();
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic,
);
});
testWidgets('InputChip mouse cursor behavior', (WidgetTester tester) async {
const SystemMouseCursor customCursor = SystemMouseCursors.grab;
await tester.pumpWidget(
wrapForChip(
child: const Center(
child: InputChip(mouseCursor: customCursor, label: Text('Chip')),
),
),
);
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
pointer: 1,
);
await gesture.addPointer(location: const Offset(10, 10));
await tester.pump();
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.basic,
);
final Offset chip = tester.getCenter(find.text('Chip'));
await gesture.moveTo(chip);
await tester.pump();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), customCursor);
});
testWidgets('Mouse cursor resolves in focused/unfocused/disabled states', (
WidgetTester tester,
) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final focusNode = FocusNode(debugLabel: 'Chip');
addTearDown(focusNode.dispose);
Widget buildChip({required bool enabled}) {
return wrapForChip(
child: Center(
child: InputChip(
mouseCursor: const WidgetStateMouseCursor.fromMap(<WidgetStatesConstraint, MouseCursor>{
WidgetState.disabled: SystemMouseCursors.forbidden,
WidgetState.focused: SystemMouseCursors.grab,
WidgetState.selected: SystemMouseCursors.click,
WidgetState.any: SystemMouseCursors.basic,
}),
focusNode: focusNode,
label: const Text('Chip'),
onSelected: enabled ? (bool value) {} : null,
),
),
);
}
// Unfocused case.
await tester.pumpWidget(buildChip(enabled: true));
final TestGesture gesture1 = await tester.createGesture(
kind: PointerDeviceKind.mouse,
pointer: 1,
);
addTearDown(gesture1.removePointer);
await gesture1.addPointer(location: tester.getCenter(find.text('Chip')));
await tester.pump();
await gesture1.moveTo(tester.getCenter(find.text('Chip')));
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.basic,
);
// Focused case.
focusNode.requestFocus();
await tester.pump();
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.grab,
);
// Disabled case.
await tester.pumpWidget(buildChip(enabled: false));
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.forbidden,
);
});
testWidgets('InputChip does not crash at zero area', (WidgetTester tester) async {
Future<void> testChip(Widget chip) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(child: SizedBox.shrink(child: chip)),
),
),
);
expect(tester.getSize(find.byType(InputChip)), Size.zero);
}
await testChip(const InputChip(label: Text('X')));
await testChip(
const InputChip(
label: Text('X'),
avatar: CircleAvatar(child: Text('A')),
),
);
await testChip(InputChip(label: const Text('X'), onDeleted: () {}));
});
}