Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
eb161e6
Add structured errors in Animations, TabView, ChangeNotifier
Oct 13, 2019
94478b3
Add structured error on MaterialPageRoute, BoxBorder, DecorationImage…
Oct 14, 2019
711b050
Add structured errors in Debug
Oct 14, 2019
3eb06f3
Fix test errors
Oct 14, 2019
2eb2df2
Add structured errors in Scaffold and Stepper
Oct 14, 2019
f5941f0
Add structured errors in part of Rendering Layer
Oct 15, 2019
6683118
Fix failing test due to FloatingPoint precision
Oct 15, 2019
6c14773
Fix failing tests due to precision error and not using final
Oct 15, 2019
73c4c52
Fix failing test due to floating precision error with RegEx instead
Oct 15, 2019
c2544b3
Add structured error in CustomLayout and increase test coverage
Oct 15, 2019
0275a37
Add structured error & its test in ListBody
Oct 16, 2019
8dbadb8
Add structured error in ProxyBox and increase test coverage
Oct 16, 2019
9894d10
Add structured error message in Viewport
Oct 16, 2019
7397706
Fix styles and add more assertions on ErrorHint and DiagnosticProperty
Oct 16, 2019
94015d9
Add structured error in scheduler/binding and scheduler/ticker
Oct 16, 2019
aecce8c
Add structured error in AssetBundle and TextInput
Oct 16, 2019
bc64537
Add structured errors in several widgets #1
Oct 17, 2019
cbbd90e
Remove unused import
Oct 17, 2019
3c0dd4a
Add assertions on hint messages
Oct 17, 2019
724c74a
Fix catch spacing
Oct 17, 2019
96c9edc
Add structured error in several widgets part 2 and increase code cove…
Oct 17, 2019
b406592
Add structured error in flutter_test/widget_tester
Oct 17, 2019
d6ed3af
Fix floating precision accuracy by using RegExp
Oct 17, 2019
dd245a7
Remove todo to add tests in Scaffold showBottomSheet
Oct 18, 2019
2408110
Fix reviews by indenting lines and fixing the assertion orders
Oct 18, 2019
128631b
Fix failing tests due to renaming class
Oct 18, 2019
72bab51
Try skipping the NetworkBundleTest
Oct 21, 2019
31f82f4
Merge branch 'master' of github.com:flutter/flutter into structured-e…
Oct 24, 2019
1cbc5c3
Remove leading space in material/debug error hint
Oct 24, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions packages/flutter/lib/src/animation/animations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,14 @@ class CurvedAnimation extends Animation<double> with AnimationWithParentMixin<do
final double transformedValue = activeCurve.transform(t);
final double roundedTransformedValue = transformedValue.round().toDouble();
if (roundedTransformedValue != t) {
throw FlutterError(
'Invalid curve endpoint at $t.\n'
'Curves must map 0.0 to near zero and 1.0 to near one but '
'${activeCurve.runtimeType} mapped $t to $transformedValue, which '
'is near $roundedTransformedValue.'
);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Invalid curve endpoint at $t.'),
ErrorDescription(
'Curves must map 0.0 to near zero and 1.0 to near one but '
'${activeCurve.runtimeType} mapped $t to $transformedValue, which '
'is near $roundedTransformedValue.'
)
]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought FlutterError would automatically take its first line and use it as a summary and use the rest as a description, is that not the case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I believe so too, this is actually redundant change
cc: @jacob314

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes go ahead and revert this change as part of your followup cleanup CL.

}
return true;
}());
Expand Down
38 changes: 21 additions & 17 deletions packages/flutter/lib/src/cupertino/tab_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,29 +192,33 @@ class _CupertinoTabViewState extends State<CupertinoTabView> {
Route<dynamic> _onUnknownRoute(RouteSettings settings) {
assert(() {
if (widget.onUnknownRoute == null) {
throw FlutterError(
'Could not find a generator for route $settings in the $runtimeType.\n'
'Generators for routes are searched for in the following order:\n'
' 1. For the "/" route, the "builder" property, if non-null, is used.\n'
' 2. Otherwise, the "routes" table is used, if it has an entry for '
'the route.\n'
' 3. Otherwise, onGenerateRoute is called. It should return a '
'non-null value for any valid route not handled by "builder" and "routes".\n'
' 4. Finally if all else fails onUnknownRoute is called.\n'
'Unfortunately, onUnknownRoute was not set.'
);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Could not find a generator for route $settings in the $runtimeType.'),
ErrorDescription(
'Generators for routes are searched for in the following order:\n'
' 1. For the "/" route, the "builder" property, if non-null, is used.\n'
' 2. Otherwise, the "routes" table is used, if it has an entry for '
'the route.\n'
' 3. Otherwise, onGenerateRoute is called. It should return a '
'non-null value for any valid route not handled by "builder" and "routes".\n'
' 4. Finally if all else fails onUnknownRoute is called.\n'
'Unfortunately, onUnknownRoute was not set.'
)
]);
}
return true;
}());
final Route<dynamic> result = widget.onUnknownRoute(settings);
assert(() {
if (result == null) {
throw FlutterError(
'The onUnknownRoute callback returned null.\n'
'When the $runtimeType requested the route $settings from its '
'onUnknownRoute callback, the callback returned null. Such callbacks '
'must never return null.'
);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('The onUnknownRoute callback returned null.'),
ErrorDescription(
'When the $runtimeType requested the route $settings from its '
'onUnknownRoute callback, the callback returned null. Such callbacks '
'must never return null.'
)
]);
}
return true;
}());
Expand Down
8 changes: 4 additions & 4 deletions packages/flutter/lib/src/foundation/change_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ class ChangeNotifier implements Listenable {
bool _debugAssertNotDisposed() {
assert(() {
if (_listeners == null) {
throw FlutterError(
'A $runtimeType was used after being disposed.\n'
'Once you have called dispose() on a $runtimeType, it can no longer be used.'
);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('A $runtimeType was used after being disposed.'),
ErrorDescription('Once you have called dispose() on a $runtimeType, it can no longer be used.')
]);
}
return true;
}());
Expand Down
128 changes: 45 additions & 83 deletions packages/flutter/lib/src/material/debug.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'material.dart';
Expand All @@ -24,45 +25,26 @@ import 'scaffold.dart' show Scaffold;
bool debugCheckHasMaterial(BuildContext context) {
assert(() {
if (context.widget is! Material && context.ancestorWidgetOfExactType(Material) == null) {
final StringBuffer message = StringBuffer();
message.writeln('No Material widget found.');
message.writeln(
'${context.widget.runtimeType} widgets require a Material '
'widget ancestor.'
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('No Material widget found.'),
ErrorDescription(
'${context.widget.runtimeType} widgets require a Material '
'widget ancestor.\n'
'In material design, most widgets are conceptually "printed" on '
'a sheet of material. In Flutter\'s material library, that '
'material is represented by the Material widget. It is the '
'Material widget that renders ink splashes, for instance. '
'Because of this, many material library widgets require that '
'there be a Material widget in the tree above them.'
),
ErrorHint(
'To introduce a Material widget, you can either directly '
'include one, or use a widget that contains Material itself, '
'such as a Card, Dialog, Drawer, or Scaffold.',
),
...context.describeMissingAncestor(expectedAncestorType: Material)
]
);
message.writeln(
'In material design, most widgets are conceptually "printed" on '
'a sheet of material. In Flutter\'s material library, that '
'material is represented by the Material widget. It is the '
'Material widget that renders ink splashes, for instance. '
'Because of this, many material library widgets require that '
'there be a Material widget in the tree above them.'
);
message.writeln(
'To introduce a Material widget, you can either directly '
'include one, or use a widget that contains Material itself, '
'such as a Card, Dialog, Drawer, or Scaffold.'
);
message.writeln(
'The specific widget that could not find a Material ancestor was:'
);
message.writeln(' ${context.widget}');
final List<Widget> ancestors = <Widget>[];
context.visitAncestorElements((Element element) {
ancestors.add(element.widget);
return true;
});
if (ancestors.isNotEmpty) {
message.write('The ancestors of this widget were:');
for (Widget ancestor in ancestors)
message.write('\n $ancestor');
} else {
message.writeln(
'This widget is the root of the tree, so it has no '
'ancestors, let alone a "Material" ancestor.'
);
}
throw FlutterError(message.toString());
}
return true;
}());
Expand All @@ -87,42 +69,24 @@ bool debugCheckHasMaterial(BuildContext context) {
bool debugCheckHasMaterialLocalizations(BuildContext context) {
assert(() {
if (Localizations.of<MaterialLocalizations>(context, MaterialLocalizations) == null) {
final StringBuffer message = StringBuffer();
message.writeln('No MaterialLocalizations found.');
message.writeln(
'${context.widget.runtimeType} widgets require MaterialLocalizations '
'to be provided by a Localizations widget ancestor.'
);
message.writeln(
'Localizations are used to generate many different messages, labels,'
'and abbreviations which are used by the material library. '
);
message.writeln(
'To introduce a MaterialLocalizations, either use a '
' MaterialApp at the root of your application to include them '
'automatically, or add a Localization widget with a '
'MaterialLocalizations delegate.'
);
message.writeln(
'The specific widget that could not find a MaterialLocalizations ancestor was:'
);
message.writeln(' ${context.widget}');
final List<Widget> ancestors = <Widget>[];
context.visitAncestorElements((Element element) {
ancestors.add(element.widget);
return true;
});
if (ancestors.isNotEmpty) {
message.write('The ancestors of this widget were:');
for (Widget ancestor in ancestors)
message.write('\n $ancestor');
} else {
message.writeln(
'This widget is the root of the tree, so it has no '
'ancestors, let alone a "Localizations" ancestor.'
);
}
throw FlutterError(message.toString());
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('No MaterialLocalizations found.'),
ErrorDescription(
'${context.widget.runtimeType} widgets require MaterialLocalizations '
'to be provided by a Localizations widget ancestor.'
),
ErrorDescription(
'Localizations are used to generate many different messages, labels,'
'and abbreviations which are used by the material library.'
),
ErrorHint(
'To introduce a MaterialLocalizations, either use a '
'MaterialApp at the root of your application to include them '
'automatically, or add a Localization widget with a '
'MaterialLocalizations delegate.'
),
...context.describeMissingAncestor(expectedAncestorType: MaterialLocalizations)
]);
}
return true;
}());
Expand All @@ -145,17 +109,15 @@ bool debugCheckHasMaterialLocalizations(BuildContext context) {
bool debugCheckHasScaffold(BuildContext context) {
assert(() {
if (context.widget is! Scaffold && context.ancestorWidgetOfExactType(Scaffold) == null) {
final Element element = context;
throw FlutterError(
'No Scaffold widget found.\n'
'${context.widget.runtimeType} widgets require a Scaffold widget ancestor.\n'
'The Specific widget that could not find a Scaffold ancestor was:\n'
' ${context.widget}\n'
'The ownership chain for the affected widget is:\n'
' ${element.debugGetCreatorChain(10)}\n'
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('No Scaffold widget found.'),
ErrorDescription('${context.widget.runtimeType} widgets require a Scaffold widget ancestor.'),
...context.describeMissingAncestor(expectedAncestorType: Scaffold),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@munificent being able to use ... made this code a lot more pleasant to write!

ErrorHint(
'Typically, the Scaffold widget is introduced by the MaterialApp or '
'WidgetsApp widget at the top of your application widget tree.'
);
)
]);
}
return true;
}());
Expand Down
8 changes: 4 additions & 4 deletions packages/flutter/lib/src/material/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ class MaterialPageRoute<T> extends PageRoute<T> {
final Widget result = builder(context);
assert(() {
if (result == null) {
throw FlutterError(
'The builder for route "${settings.name}" returned null.\n'
'Route builders must never return null.'
);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('The builder for route "${settings.name}" returned null.'),
ErrorDescription('Route builders must never return null.')
]);
}
return true;
}());
Expand Down
Loading