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.
|
/// Provides a child model for a render object subclass that has a unique child.
|
||||||
abstract class RenderObjectWithChildMixin<ChildType extends RenderObject> implements RenderObject {
|
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;
|
ChildType _child;
|
||||||
/// The render object's unique child
|
/// The render object's unique child
|
||||||
ChildType get child => _child;
|
ChildType get child => _child;
|
||||||
@ -2770,6 +2794,30 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
|
|||||||
/// The number of children.
|
/// The number of children.
|
||||||
int get childCount => _childCount;
|
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 _firstChild;
|
||||||
ChildType _lastChild;
|
ChildType _lastChild;
|
||||||
void _insertIntoChildList(ChildType child, { ChildType after }) {
|
void _insertIntoChildList(ChildType child, { ChildType after }) {
|
||||||
|
@ -599,6 +599,7 @@ class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObje
|
|||||||
@override
|
@override
|
||||||
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
||||||
assert(slot == _rootChildSlot);
|
assert(slot == _rootChildSlot);
|
||||||
|
assert(renderObject.debugValidateChild(child));
|
||||||
renderObject.child = child;
|
renderObject.child = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4096,6 +4096,7 @@ class SingleChildRenderObjectElement extends RenderObjectElement {
|
|||||||
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
||||||
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject;
|
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject;
|
||||||
assert(slot == null);
|
assert(slot == null);
|
||||||
|
assert(renderObject.debugValidateChild(child));
|
||||||
renderObject.child = child;
|
renderObject.child = child;
|
||||||
assert(renderObject == this.renderObject);
|
assert(renderObject == this.renderObject);
|
||||||
}
|
}
|
||||||
@ -4144,6 +4145,7 @@ class MultiChildRenderObjectElement extends RenderObjectElement {
|
|||||||
@override
|
@override
|
||||||
void insertChildRenderObject(RenderObject child, Element slot) {
|
void insertChildRenderObject(RenderObject child, Element slot) {
|
||||||
final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> renderObject = this.renderObject;
|
final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> renderObject = this.renderObject;
|
||||||
|
assert(renderObject.debugValidateChild(child));
|
||||||
renderObject.insert(child, after: slot?.renderObject);
|
renderObject.insert(child, after: slot?.renderObject);
|
||||||
assert(renderObject == this.renderObject);
|
assert(renderObject == this.renderObject);
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,7 @@ class _LayoutBuilderElement extends RenderObjectElement {
|
|||||||
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
void insertChildRenderObject(RenderObject child, dynamic slot) {
|
||||||
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject;
|
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject;
|
||||||
assert(slot == null);
|
assert(slot == null);
|
||||||
|
assert(renderObject.debugValidateChild(child));
|
||||||
renderObject.child = child;
|
renderObject.child = child;
|
||||||
assert(renderObject == this.renderObject);
|
assert(renderObject == this.renderObject);
|
||||||
}
|
}
|
||||||
|
@ -422,6 +422,7 @@ class _TheatreElement extends RenderObjectElement {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void insertChildRenderObject(RenderBox child, dynamic slot) {
|
void insertChildRenderObject(RenderBox child, dynamic slot) {
|
||||||
|
assert(renderObject.debugValidateChild(child));
|
||||||
if (slot == _onstageSlot) {
|
if (slot == _onstageSlot) {
|
||||||
assert(child is RenderStack);
|
assert(child is RenderStack);
|
||||||
renderObject.child = child;
|
renderObject.child = child;
|
||||||
|
@ -647,6 +647,7 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
|
|||||||
void insertChildRenderObject(covariant RenderObject child, int slot) {
|
void insertChildRenderObject(covariant RenderObject child, int slot) {
|
||||||
assert(slot != null);
|
assert(slot != null);
|
||||||
assert(_currentlyUpdatingChildIndex == slot);
|
assert(_currentlyUpdatingChildIndex == slot);
|
||||||
|
assert(renderObject.debugValidateChild(child));
|
||||||
renderObject.insert(child, after: _currentBeforeChild);
|
renderObject.insert(child, after: _currentBeforeChild);
|
||||||
assert(() {
|
assert(() {
|
||||||
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
|
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
|
||||||
|
@ -125,6 +125,7 @@ class _SliverPersistentHeaderElement extends RenderObjectElement {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void insertChildRenderObject(covariant RenderObject child, Null slot) {
|
void insertChildRenderObject(covariant RenderObject child, Null slot) {
|
||||||
|
assert(renderObject.debugValidateChild(child));
|
||||||
renderObject.child = 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…
x
Reference in New Issue
Block a user