mirror of
https://github.com/flutter/flutter
synced 2024-10-14 04:02:56 +00:00
Box/Sliver mismatches should have better error messages (#9525)
We now attempt to explain what went wrong. Fixes #9509
This commit is contained in:
parent
39d9bcdae8
commit
930183916c
|
@ -2668,6 +2668,30 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||
///
|
||||
/// Provides a child model for a render object subclass that has a unique child.
|
||||
abstract class RenderObjectWithChildMixin<ChildType extends RenderObject> implements RenderObject {
|
||||
bool debugValidateChild(RenderObject child) {
|
||||
assert(() {
|
||||
if (child is! ChildType) {
|
||||
throw new FlutterError(
|
||||
'A $runtimeType expected a child of type $ChildType but received a '
|
||||
'child of type ${child.runtimeType}.\n'
|
||||
'RenderObjects expect specific types of children because they '
|
||||
'coordinate with their children during layout and paint. For '
|
||||
'example, a RenderSliver cannot be the child of a RenderBox because '
|
||||
'a RenderSliver does not understand the RenderBox layout protocol.\n'
|
||||
'\n'
|
||||
'The $runtimeType that expected a $ChildType child was created by:\n'
|
||||
' $debugCreator\n'
|
||||
'\n'
|
||||
'The ${child.runtimeType} that did not match the expected child type '
|
||||
'was created by:\n'
|
||||
' ${child.debugCreator}\n'
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
ChildType _child;
|
||||
/// The render object's unique child
|
||||
ChildType get child => _child;
|
||||
|
@ -2770,6 +2794,30 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
|
|||
/// The number of children.
|
||||
int get childCount => _childCount;
|
||||
|
||||
bool debugValidateChild(RenderObject child) {
|
||||
assert(() {
|
||||
if (child is! ChildType) {
|
||||
throw new FlutterError(
|
||||
'A $runtimeType expected a child of type $ChildType but received a '
|
||||
'child of type ${child.runtimeType}.\n'
|
||||
'RenderObjects expect specific types of children because they '
|
||||
'coordinate with their children during layout and paint. For '
|
||||
'example, a RenderSliver cannot be the child of a RenderBox because '
|
||||
'a RenderSliver does not understand the RenderBox layout protocol.\n'
|
||||
'\n'
|
||||
'The $runtimeType that expected a $ChildType child was created by:\n'
|
||||
' $debugCreator\n'
|
||||
'\n'
|
||||
'The ${child.runtimeType} that did not match the expected child type '
|
||||
'was created by:\n'
|
||||
' ${child.debugCreator}\n'
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
ChildType _firstChild;
|
||||
ChildType _lastChild;
|
||||
void _insertIntoChildList(ChildType child, { ChildType after }) {
|
||||
|
|
|
@ -599,6 +599,7 @@ class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObje
|
|||
@override
|
||||
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
||||
assert(slot == _rootChildSlot);
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
renderObject.child = child;
|
||||
}
|
||||
|
||||
|
|
|
@ -4096,6 +4096,7 @@ class SingleChildRenderObjectElement extends RenderObjectElement {
|
|||
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
||||
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject;
|
||||
assert(slot == null);
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
renderObject.child = child;
|
||||
assert(renderObject == this.renderObject);
|
||||
}
|
||||
|
@ -4144,6 +4145,7 @@ class MultiChildRenderObjectElement extends RenderObjectElement {
|
|||
@override
|
||||
void insertChildRenderObject(RenderObject child, Element slot) {
|
||||
final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> renderObject = this.renderObject;
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
renderObject.insert(child, after: slot?.renderObject);
|
||||
assert(renderObject == this.renderObject);
|
||||
}
|
||||
|
|
|
@ -131,6 +131,7 @@ class _LayoutBuilderElement extends RenderObjectElement {
|
|||
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
||||
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject;
|
||||
assert(slot == null);
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
renderObject.child = child;
|
||||
assert(renderObject == this.renderObject);
|
||||
}
|
||||
|
|
|
@ -422,6 +422,7 @@ class _TheatreElement extends RenderObjectElement {
|
|||
|
||||
@override
|
||||
void insertChildRenderObject(RenderBox child, dynamic slot) {
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
if (slot == _onstageSlot) {
|
||||
assert(child is RenderStack);
|
||||
renderObject.child = child;
|
||||
|
|
|
@ -647,6 +647,7 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
|
|||
void insertChildRenderObject(covariant RenderObject child, int slot) {
|
||||
assert(slot != null);
|
||||
assert(_currentlyUpdatingChildIndex == slot);
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
renderObject.insert(child, after: _currentBeforeChild);
|
||||
assert(() {
|
||||
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
|
||||
|
|
|
@ -125,6 +125,7 @@ class _SliverPersistentHeaderElement extends RenderObjectElement {
|
|||
|
||||
@override
|
||||
void insertChildRenderObject(covariant RenderObject child, Null slot) {
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
renderObject.child = child;
|
||||
}
|
||||
|
||||
|
|
58
packages/flutter/test/widgets/box_sliver_mismatch_test.dart
Normal file
58
packages/flutter/test/widgets/box_sliver_mismatch_test.dart
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2017 The Chromium 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 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Sliver in a box', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new DecoratedBox(
|
||||
decoration: const BoxDecoration(),
|
||||
child: new SliverList(
|
||||
delegate: const SliverChildListDelegate(const <Widget>[]),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.takeException(), isFlutterError);
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Row(
|
||||
children: <Widget>[
|
||||
new SliverList(
|
||||
delegate: const SliverChildListDelegate(const <Widget>[]),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.takeException(), isFlutterError);
|
||||
});
|
||||
|
||||
testWidgets('Box in a sliver', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
const SizedBox(),
|
||||
],
|
||||
)
|
||||
);
|
||||
|
||||
expect(tester.takeException(), isFlutterError);
|
||||
|
||||
await tester.pumpWidget(
|
||||
new CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
const SliverPadding(
|
||||
padding: EdgeInsets.zero,
|
||||
sliver: const SizedBox(),
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
|
||||
expect(tester.takeException(), isFlutterError);
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue