flutter_flutter/packages/flutter/test/material/choice_chip_test.dart
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

895 lines
30 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.
import 'dart:ui';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
RenderBox getMaterialBox(WidgetTester tester, Finder type) {
return tester.firstRenderObject<RenderBox>(
find.descendant(of: type, matching: find.byType(CustomPaint)),
);
}
Material getMaterial(WidgetTester tester) {
return tester.widget<Material>(
find.descendant(of: find.byType(ChoiceChip), 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;
}
DefaultTextStyle getLabelStyle(WidgetTester tester, String labelText) {
return tester.widget(
find.ancestor(of: find.text(labelText), matching: find.byType(DefaultTextStyle)).first,
);
}
/// Adds the basic requirements for a Chip.
Widget wrapForChip({
required Widget child,
TextDirection textDirection = TextDirection.ltr,
TextScaler textScaler = TextScaler.noScaling,
Brightness brightness = Brightness.light,
bool? useMaterial3,
}) {
return MaterialApp(
theme: ThemeData(brightness: brightness, useMaterial3: useMaterial3),
home: Directionality(
textDirection: textDirection,
child: MediaQuery(
data: MediaQueryData(textScaler: textScaler),
child: Material(child: child),
),
),
);
}
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);
}
void main() {
testWidgets('Material2 - ChoiceChip defaults', (WidgetTester tester) async {
final theme = ThemeData(useMaterial3: false);
const label = 'choice chip';
// Test enabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: Center(
child: ChoiceChip(
selected: false,
onSelected: (bool valueChanged) {},
label: const Text(label),
),
),
),
),
);
// Test default chip size.
expect(tester.getSize(find.byType(ChoiceChip)), const Size(178.0, 48.0));
// Test default label style.
expect(
getLabelStyle(tester, label).style.color,
theme.textTheme.bodyLarge!.color!.withAlpha(0xde),
);
Material chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, Colors.black);
expect(chipMaterial.shape, const StadiumBorder());
var decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, Colors.black.withAlpha(0x1f));
// Test disabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: const Material(child: ChoiceChip(selected: false, label: Text(label))),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, Colors.black);
expect(chipMaterial.shape, const StadiumBorder());
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, Colors.black38);
// Test selected enabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: ChoiceChip(
selected: true,
onSelected: (bool valueChanged) {},
label: const Text(label),
),
),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, Colors.black);
expect(chipMaterial.shape, const StadiumBorder());
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, Colors.black.withAlpha(0x3d));
// Test selected disabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: const Material(child: ChoiceChip(selected: true, label: Text(label))),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, Colors.black);
expect(chipMaterial.shape, const StadiumBorder());
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, Colors.black.withAlpha(0x3d));
});
testWidgets('Material3 - ChoiceChip defaults', (WidgetTester tester) async {
final theme = ThemeData();
const label = 'choice chip';
// Test enabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: Center(
child: ChoiceChip(
selected: false,
onSelected: (bool valueChanged) {},
label: const Text(label),
),
),
),
),
);
// Test default chip size.
expect(
tester.getSize(find.byType(ChoiceChip)),
within(distance: 0.01, from: const Size(189.1, 48.0)),
);
// Test default label style.
expect(
getLabelStyle(tester, label).style.color!.value,
theme.colorScheme.onSurfaceVariant.value,
);
Material chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, Colors.transparent);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: theme.colorScheme.outlineVariant),
),
);
var decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, null);
// Test disabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: const Material(child: ChoiceChip(selected: false, label: Text(label))),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, Colors.transparent);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: theme.colorScheme.onSurface.withOpacity(0.12)),
),
);
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, null);
// Test selected enabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: ChoiceChip(
selected: true,
onSelected: (bool valueChanged) {},
label: const Text(label),
),
),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, null);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: Colors.transparent),
),
);
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, theme.colorScheme.secondaryContainer);
// Test selected disabled ChoiceChip defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: const Material(child: ChoiceChip(selected: true, label: Text(label))),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, null);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: Colors.transparent),
),
);
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, theme.colorScheme.onSurface.withOpacity(0.12));
});
testWidgets('Material3 - ChoiceChip.elevated defaults', (WidgetTester tester) async {
final theme = ThemeData();
const label = 'choice chip';
// Test enabled ChoiceChip.elevated defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: Center(
child: ChoiceChip.elevated(
selected: false,
onSelected: (bool valueChanged) {},
label: const Text(label),
),
),
),
),
);
// Test default chip size.
expect(
tester.getSize(find.byType(ChoiceChip)),
within(distance: 0.01, from: const Size(189.1, 48.0)),
);
// Test default label style.
expect(
getLabelStyle(tester, label).style.color!.value,
theme.colorScheme.onSurfaceVariant.value,
);
Material chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 1);
expect(chipMaterial.shadowColor, theme.colorScheme.shadow);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: Colors.transparent),
),
);
var decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, theme.colorScheme.surfaceContainerLow);
// Test disabled ChoiceChip.elevated defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: const Material(child: ChoiceChip.elevated(selected: false, label: Text(label))),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, theme.colorScheme.shadow);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: Colors.transparent),
),
);
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, theme.colorScheme.onSurface.withOpacity(0.12));
// Test selected enabled ChoiceChip.elevated defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: Material(
child: ChoiceChip.elevated(
selected: true,
onSelected: (bool valueChanged) {},
label: const Text(label),
),
),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 1);
expect(chipMaterial.shadowColor, null);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: Colors.transparent),
),
);
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, theme.colorScheme.secondaryContainer);
// Test selected disabled ChoiceChip.elevated defaults.
await tester.pumpWidget(
MaterialApp(
theme: theme,
home: const Material(child: ChoiceChip.elevated(selected: false, label: Text(label))),
),
);
await tester.pumpAndSettle();
chipMaterial = getMaterial(tester);
expect(chipMaterial.elevation, 0);
expect(chipMaterial.shadowColor, theme.colorScheme.shadow);
expect(chipMaterial.surfaceTintColor, Colors.transparent);
expect(
chipMaterial.shape,
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
side: BorderSide(color: Colors.transparent),
),
);
decoration = tester.widget<Ink>(find.byType(Ink)).decoration! as ShapeDecoration;
expect(decoration.color, theme.colorScheme.onSurface.withOpacity(0.12));
});
testWidgets('ChoiceChip.color resolves material states', (WidgetTester tester) async {
const disabledSelectedColor = Color(0xffffff00);
const disabledColor = Color(0xff00ff00);
const backgroundColor = Color(0xff0000ff);
const selectedColor = Color(0xffff0000);
final WidgetStateProperty<Color?> 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;
});
Widget buildApp({required bool enabled, required bool selected}) {
return wrapForChip(
child: Column(
children: <Widget>[
ChoiceChip(
onSelected: enabled ? (bool value) {} : null,
selected: selected,
color: color,
label: const Text('ChoiceChip'),
),
ChoiceChip.elevated(
onSelected: enabled ? (bool value) {} : null,
selected: selected,
color: color,
label: const Text('ChoiceChip.elevated'),
),
],
),
);
}
// Test enabled state.
await tester.pumpWidget(buildApp(enabled: true, selected: false));
// Enabled ChoiceChip should have the provided backgroundColor.
expect(
getMaterialBox(tester, find.byType(RawChip).first),
paints..rrect(color: backgroundColor),
);
// Enabled elevated ChoiceChip should have the provided backgroundColor.
expect(
getMaterialBox(tester, find.byType(RawChip).last),
paints..rrect(color: backgroundColor),
);
// Test disabled state.
await tester.pumpWidget(buildApp(enabled: false, selected: false));
await tester.pumpAndSettle();
// Disabled ChoiceChip should have the provided disabledColor.
expect(getMaterialBox(tester, find.byType(RawChip).first), paints..rrect(color: disabledColor));
// Disabled elevated ChoiceChip should have the provided disabledColor.
expect(getMaterialBox(tester, find.byType(RawChip).last), paints..rrect(color: disabledColor));
// Test enabled & selected state.
await tester.pumpWidget(buildApp(enabled: true, selected: true));
await tester.pumpAndSettle();
// Enabled & selected ChoiceChip should have the provided selectedColor.
expect(getMaterialBox(tester, find.byType(RawChip).first), paints..rrect(color: selectedColor));
// Enabled & selected elevated ChoiceChip should have the provided selectedColor.
expect(getMaterialBox(tester, find.byType(RawChip).last), paints..rrect(color: selectedColor));
// Test disabled & selected state.
await tester.pumpWidget(buildApp(enabled: false, selected: true));
await tester.pumpAndSettle();
// Disabled & selected ChoiceChip should have the provided disabledSelectedColor.
expect(
getMaterialBox(tester, find.byType(RawChip).first),
paints..rrect(color: disabledSelectedColor),
);
// Disabled & selected elevated ChoiceChip should have the provided disabledSelectedColor.
expect(
getMaterialBox(tester, find.byType(RawChip).last),
paints..rrect(color: disabledSelectedColor),
);
});
testWidgets('ChoiceChip 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: Column(
children: <Widget>[
ChoiceChip(
onSelected: enabled ? (bool value) {} : null,
selected: selected,
disabledColor: disabledColor,
backgroundColor: backgroundColor,
selectedColor: selectedColor,
label: const Text('ChoiceChip'),
),
ChoiceChip.elevated(
onSelected: enabled ? (bool value) {} : null,
selected: selected,
disabledColor: disabledColor,
backgroundColor: backgroundColor,
selectedColor: selectedColor,
label: const Text('ChoiceChip.elevated'),
),
],
),
);
}
// Test enabled chips.
await tester.pumpWidget(buildApp(enabled: true, selected: false));
// Enabled ChoiceChip should have the provided backgroundColor.
expect(
getMaterialBox(tester, find.byType(RawChip).first),
paints..rrect(color: backgroundColor),
);
// Enabled elevated ChoiceChip should have the provided backgroundColor.
expect(
getMaterialBox(tester, find.byType(RawChip).last),
paints..rrect(color: backgroundColor),
);
// Test disabled chips.
await tester.pumpWidget(buildApp(enabled: false, selected: false));
await tester.pumpAndSettle();
// Disabled ChoiceChip should have the provided disabledColor.
expect(getMaterialBox(tester, find.byType(RawChip).first), paints..rrect(color: disabledColor));
// Disabled elevated ChoiceChip should have the provided disabledColor.
expect(getMaterialBox(tester, find.byType(RawChip).last), paints..rrect(color: disabledColor));
// Test enabled & selected chips.
await tester.pumpWidget(buildApp(enabled: true, selected: true));
await tester.pumpAndSettle();
// Enabled & selected ChoiceChip should have the provided selectedColor.
expect(getMaterialBox(tester, find.byType(RawChip).first), paints..rrect(color: selectedColor));
// Enabled & selected elevated ChoiceChip should have the provided selectedColor.
expect(getMaterialBox(tester, find.byType(RawChip).last), paints..rrect(color: selectedColor));
});
testWidgets('ChoiceChip can be tapped', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(child: ChoiceChip(selected: false, label: Text('choice chip'))),
),
);
await tester.tap(find.byType(ChoiceChip));
expect(tester.takeException(), null);
});
testWidgets('ChoiceChip clipBehavior properly passes through to the Material', (
WidgetTester tester,
) async {
const label = Text('label');
await tester.pumpWidget(wrapForChip(child: const ChoiceChip(label: label, selected: false)));
checkChipMaterialClipBehavior(tester, Clip.none);
await tester.pumpWidget(
wrapForChip(
child: const ChoiceChip(label: label, selected: false, clipBehavior: Clip.antiAlias),
),
);
checkChipMaterialClipBehavior(tester, Clip.antiAlias);
});
testWidgets('ChoiceChip passes iconTheme property to RawChip', (WidgetTester tester) async {
const iconTheme = IconThemeData(color: Colors.red);
await tester.pumpWidget(
wrapForChip(
child: const ChoiceChip(label: Text('Test'), selected: true, iconTheme: iconTheme),
),
);
final RawChip rawChip = tester.widget(find.byType(RawChip));
expect(rawChip.iconTheme, iconTheme);
});
testWidgets('ChoiceChip passes showCheckmark from ChipTheme to RawChip', (
WidgetTester tester,
) async {
const showCheckmark = false;
await tester.pumpWidget(
wrapForChip(
child: const ChipTheme(
data: ChipThemeData(showCheckmark: showCheckmark),
child: ChoiceChip(label: Text('Test'), selected: true),
),
),
);
final RawChip rawChip = tester.widget(find.byType(RawChip));
expect(rawChip.showCheckmark, showCheckmark);
});
testWidgets('ChoiceChip passes checkmark properties to RawChip', (WidgetTester tester) async {
const showCheckmark = false;
const checkmarkColor = Color(0xff0000ff);
await tester.pumpWidget(
wrapForChip(
child: const ChoiceChip(
label: Text('Test'),
selected: true,
showCheckmark: showCheckmark,
checkmarkColor: checkmarkColor,
),
),
);
final RawChip rawChip = tester.widget(find.byType(RawChip));
expect(rawChip.showCheckmark, showCheckmark);
expect(rawChip.checkmarkColor, checkmarkColor);
});
testWidgets('ChoiceChip uses provided iconTheme', (WidgetTester tester) async {
final theme = ThemeData();
Widget buildChip({IconThemeData? iconTheme}) {
return MaterialApp(
theme: theme,
home: Material(
child: ChoiceChip(
iconTheme: iconTheme,
avatar: const Icon(Icons.add),
label: const Text('Test'),
selected: false,
onSelected: (bool _) {},
),
),
);
}
// Test default icon theme.
await tester.pumpWidget(buildChip());
expect(getIconData(tester).color, theme.colorScheme.primary);
// Test provided icon theme.
await tester.pumpWidget(buildChip(iconTheme: const IconThemeData(color: Color(0xff00ff00))));
expect(getIconData(tester).color, const Color(0xff00ff00));
});
testWidgets('ChoiceChip 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: ChoiceChip(
avatarBoxConstraints: avatarBoxConstraints,
avatar: const Icon(Icons.favorite),
label: Container(
width: labelSize.width,
height: labelSize.width,
color: const Color(0xFFFF0000),
),
selected: false,
),
),
);
}
// Test default avatar layout constraints.
await tester.pumpWidget(buildChip());
expect(tester.getSize(find.byType(ChoiceChip)).width, equals(234.0));
expect(tester.getSize(find.byType(ChoiceChip)).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(ChoiceChip)).width, equals(152.0));
expect(tester.getSize(find.byType(ChoiceChip)).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('ChoiceChip.chipAnimationStyle is passed to RawChip', (WidgetTester tester) async {
final chipAnimationStyle = ChipAnimationStyle(
enableAnimation: const AnimationStyle(duration: Durations.extralong4),
selectAnimation: AnimationStyle.noAnimation,
);
await tester.pumpWidget(
wrapForChip(
child: Center(
child: ChoiceChip(
chipAnimationStyle: chipAnimationStyle,
selected: true,
label: const Text('ChoiceChip'),
),
),
),
);
expect(tester.widget<RawChip>(find.byType(RawChip)).chipAnimationStyle, chipAnimationStyle);
});
testWidgets('Elevated ChoiceChip.chipAnimationStyle is passed to RawChip', (
WidgetTester tester,
) async {
final chipAnimationStyle = ChipAnimationStyle(
enableAnimation: const AnimationStyle(duration: Durations.extralong4),
selectAnimation: AnimationStyle.noAnimation,
);
await tester.pumpWidget(
wrapForChip(
child: Center(
child: ChoiceChip.elevated(
chipAnimationStyle: chipAnimationStyle,
selected: true,
label: const Text('ChoiceChip'),
),
),
),
);
expect(tester.widget<RawChip>(find.byType(RawChip)).chipAnimationStyle, chipAnimationStyle);
});
testWidgets('ChoiceChip has expected default mouse cursor on hover', (WidgetTester tester) async {
await tester.pumpWidget(
wrapForChip(
child: Center(
child: ChoiceChip(
selected: false,
label: const Text('Chip'),
onSelected: (bool value) {},
),
),
),
);
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.byType(ChoiceChip));
await gesture.moveTo(chip);
await tester.pump();
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic,
);
});
testWidgets('ChoiceChip mouse cursor behavior', (WidgetTester tester) async {
const SystemMouseCursor customCursor = SystemMouseCursors.grab;
await tester.pumpWidget(
wrapForChip(
child: const Center(
child: ChoiceChip(selected: false, 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: ChoiceChip(
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,
selected: false,
),
),
);
}
// 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('ChoiceChip renders at zero area', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Center(
child: SizedBox.shrink(
child: Scaffold(body: ChoiceChip(label: Text('X'), selected: true)),
),
),
),
);
final Finder xText = find.text('X');
expect(tester.getSize(xText).isEmpty, isTrue);
});
}