diff --git a/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart b/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart index 0c5dc4b840..221897f8b7 100644 --- a/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart +++ b/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart @@ -211,9 +211,9 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda trailingChildWithLayout = firstChild; } - while (targetLastIndex == null || indexOf(trailingChildWithLayout) < targetLastIndex) { + for (int index = indexOf(trailingChildWithLayout) + 1; targetLastIndex == null || index <= targetLastIndex; ++index) { RenderBox child = childAfter(trailingChildWithLayout); - if (child == null) { + if (child == null || indexOf(child) != index) { child = insertAndLayoutChild(childConstraints, after: trailingChildWithLayout); if (child == null) { // We have run out of children. @@ -225,6 +225,7 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda trailingChildWithLayout = child; assert(child != null); final SliverMultiBoxAdaptorParentData childParentData = child.parentData; + assert(childParentData.index == index); childParentData.layoutOffset = indexToLayoutOffset(itemExtent, childParentData.index); } diff --git a/packages/flutter/lib/src/rendering/sliver_grid.dart b/packages/flutter/lib/src/rendering/sliver_grid.dart index 3d23d565e8..f5901efab3 100644 --- a/packages/flutter/lib/src/rendering/sliver_grid.dart +++ b/packages/flutter/lib/src/rendering/sliver_grid.dart @@ -579,7 +579,7 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor { final SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index); final BoxConstraints childConstraints = gridGeometry.getBoxConstraints(constraints); RenderBox child = childAfter(trailingChildWithLayout); - if (child == null) { + if (child == null || indexOf(child) != index) { child = insertAndLayoutChild(childConstraints, after: trailingChildWithLayout); if (child == null) { // We have run out of children. diff --git a/packages/flutter/test/widgets/slivers_test.dart b/packages/flutter/test/widgets/slivers_test.dart index ed5e21351b..e2c4c9390d 100644 --- a/packages/flutter/test/widgets/slivers_test.dart +++ b/packages/flutter/test/widgets/slivers_test.dart @@ -196,6 +196,73 @@ void main() { expect(find.text('BOTTOM'), findsOneWidget); }); + testWidgets('SliverGrid Correctly layout children after rearranging', (WidgetTester tester) async { + await tester.pumpWidget(const TestSliverGrid( + [ + Text('item0', key: Key('0')), + Text('item1', key: Key('1')), + ] + )); + await tester.pumpWidget(const TestSliverGrid( + [ + Text('item0', key: Key('0')), + Text('item3', key: Key('3')), + Text('item4', key: Key('4')), + Text('item1', key: Key('1')), + ] + )); + expect(find.text('item0'), findsOneWidget); + expect(find.text('item3'), findsOneWidget); + expect(find.text('item4'), findsOneWidget); + expect(find.text('item1'), findsOneWidget); + + final Offset item0Location = tester.getCenter(find.text('item0')); + final Offset item3Location = tester.getCenter(find.text('item3')); + final Offset item4Location = tester.getCenter(find.text('item4')); + final Offset item1Location = tester.getCenter(find.text('item1')); + + expect(isRight(item0Location, item3Location) && sameHorizontal(item0Location, item3Location), true); + expect(isBelow(item0Location, item4Location) && sameVertical(item0Location, item4Location), true); + expect(isBelow(item0Location, item1Location) && isRight(item0Location, item1Location), true); + }, + ); + + testWidgets('SliverFixedExtentList Correctly layout children after rearranging', (WidgetTester tester) async { + await tester.pumpWidget(const TestSliverFixedExtentList( + [ + Text('item0', key: Key('0')), + Text('item2', key: Key('2')), + Text('item1', key: Key('1')), + ] + )); + await tester.pumpWidget(const TestSliverFixedExtentList( + [ + Text('item0', key: Key('0')), + Text('item3', key: Key('3')), + Text('item1', key: Key('1')), + Text('item4', key: Key('4')), + Text('item2', key: Key('2')), + ] + )); + expect(find.text('item0'), findsOneWidget); + expect(find.text('item3'), findsOneWidget); + expect(find.text('item1'), findsOneWidget); + expect(find.text('item4'), findsOneWidget); + expect(find.text('item2'), findsOneWidget); + + final Offset item0Location = tester.getCenter(find.text('item0')); + final Offset item3Location = tester.getCenter(find.text('item3')); + final Offset item1Location = tester.getCenter(find.text('item1')); + final Offset item4Location = tester.getCenter(find.text('item4')); + final Offset item2Location = tester.getCenter(find.text('item2')); + + expect(isBelow(item0Location, item3Location) && sameVertical(item0Location, item3Location), true); + expect(isBelow(item3Location, item1Location) && sameVertical(item3Location, item1Location), true); + expect(isBelow(item1Location, item4Location) && sameVertical(item1Location, item4Location), true); + expect(isBelow(item4Location, item2Location) && sameVertical(item4Location, item2Location), true); + }, + ); + testWidgets('Can override ErrorWidget.build', (WidgetTester tester) async { const Text errorText = Text('error'); final ErrorWidgetBuilder oldBuilder = ErrorWidget.builder; @@ -212,3 +279,56 @@ void main() { ErrorWidget.builder = oldBuilder; }); } + +bool isRight(Offset a, Offset b) => b.dx > a.dx; +bool isBelow(Offset a, Offset b) => b.dy > a.dy; +bool sameHorizontal(Offset a, Offset b) => b.dy == a.dy; +bool sameVertical(Offset a, Offset b) => b.dx == a.dx; + +class TestSliverGrid extends StatelessWidget { + const TestSliverGrid(this.children); + + final List children; + + @override + Widget build(BuildContext context) { + return Directionality( + textDirection: TextDirection.ltr, + child: CustomScrollView( + slivers: [ + SliverGrid( + delegate: SliverChildListDelegate( + children, + ), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + ), + ), + ], + ) + ); + } +} + +class TestSliverFixedExtentList extends StatelessWidget { + const TestSliverFixedExtentList(this.children); + + final List children; + + @override + Widget build(BuildContext context) { + return Directionality( + textDirection: TextDirection.ltr, + child: CustomScrollView( + slivers: [ + SliverFixedExtentList( + itemExtent: 10.0, + delegate: SliverChildListDelegate( + children, + ), + ), + ], + ) + ); + } +}