This reverts commit f8cd24d in an attempt to re-land it. There are no changes in this PR from #74373, since it only failed Google internal tests, and we think that the solution involves updating those tests instead of changing this code.
This commit is contained in:
parent
c9ccb17710
commit
7dbd586e04
@ -215,13 +215,13 @@ class Ink extends StatefulWidget {
|
|||||||
/// any [padding].
|
/// any [padding].
|
||||||
final double? height;
|
final double? height;
|
||||||
|
|
||||||
EdgeInsetsGeometry? get _paddingIncludingDecoration {
|
EdgeInsetsGeometry get _paddingIncludingDecoration {
|
||||||
if (decoration == null || decoration!.padding == null)
|
if (decoration == null || decoration!.padding == null)
|
||||||
return padding;
|
return padding ?? EdgeInsets.zero;
|
||||||
final EdgeInsetsGeometry? decorationPadding = decoration!.padding;
|
final EdgeInsetsGeometry decorationPadding = decoration!.padding!;
|
||||||
if (padding == null)
|
if (padding == null)
|
||||||
return decorationPadding;
|
return decorationPadding;
|
||||||
return padding!.add(decorationPadding!);
|
return padding!.add(decorationPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -236,6 +236,7 @@ class Ink extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _InkState extends State<Ink> {
|
class _InkState extends State<Ink> {
|
||||||
|
final GlobalKey _boxKey = GlobalKey();
|
||||||
InkDecoration? _ink;
|
InkDecoration? _ink;
|
||||||
|
|
||||||
void _handleRemoved() {
|
void _handleRemoved() {
|
||||||
@ -249,31 +250,31 @@ class _InkState extends State<Ink> {
|
|||||||
super.deactivate();
|
super.deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _build(BuildContext context, BoxConstraints constraints) {
|
Widget _build(BuildContext context) {
|
||||||
|
// By creating the InkDecoration from within a Builder widget, we can
|
||||||
|
// use the RenderBox of the Padding widget.
|
||||||
if (_ink == null) {
|
if (_ink == null) {
|
||||||
_ink = InkDecoration(
|
_ink = InkDecoration(
|
||||||
decoration: widget.decoration,
|
decoration: widget.decoration,
|
||||||
configuration: createLocalImageConfiguration(context),
|
configuration: createLocalImageConfiguration(context),
|
||||||
controller: Material.of(context)!,
|
controller: Material.of(context)!,
|
||||||
referenceBox: context.findRenderObject()! as RenderBox,
|
referenceBox: _boxKey.currentContext!.findRenderObject()! as RenderBox,
|
||||||
onRemoved: _handleRemoved,
|
onRemoved: _handleRemoved,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_ink!.decoration = widget.decoration;
|
_ink!.decoration = widget.decoration;
|
||||||
_ink!.configuration = createLocalImageConfiguration(context);
|
_ink!.configuration = createLocalImageConfiguration(context);
|
||||||
}
|
}
|
||||||
Widget? current = widget.child;
|
return widget.child ?? Container();
|
||||||
final EdgeInsetsGeometry? effectivePadding = widget._paddingIncludingDecoration;
|
|
||||||
if (effectivePadding != null)
|
|
||||||
current = Padding(padding: effectivePadding, child: current);
|
|
||||||
return current ?? Container();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(debugCheckHasMaterial(context));
|
assert(debugCheckHasMaterial(context));
|
||||||
Widget result = LayoutBuilder(
|
Widget result = Padding(
|
||||||
builder: _build,
|
key: _boxKey,
|
||||||
|
padding: widget._paddingIncludingDecoration,
|
||||||
|
child: Builder(builder: _build),
|
||||||
);
|
);
|
||||||
if (widget.width != null || widget.height != null) {
|
if (widget.width != null || widget.height != null) {
|
||||||
result = SizedBox(
|
result = SizedBox(
|
||||||
|
@ -11,6 +11,7 @@ import 'colors.dart';
|
|||||||
import 'constants.dart';
|
import 'constants.dart';
|
||||||
import 'debug.dart';
|
import 'debug.dart';
|
||||||
import 'divider.dart';
|
import 'divider.dart';
|
||||||
|
import 'ink_decoration.dart';
|
||||||
import 'ink_well.dart';
|
import 'ink_well.dart';
|
||||||
import 'material_state.dart';
|
import 'material_state.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
@ -108,7 +109,7 @@ class ListTileTheme extends InheritedTheme {
|
|||||||
final bool dense;
|
final bool dense;
|
||||||
|
|
||||||
/// {@template flutter.material.ListTileTheme.shape}
|
/// {@template flutter.material.ListTileTheme.shape}
|
||||||
/// If specified, [shape] defines the shape of the [ListTile]'s [InkWell] border.
|
/// If specified, [shape] defines the [ListTile]'s shape.
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
final ShapeBorder? shape;
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
@ -837,13 +838,12 @@ class ListTile extends StatelessWidget {
|
|||||||
/// widgets within a [Theme].
|
/// widgets within a [Theme].
|
||||||
final VisualDensity? visualDensity;
|
final VisualDensity? visualDensity;
|
||||||
|
|
||||||
/// The shape of the tile's [InkWell].
|
/// The tile's shape.
|
||||||
///
|
///
|
||||||
/// Defines the tile's [InkWell.customBorder].
|
/// Defines the tile's [InkWell.customBorder] and [Ink.decoration] shape.
|
||||||
///
|
///
|
||||||
/// If this property is null then [CardTheme.shape] of [ThemeData.cardTheme]
|
/// If this property is null then [ListTileTheme.shape] is used.
|
||||||
/// is used. If that's null then the shape will be a [RoundedRectangleBorder]
|
/// If that's null then a rectangular [Border] will be used.
|
||||||
/// with a circular corner radius of 4.0.
|
|
||||||
final ShapeBorder? shape;
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
/// The tile's internal padding.
|
/// The tile's internal padding.
|
||||||
@ -1185,8 +1185,11 @@ class ListTile extends StatelessWidget {
|
|||||||
child: Semantics(
|
child: Semantics(
|
||||||
selected: selected,
|
selected: selected,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
child: ColoredBox(
|
child: Ink(
|
||||||
color: _tileBackgroundColor(tileTheme),
|
decoration: ShapeDecoration(
|
||||||
|
shape: shape ?? tileTheme.shape ?? const Border(),
|
||||||
|
color: _tileBackgroundColor(tileTheme),
|
||||||
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
bottom: false,
|
||||||
|
@ -577,7 +577,13 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void applyPaintTransform(RenderBox child, Matrix4 transform) {
|
void applyPaintTransform(RenderBox child, Matrix4 transform) {
|
||||||
if (_keepAliveBucket.containsKey(indexOf(child))) {
|
final SliverMultiBoxAdaptorParentData childParentData = child.parentData! as SliverMultiBoxAdaptorParentData;
|
||||||
|
if (childParentData.index == null) {
|
||||||
|
// If the child has no index, such as with the prototype of a
|
||||||
|
// SliverPrototypeExtentList, then it is not visible, so we give it a
|
||||||
|
// zero transform to prevent it from painting.
|
||||||
|
transform.setZero();
|
||||||
|
} else if (_keepAliveBucket.containsKey(childParentData.index)) {
|
||||||
// It is possible that widgets under kept alive children want to paint
|
// It is possible that widgets under kept alive children want to paint
|
||||||
// themselves. For example, the Material widget tries to paint all
|
// themselves. For example, the Material widget tries to paint all
|
||||||
// InkFeatures under its subtree as long as they are not disposed. In
|
// InkFeatures under its subtree as long as they are not disposed. In
|
||||||
|
@ -134,7 +134,7 @@ void main() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final Rect paddingRect = tester.getRect(find.byType(Padding));
|
final Rect paddingRect = tester.getRect(find.byType(SafeArea));
|
||||||
final Rect checkboxRect = tester.getRect(find.byType(Checkbox));
|
final Rect checkboxRect = tester.getRect(find.byType(Checkbox));
|
||||||
final Rect titleRect = tester.getRect(find.text('Title'));
|
final Rect titleRect = tester.getRect(find.text('Title'));
|
||||||
|
|
||||||
@ -246,35 +246,34 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('CheckboxListTile respects tileColor', (WidgetTester tester) async {
|
testWidgets('CheckboxListTile respects tileColor', (WidgetTester tester) async {
|
||||||
const Color tileColor = Colors.black;
|
final Color tileColor = Colors.red.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: CheckboxListTile(
|
child: CheckboxListTile(
|
||||||
value: false,
|
value: false,
|
||||||
onChanged: null,
|
onChanged: null,
|
||||||
title: Text('Title'),
|
title: const Text('Title'),
|
||||||
tileColor: tileColor,
|
tileColor: tileColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final ColoredBox coloredBox = tester.firstWidget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: tileColor));
|
||||||
expect(coloredBox.color, equals(tileColor));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('CheckboxListTile respects selectedTileColor', (WidgetTester tester) async {
|
testWidgets('CheckboxListTile respects selectedTileColor', (WidgetTester tester) async {
|
||||||
const Color selectedTileColor = Colors.black;
|
final Color selectedTileColor = Colors.green.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: CheckboxListTile(
|
child: CheckboxListTile(
|
||||||
value: false,
|
value: false,
|
||||||
onChanged: null,
|
onChanged: null,
|
||||||
title: Text('Title'),
|
title: const Text('Title'),
|
||||||
selected: true,
|
selected: true,
|
||||||
selectedTileColor: selectedTileColor,
|
selectedTileColor: selectedTileColor,
|
||||||
),
|
),
|
||||||
@ -282,7 +281,6 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final ColoredBox coloredBox = tester.firstWidget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||||
expect(coloredBox.color, equals(selectedTileColor));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1254,7 +1254,6 @@ void main() {
|
|||||||
testWidgets('ListTile is focusable and has correct focus color', (WidgetTester tester) async {
|
testWidgets('ListTile is focusable and has correct focus color', (WidgetTester tester) async {
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'ListTile');
|
final FocusNode focusNode = FocusNode(debugLabel: 'ListTile');
|
||||||
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||||
const Key tileKey = Key('listTile');
|
|
||||||
Widget buildApp({bool enabled = true}) {
|
Widget buildApp({bool enabled = true}) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Material(
|
home: Material(
|
||||||
@ -1265,7 +1264,6 @@ void main() {
|
|||||||
height: 100,
|
height: 100,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
key: tileKey,
|
|
||||||
onTap: enabled ? () {} : null,
|
onTap: enabled ? () {} : null,
|
||||||
focusColor: Colors.orange[500],
|
focusColor: Colors.orange[500],
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
@ -1282,16 +1280,14 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||||
expect(
|
expect(
|
||||||
Material.of(tester.element(find.byKey(tileKey))),
|
find.byType(Material),
|
||||||
paints
|
paints
|
||||||
..rect(
|
..rect(
|
||||||
color: Colors.orange[500],
|
color: Colors.orange[500],
|
||||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0))
|
||||||
)
|
|
||||||
..rect(
|
..rect(
|
||||||
color: const Color(0xffffffff),
|
color: const Color(0xffffffff),
|
||||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check when the list tile is disabled.
|
// Check when the list tile is disabled.
|
||||||
@ -1299,7 +1295,7 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(focusNode.hasPrimaryFocus, isFalse);
|
expect(focusNode.hasPrimaryFocus, isFalse);
|
||||||
expect(
|
expect(
|
||||||
Material.of(tester.element(find.byKey(tileKey))),
|
find.byType(Material),
|
||||||
paints
|
paints
|
||||||
..rect(
|
..rect(
|
||||||
color: const Color(0xffffffff),
|
color: const Color(0xffffffff),
|
||||||
@ -1309,7 +1305,6 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('ListTile can be hovered and has correct hover color', (WidgetTester tester) async {
|
testWidgets('ListTile can be hovered and has correct hover color', (WidgetTester tester) async {
|
||||||
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||||
const Key tileKey = Key('ListTile');
|
|
||||||
Widget buildApp({bool enabled = true}) {
|
Widget buildApp({bool enabled = true}) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Material(
|
home: Material(
|
||||||
@ -1320,7 +1315,6 @@ void main() {
|
|||||||
height: 100,
|
height: 100,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
key: tileKey,
|
|
||||||
onTap: enabled ? () {} : null,
|
onTap: enabled ? () {} : null,
|
||||||
hoverColor: Colors.orange[500],
|
hoverColor: Colors.orange[500],
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
@ -1336,7 +1330,7 @@ void main() {
|
|||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(
|
expect(
|
||||||
Material.of(tester.element(find.byKey(tileKey))),
|
find.byType(Material),
|
||||||
paints
|
paints
|
||||||
..rect(
|
..rect(
|
||||||
color: const Color(0x1f000000),
|
color: const Color(0x1f000000),
|
||||||
@ -1349,30 +1343,30 @@ void main() {
|
|||||||
// Start hovering
|
// Start hovering
|
||||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||||
addTearDown(gesture.removePointer);
|
addTearDown(gesture.removePointer);
|
||||||
await gesture.moveTo(tester.getCenter(find.byKey(tileKey)));
|
await gesture.moveTo(tester.getCenter(find.byType(ListTile)));
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp());
|
await tester.pumpWidget(buildApp());
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(
|
expect(
|
||||||
Material.of(tester.element(find.byKey(tileKey))),
|
find.byType(Material),
|
||||||
paints
|
paints
|
||||||
..rect(
|
..rect(
|
||||||
color: const Color(0x1f000000),
|
color: const Color(0x1f000000),
|
||||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0))
|
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0))
|
||||||
..rect(
|
..rect(
|
||||||
color: Colors.orange[500],
|
color: Colors.orange[500],
|
||||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0))
|
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0))
|
||||||
..rect(
|
..rect(
|
||||||
color: const Color(0xffffffff),
|
color: const Color(0xffffffff),
|
||||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)),
|
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(buildApp(enabled: false));
|
await tester.pumpWidget(buildApp(enabled: false));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(
|
expect(
|
||||||
Material.of(tester.element(find.byKey(tileKey))),
|
find.byType(Material),
|
||||||
paints
|
paints
|
||||||
..rect(
|
..rect(
|
||||||
color: Colors.orange[500],
|
color: Colors.orange[500],
|
||||||
@ -1459,6 +1453,75 @@ void main() {
|
|||||||
expect(box.size, equals(const Size(800, 44)));
|
expect(box.size, equals(const Size(800, 44)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('ListTile shape is painted correctly', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/63877
|
||||||
|
const ShapeBorder rectShape = RoundedRectangleBorder();
|
||||||
|
const ShapeBorder stadiumShape = StadiumBorder();
|
||||||
|
final Color tileColor = Colors.red.shade500;
|
||||||
|
|
||||||
|
Widget buildListTile(ShapeBorder shape) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Material(
|
||||||
|
child: Center(
|
||||||
|
child: ListTile(shape: shape, tileColor: tileColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test rectangle shape
|
||||||
|
await tester.pumpWidget(buildListTile(rectShape));
|
||||||
|
Rect rect = tester.getRect(find.byType(ListTile));
|
||||||
|
|
||||||
|
// Check if a path was painted with the correct color and shape
|
||||||
|
expect(
|
||||||
|
find.byType(Material),
|
||||||
|
paints..path(
|
||||||
|
color: tileColor,
|
||||||
|
// Corners should be included
|
||||||
|
includes: <Offset>[
|
||||||
|
Offset(rect.left, rect.top),
|
||||||
|
Offset(rect.right, rect.top),
|
||||||
|
Offset(rect.left, rect.bottom),
|
||||||
|
Offset(rect.right, rect.bottom),
|
||||||
|
],
|
||||||
|
// Points outside rect should be excluded
|
||||||
|
excludes: <Offset>[
|
||||||
|
Offset(rect.left - 1, rect.top - 1),
|
||||||
|
Offset(rect.right + 1, rect.top - 1),
|
||||||
|
Offset(rect.left - 1, rect.bottom + 1),
|
||||||
|
Offset(rect.right + 1, rect.bottom + 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test stadium shape
|
||||||
|
await tester.pumpWidget(buildListTile(stadiumShape));
|
||||||
|
rect = tester.getRect(find.byType(ListTile));
|
||||||
|
|
||||||
|
// Check if a path was painted with the correct color and shape
|
||||||
|
expect(
|
||||||
|
find.byType(Material),
|
||||||
|
paints..path(
|
||||||
|
color: tileColor,
|
||||||
|
// Center points of sides should be included
|
||||||
|
includes: <Offset>[
|
||||||
|
Offset(rect.left + rect.width / 2, rect.top),
|
||||||
|
Offset(rect.left, rect.top + rect.height / 2),
|
||||||
|
Offset(rect.right, rect.top + rect.height / 2),
|
||||||
|
Offset(rect.left + rect.width / 2, rect.bottom),
|
||||||
|
],
|
||||||
|
// Corners should be excluded
|
||||||
|
excludes: <Offset>[
|
||||||
|
Offset(rect.left, rect.top),
|
||||||
|
Offset(rect.right, rect.top),
|
||||||
|
Offset(rect.left, rect.bottom),
|
||||||
|
Offset(rect.right, rect.bottom),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('ListTile changes mouse cursor when hovered', (WidgetTester tester) async {
|
testWidgets('ListTile changes mouse cursor when hovered', (WidgetTester tester) async {
|
||||||
// Test ListTile() constructor
|
// Test ListTile() constructor
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1540,8 +1603,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('ListTile respects tileColor & selectedTileColor', (WidgetTester tester) async {
|
testWidgets('ListTile respects tileColor & selectedTileColor', (WidgetTester tester) async {
|
||||||
bool isSelected = false;
|
bool isSelected = false;
|
||||||
const Color selectedTileColor = Colors.red;
|
final Color tileColor = Colors.green.shade500;
|
||||||
const Color tileColor = Colors.green;
|
final Color selectedTileColor = Colors.red.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@ -1566,16 +1629,48 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Initially, when isSelected is false, the ListTile should respect tileColor.
|
// Initially, when isSelected is false, the ListTile should respect tileColor.
|
||||||
ColoredBox coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: tileColor));
|
||||||
expect(coloredBox.color, tileColor);
|
|
||||||
|
|
||||||
// Tap on tile to change isSelected.
|
// Tap on tile to change isSelected.
|
||||||
await tester.tap(find.byType(ListTile));
|
await tester.tap(find.byType(ListTile));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
// When isSelected is true, the ListTile should respect selectedTileColor.
|
// When isSelected is true, the ListTile should respect selectedTileColor.
|
||||||
coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||||
expect(coloredBox.color, selectedTileColor);
|
});
|
||||||
|
|
||||||
|
testWidgets('ListTile shows Material ripple effects on top of tileColor', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/73616
|
||||||
|
final Color tileColor = Colors.red.shade500;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Material(
|
||||||
|
child: Center(
|
||||||
|
child: ListTile(
|
||||||
|
tileColor: tileColor,
|
||||||
|
onTap: () {},
|
||||||
|
title: const Text('Title'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Before ListTile is tapped, it should be tileColor
|
||||||
|
expect(find.byType(Material), paints..path(color: tileColor));
|
||||||
|
|
||||||
|
// Tap on tile to trigger ink effect and wait for it to be underway.
|
||||||
|
await tester.tap(find.byType(ListTile));
|
||||||
|
await tester.pump(const Duration(milliseconds: 200));
|
||||||
|
|
||||||
|
// After tap, the tile could be drawn in tileColor, with the ripple (circle) on top
|
||||||
|
expect(
|
||||||
|
find.byType(Material),
|
||||||
|
paints
|
||||||
|
..path(color: tileColor)
|
||||||
|
..circle(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ListTile default tile color', (WidgetTester tester) async {
|
testWidgets('ListTile default tile color', (WidgetTester tester) async {
|
||||||
@ -1602,16 +1697,13 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
ColoredBox coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: defaultColor));
|
||||||
expect(coloredBox.color, defaultColor);
|
|
||||||
|
|
||||||
// Tap on tile to change isSelected.
|
// Tap on tile to change isSelected.
|
||||||
await tester.tap(find.byType(ListTile));
|
await tester.tap(find.byType(ListTile));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: defaultColor));
|
||||||
expect(isSelected, isTrue);
|
|
||||||
expect(coloredBox.color, defaultColor);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ListTile respects ListTileTheme\'s tileColor & selectedTileColor', (WidgetTester tester) async {
|
testWidgets('ListTile respects ListTileTheme\'s tileColor & selectedTileColor', (WidgetTester tester) async {
|
||||||
@ -1622,8 +1714,8 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Material(
|
home: Material(
|
||||||
child: ListTileTheme(
|
child: ListTileTheme(
|
||||||
selectedTileColor: Colors.green,
|
tileColor: Colors.green.shade500,
|
||||||
tileColor: Colors.red,
|
selectedTileColor: Colors.red.shade500,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: StatefulBuilder(
|
child: StatefulBuilder(
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
@ -1643,21 +1735,19 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
ColoredBox coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: theme.tileColor));
|
||||||
expect(coloredBox.color, theme.tileColor);
|
|
||||||
|
|
||||||
// Tap on tile to change isSelected.
|
// Tap on tile to change isSelected.
|
||||||
await tester.tap(find.byType(ListTile));
|
await tester.tap(find.byType(ListTile));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: theme.selectedTileColor));
|
||||||
expect(coloredBox.color, theme.selectedTileColor);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ListTileTheme\'s tileColor & selectedTileColor are overridden by ListTile properties', (WidgetTester tester) async {
|
testWidgets('ListTileTheme\'s tileColor & selectedTileColor are overridden by ListTile properties', (WidgetTester tester) async {
|
||||||
bool isSelected = false;
|
bool isSelected = false;
|
||||||
const Color tileColor = Colors.brown;
|
final Color tileColor = Colors.green.shade500;
|
||||||
const Color selectedTileColor = Colors.purple;
|
final Color selectedTileColor = Colors.red.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@ -1685,15 +1775,13 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
ColoredBox coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: tileColor));
|
||||||
expect(coloredBox.color, tileColor);
|
|
||||||
|
|
||||||
// Tap on tile to change isSelected.
|
// Tap on tile to change isSelected.
|
||||||
await tester.tap(find.byType(ListTile));
|
await tester.tap(find.byType(ListTile));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
coloredBox = tester.widget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||||
expect(coloredBox.color, selectedTileColor);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ListTile layout at zero size', (WidgetTester tester) async {
|
testWidgets('ListTile layout at zero size', (WidgetTester tester) async {
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
import '../widgets/semantics_tester.dart';
|
import '../widgets/semantics_tester.dart';
|
||||||
|
|
||||||
@ -627,7 +628,7 @@ void main() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final Rect paddingRect = tester.getRect(find.byType(Padding));
|
final Rect paddingRect = tester.getRect(find.byType(SafeArea));
|
||||||
final Rect radioRect = tester.getRect(find.byType(radioType));
|
final Rect radioRect = tester.getRect(find.byType(radioType));
|
||||||
final Rect titleRect = tester.getRect(find.text('Title'));
|
final Rect titleRect = tester.getRect(find.text('Title'));
|
||||||
|
|
||||||
@ -667,37 +668,36 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('RadioListTile respects tileColor', (WidgetTester tester) async {
|
testWidgets('RadioListTile respects tileColor', (WidgetTester tester) async {
|
||||||
const Color tileColor = Colors.red;
|
final Color tileColor = Colors.red.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: RadioListTile<bool>(
|
child: RadioListTile<bool>(
|
||||||
value: false,
|
value: false,
|
||||||
groupValue: true,
|
groupValue: true,
|
||||||
onChanged: null,
|
onChanged: null,
|
||||||
title: Text('Title'),
|
title: const Text('Title'),
|
||||||
tileColor: tileColor,
|
tileColor: tileColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final ColoredBox coloredBox = tester.firstWidget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: tileColor));
|
||||||
expect(coloredBox.color, tileColor);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('RadioListTile respects selectedTileColor', (WidgetTester tester) async {
|
testWidgets('RadioListTile respects selectedTileColor', (WidgetTester tester) async {
|
||||||
const Color selectedTileColor = Colors.black;
|
final Color selectedTileColor = Colors.green.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: RadioListTile<bool>(
|
child: RadioListTile<bool>(
|
||||||
value: false,
|
value: false,
|
||||||
groupValue: true,
|
groupValue: true,
|
||||||
onChanged: null,
|
onChanged: null,
|
||||||
title: Text('Title'),
|
title: const Text('Title'),
|
||||||
selected: true,
|
selected: true,
|
||||||
selectedTileColor: selectedTileColor,
|
selectedTileColor: selectedTileColor,
|
||||||
),
|
),
|
||||||
@ -705,7 +705,6 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final ColoredBox coloredBox = tester.firstWidget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||||
expect(coloredBox.color, equals(selectedTileColor));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -359,35 +359,34 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('SwitchListTile respects tileColor', (WidgetTester tester) async {
|
testWidgets('SwitchListTile respects tileColor', (WidgetTester tester) async {
|
||||||
const Color tileColor = Colors.red;
|
final Color tileColor = Colors.red.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: SwitchListTile(
|
child: SwitchListTile(
|
||||||
value: false,
|
value: false,
|
||||||
onChanged: null,
|
onChanged: null,
|
||||||
title: Text('Title'),
|
title: const Text('Title'),
|
||||||
tileColor: tileColor,
|
tileColor: tileColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final ColoredBox coloredBox = tester.firstWidget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: tileColor));
|
||||||
expect(coloredBox.color, tileColor);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('SwitchListTile respects selectedTileColor', (WidgetTester tester) async {
|
testWidgets('SwitchListTile respects selectedTileColor', (WidgetTester tester) async {
|
||||||
const Color selectedTileColor = Colors.black;
|
final Color selectedTileColor = Colors.green.shade500;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: SwitchListTile(
|
child: SwitchListTile(
|
||||||
value: false,
|
value: false,
|
||||||
onChanged: null,
|
onChanged: null,
|
||||||
title: Text('Title'),
|
title: const Text('Title'),
|
||||||
selected: true,
|
selected: true,
|
||||||
selectedTileColor: selectedTileColor,
|
selectedTileColor: selectedTileColor,
|
||||||
),
|
),
|
||||||
@ -395,8 +394,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final ColoredBox coloredBox = tester.firstWidget(find.byType(ColoredBox));
|
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||||
expect(coloredBox.color, equals(selectedTileColor));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
@ -21,14 +22,14 @@ class TestItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildFrame({ int? count, double? width, double? height, Axis? scrollDirection }) {
|
Widget buildFrame({ int? count, double? width, double? height, Axis? scrollDirection, Key? prototypeKey }) {
|
||||||
return Directionality(
|
return Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
scrollDirection: scrollDirection ?? Axis.vertical,
|
scrollDirection: scrollDirection ?? Axis.vertical,
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
SliverPrototypeExtentList(
|
SliverPrototypeExtentList(
|
||||||
prototypeItem: TestItem(item: -1, width: width, height: height),
|
prototypeItem: TestItem(item: -1, width: width, height: height, key: prototypeKey),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) => TestItem(item: index),
|
(BuildContext context, int index) => TestItem(item: index),
|
||||||
childCount: count,
|
childCount: count,
|
||||||
@ -136,4 +137,18 @@ void main() {
|
|||||||
for (int i = 1; i < 10; i += 1)
|
for (int i = 1; i < 10; i += 1)
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
expect(find.text('Item $i'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('SliverPrototypeExtentList prototypeItem paint transform is zero.', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/67117
|
||||||
|
// This test ensures that the SliverPrototypeExtentList does not cause an
|
||||||
|
// assertion error when calculating the paint transform of its prototypeItem.
|
||||||
|
// The paint transform of the prototypeItem should be zero, since it is not visible.
|
||||||
|
final GlobalKey prototypeKey = GlobalKey();
|
||||||
|
await tester.pumpWidget(buildFrame(count: 20, height: 100.0, prototypeKey: prototypeKey));
|
||||||
|
|
||||||
|
final RenderObject scrollView = tester.renderObject(find.byType(CustomScrollView));
|
||||||
|
final RenderObject prototype = prototypeKey.currentContext!.findRenderObject()!;
|
||||||
|
|
||||||
|
expect(prototype.getTransformTo(scrollView), Matrix4.zero());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user