Remove the second argument to WidgetError.
This makes WidgetError more like RenderingError, which will aid with https://github.com/flutter/flutter/issues/2356. Fixes https://github.com/flutter/flutter/issues/2443
This commit is contained in:
parent
f64101ab06
commit
ef9132a050
@ -256,7 +256,7 @@ class _DismissableState extends State<Dismissable> {
|
|||||||
assert(_resizeAnimation.status == AnimationStatus.completed);
|
assert(_resizeAnimation.status == AnimationStatus.completed);
|
||||||
throw new WidgetError(
|
throw new WidgetError(
|
||||||
'Dismissable widget completed its resize animation without being removed from the tree.\n'
|
'Dismissable widget completed its resize animation without being removed from the tree.\n'
|
||||||
'Make sure to implement the onDismissed handler and to immediately remove the Dismissable\n'
|
'Make sure to implement the onDismissed handler and to immediately remove the Dismissable '
|
||||||
'widget from the application once that handler has fired.'
|
'widget from the application once that handler has fired.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -149,8 +149,12 @@ abstract class GlobalKey<T extends State<StatefulComponent>> extends Key {
|
|||||||
message += 'The following GlobalKey was found multiple times among mounted elements: $key (${_debugDuplicates[key]} instances)\n';
|
message += 'The following GlobalKey was found multiple times among mounted elements: $key (${_debugDuplicates[key]} instances)\n';
|
||||||
message += 'The most recently registered instance is: ${_registry[key]}\n';
|
message += 'The most recently registered instance is: ${_registry[key]}\n';
|
||||||
}
|
}
|
||||||
if (!_debugDuplicates.isEmpty)
|
if (!_debugDuplicates.isEmpty) {
|
||||||
throw new WidgetError('Incorrect GlobalKey usage.', message);
|
throw new WidgetError(
|
||||||
|
'Incorrect GlobalKey usage.\n'
|
||||||
|
'$message'
|
||||||
|
);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1109,9 +1113,10 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
|
|||||||
}
|
}
|
||||||
if (_debugStateLocked && (!_debugAllowIgnoredCallsToMarkNeedsBuild || !dirty)) {
|
if (_debugStateLocked && (!_debugAllowIgnoredCallsToMarkNeedsBuild || !dirty)) {
|
||||||
throw new WidgetError(
|
throw new WidgetError(
|
||||||
'Cannot mark this component as needing to build because the framework is '
|
'setState() or markNeedsBuild() called during build.\n'
|
||||||
'already in the process of building widgets. A widget can be marked as '
|
'This component cannot be marked as needing to build because the framework '
|
||||||
'needing to be built during the build phase only if one if its ancestor '
|
'is already in the process of building widgets. A widget can be marked as '
|
||||||
|
'needing to be built during the build phase only if one if its ancestors '
|
||||||
'is currently building. This exception is allowed because the framework '
|
'is currently building. This exception is allowed because the framework '
|
||||||
'builds parent widgets before children, which means a dirty descendant '
|
'builds parent widgets before children, which means a dirty descendant '
|
||||||
'will always be built. Otherwise, the framework might not visit this '
|
'will always be built. Otherwise, the framework might not visit this '
|
||||||
@ -1208,8 +1213,11 @@ abstract class ComponentElement<T extends Widget> extends BuildableElement<T> {
|
|||||||
assert(() {
|
assert(() {
|
||||||
if (built == null) {
|
if (built == null) {
|
||||||
throw new WidgetError(
|
throw new WidgetError(
|
||||||
'A build function returned null. Build functions must never return null.',
|
'A build function returned null.\n'
|
||||||
'The offending widget is: $widget'
|
'The offending widget is: $widget\n'
|
||||||
|
'Build functions must never return null. '
|
||||||
|
'To return an empty space that causes the building widget to fill available room, return "new Container()". '
|
||||||
|
'To return an empty space that takes as little room as possible, return "new Container(width: 0.0, height: 0.0)".'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1289,7 +1297,11 @@ class StatefulComponentElement<T extends StatefulComponent, U extends State<T>>
|
|||||||
assert(() {
|
assert(() {
|
||||||
if (_state._debugLifecycleState == _StateLifecycle.initialized)
|
if (_state._debugLifecycleState == _StateLifecycle.initialized)
|
||||||
return true;
|
return true;
|
||||||
throw new WidgetError('${_state.runtimeType}.initState failed to call super.initState.');
|
throw new WidgetError(
|
||||||
|
'${_state.runtimeType}.initState failed to call super.initState.\n'
|
||||||
|
'initState() implementations must always call their superclass initState() method, to ensure '
|
||||||
|
'that the entire widget is initialized correctly.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
assert(() { _state._debugLifecycleState = _StateLifecycle.ready; return true; });
|
assert(() { _state._debugLifecycleState = _StateLifecycle.ready; return true; });
|
||||||
super._firstBuild();
|
super._firstBuild();
|
||||||
@ -1324,7 +1336,11 @@ class StatefulComponentElement<T extends StatefulComponent, U extends State<T>>
|
|||||||
assert(() {
|
assert(() {
|
||||||
if (_state._debugLifecycleState == _StateLifecycle.defunct)
|
if (_state._debugLifecycleState == _StateLifecycle.defunct)
|
||||||
return true;
|
return true;
|
||||||
throw new WidgetError('${_state.runtimeType}.dispose failed to call super.dispose.');
|
throw new WidgetError(
|
||||||
|
'${_state.runtimeType}.dispose failed to call super.dispose.\n'
|
||||||
|
'dispose() implementations must always call their superclass dispose() method, to ensure '
|
||||||
|
'that all the resources used by the widget are fully released.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
assert(!dirty); // See BuildableElement.unmount for why this is important.
|
assert(!dirty); // See BuildableElement.unmount for why this is important.
|
||||||
_state._element = null;
|
_state._element = null;
|
||||||
@ -1381,12 +1397,15 @@ class ParentDataElement extends _ProxyElement<ParentDataWidget> {
|
|||||||
}
|
}
|
||||||
if (ancestor != null && badAncestors.isEmpty)
|
if (ancestor != null && badAncestors.isEmpty)
|
||||||
return true;
|
return true;
|
||||||
throw new WidgetError('Incorrect use of ParentDataWidget.', widget.debugDescribeInvalidAncestorChain(
|
throw new WidgetError(
|
||||||
description: "$this",
|
'Incorrect use of ParentDataWidget.\n' +
|
||||||
ownershipChain: parent.debugGetOwnershipChain(10),
|
widget.debugDescribeInvalidAncestorChain(
|
||||||
foundValidAncestor: ancestor != null,
|
description: "$this",
|
||||||
badAncestors: badAncestors
|
ownershipChain: parent.debugGetOwnershipChain(10),
|
||||||
));
|
foundValidAncestor: ancestor != null,
|
||||||
|
badAncestors: badAncestors
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
super.mount(parent, slot);
|
super.mount(parent, slot);
|
||||||
}
|
}
|
||||||
@ -1507,7 +1526,14 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
|
|||||||
// 'BuildContext' argument which you can pass to Theme.of() and other
|
// 'BuildContext' argument which you can pass to Theme.of() and other
|
||||||
// InheritedWidget APIs which eventually trigger a rebuild.)
|
// InheritedWidget APIs which eventually trigger a rebuild.)
|
||||||
assert(() {
|
assert(() {
|
||||||
throw new WidgetError('$runtimeType failed to implement reinvokeBuilders(), but got marked dirty.');
|
throw new WidgetError(
|
||||||
|
'$runtimeType failed to implement reinvokeBuilders(), but got marked dirty.\n'
|
||||||
|
'If a RenderObjectElement subclass supports being marked dirty, then the '
|
||||||
|
'reinvokeBuilders() method must be implemented.\n'
|
||||||
|
'If a RenderObjectElement uses a builder callback, it must support being '
|
||||||
|
'marked dirty, because builder callbacks can register the object as having '
|
||||||
|
'an Inherited dependency.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1812,7 +1838,11 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
|
|||||||
continue; // when these nodes are reordered, we just reassign the data
|
continue; // when these nodes are reordered, we just reassign the data
|
||||||
|
|
||||||
if (!idSet.add(child.key)) {
|
if (!idSet.add(child.key)) {
|
||||||
throw new WidgetError('If multiple keyed nodes exist as children of another node, they must have unique keys. $widget has multiple children with key "${child.key}".');
|
throw new WidgetError(
|
||||||
|
'Duplicate keys found.\n'
|
||||||
|
'If multiple keyed nodes exist as children of another node, they must have unique keys.\n'
|
||||||
|
'$widget has multiple children with key "${child.key}".'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1841,16 +1871,10 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WidgetError extends Error {
|
class WidgetError extends AssertionError {
|
||||||
WidgetError(String message, [ String rawDetails = '' ]) {
|
WidgetError(this.message);
|
||||||
rawDetails = rawDetails.trimRight(); // remove trailing newlines
|
final String message;
|
||||||
if (rawDetails != '')
|
String toString() => message;
|
||||||
_message = '$message\n$rawDetails';
|
|
||||||
else
|
|
||||||
_message = message;
|
|
||||||
}
|
|
||||||
String _message;
|
|
||||||
String toString() => _message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void WidgetsExceptionHandler(String context, dynamic exception, StackTrace stack);
|
typedef void WidgetsExceptionHandler(String context, dynamic exception, StackTrace stack);
|
||||||
|
@ -71,11 +71,20 @@ class GestureDetector extends StatelessComponent {
|
|||||||
bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
|
bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
|
||||||
bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
|
bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
|
||||||
if (havePan || haveScale) {
|
if (havePan || haveScale) {
|
||||||
if (havePan && haveScale)
|
if (havePan && haveScale) {
|
||||||
throw new WidgetError('Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan. Just use the scale gesture recognizer.');
|
throw new WidgetError(
|
||||||
|
'Incorrect GestureDetector arguments.\n'
|
||||||
|
'Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan. Just use the scale gesture recognizer.'
|
||||||
|
);
|
||||||
|
}
|
||||||
String recognizer = havePan ? 'pan' : 'scale';
|
String recognizer = havePan ? 'pan' : 'scale';
|
||||||
if (haveVerticalDrag && haveHorizontalDrag)
|
if (haveVerticalDrag && haveHorizontalDrag) {
|
||||||
throw new WidgetError('Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.');
|
throw new WidgetError(
|
||||||
|
'Incorrect GestureDetector arguments.\n'
|
||||||
|
'Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer '
|
||||||
|
'will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -279,8 +288,15 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
|
|||||||
/// the gesture detector should be enabled.
|
/// the gesture detector should be enabled.
|
||||||
void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
|
void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (!RenderObject.debugDoingLayout)
|
if (!RenderObject.debugDoingLayout) {
|
||||||
throw new WidgetError('replaceGestureRecognizers() can only be called during the layout phase.');
|
throw new WidgetError(
|
||||||
|
'Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState.\n'
|
||||||
|
'The replaceGestureRecognizers() method can only be called during the layout phase. '
|
||||||
|
'To set the gesture recognisers at other times, trigger a new build using setState() '
|
||||||
|
'and provide the new gesture recognisers as constructor arguments to the corresponding '
|
||||||
|
'RawGestureDetector or GestureDetector object.'
|
||||||
|
);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
_syncAll(gestures);
|
_syncAll(gestures);
|
||||||
|
@ -113,8 +113,8 @@ class Hero extends StatefulComponent {
|
|||||||
if (tagHeroes.containsKey(key)) {
|
if (tagHeroes.containsKey(key)) {
|
||||||
new WidgetError(
|
new WidgetError(
|
||||||
'There are multiple heroes that share the same key within the same subtree.\n'
|
'There are multiple heroes that share the same key within the same subtree.\n'
|
||||||
'Within each subtree for which heroes are to be animated (typically a PageRoute subtree),\n'
|
'Within each subtree for which heroes are to be animated (typically a PageRoute subtree), '
|
||||||
'either each Hero must have a unique tag, or, all the heroes with a particular tag must\n'
|
'either each Hero must have a unique tag, or, all the heroes with a particular tag must '
|
||||||
'have different keys.\n'
|
'have different keys.\n'
|
||||||
'In this case, the tag "$tag" had multiple heroes with the key "$key".'
|
'In this case, the tag "$tag" had multiple heroes with the key "$key".'
|
||||||
);
|
);
|
||||||
|
@ -260,8 +260,12 @@ class Navigator extends StatefulComponent {
|
|||||||
static void openTransaction(BuildContext context, NavigatorTransactionCallback callback) {
|
static void openTransaction(BuildContext context, NavigatorTransactionCallback callback) {
|
||||||
NavigatorState navigator = context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
|
NavigatorState navigator = context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
|
||||||
assert(() {
|
assert(() {
|
||||||
if (navigator == null)
|
if (navigator == null) {
|
||||||
throw new WidgetError('openTransaction called with a context that does not include a Navigator. The context passed to the Navigator.openTransaction() method must be that of a widget that is a descendant of a Navigator widget.');
|
throw new WidgetError(
|
||||||
|
'openTransaction called with a context that does not include a Navigator.\n'
|
||||||
|
'The context passed to the Navigator.openTransaction() method must be that of a widget that is a descendant of a Navigator widget.'
|
||||||
|
);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
navigator.openTransaction(callback);
|
navigator.openTransaction(callback);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user