Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b75abd9f
Unverified
Commit
b75abd9f
authored
Nov 20, 2019
by
Ian Hickson
Committed by
GitHub
Nov 20, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Try a mildly prettier FlutterError and make it less drastic in release mode. (#44967)
parent
664350d2
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
212 additions
and
34 deletions
+212
-34
assertions.dart
packages/flutter/lib/src/foundation/assertions.dart
+5
-0
error.dart
packages/flutter/lib/src/rendering/error.dart
+71
-28
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+78
-5
error_test.dart
packages/flutter/test/rendering/error_test.dart
+37
-1
error_widget_builder_test.dart
packages/flutter/test/widgets/error_widget_builder_test.dart
+21
-0
No files found.
packages/flutter/lib/src/foundation/assertions.dart
View file @
b75abd9f
...
@@ -479,6 +479,11 @@ class FlutterErrorDetails extends Diagnosticable {
...
@@ -479,6 +479,11 @@ class FlutterErrorDetails extends Diagnosticable {
/// Error class used to report Flutter-specific assertion failures and
/// Error class used to report Flutter-specific assertion failures and
/// contract violations.
/// contract violations.
///
/// See also:
///
/// * <https://flutter.dev/docs/testing/errors>, more information about error
/// handling in Flutter.
class
FlutterError
extends
Error
with
DiagnosticableTreeMixin
implements
AssertionError
{
class
FlutterError
extends
Error
with
DiagnosticableTreeMixin
implements
AssertionError
{
/// Create an error message from a string.
/// Create an error message from a string.
///
///
...
...
packages/flutter/lib/src/rendering/error.dart
View file @
b75abd9f
...
@@ -10,9 +10,6 @@ import 'object.dart';
...
@@ -10,9 +10,6 @@ import 'object.dart';
const
double
_kMaxWidth
=
100000.0
;
const
double
_kMaxWidth
=
100000.0
;
const
double
_kMaxHeight
=
100000.0
;
const
double
_kMaxHeight
=
100000.0
;
// Line length to fit small phones without dynamically checking size.
const
String
_kLine
=
'
\n\n
────────────────────
\n\n
'
;
/// A render object used as a placeholder when an error occurs.
/// A render object used as a placeholder when an error occurs.
///
///
/// The box will be painted in the color given by the
/// The box will be painted in the color given by the
...
@@ -25,8 +22,9 @@ const String _kLine = '\n\n─────────────────
...
@@ -25,8 +22,9 @@ const String _kLine = '\n\n─────────────────
/// [RenderErrorBox.textStyle] and [RenderErrorBox.paragraphStyle] static
/// [RenderErrorBox.textStyle] and [RenderErrorBox.paragraphStyle] static
/// properties.
/// properties.
///
///
/// Again to help simplify the class, this box tries to be 100000.0 pixels wide
/// Again to help simplify the class, if the parent has left the constraints
/// and high, to approximate being infinitely high but without using infinities.
/// unbounded, this box tries to be 100000.0 pixels wide and high, to
/// approximate being infinitely high but without using infinities.
class
RenderErrorBox
extends
RenderBox
{
class
RenderErrorBox
extends
RenderBox
{
/// Creates a RenderErrorBox render object.
/// Creates a RenderErrorBox render object.
///
///
...
@@ -45,13 +43,10 @@ class RenderErrorBox extends RenderBox {
...
@@ -45,13 +43,10 @@ class RenderErrorBox extends RenderBox {
// see the paragraph.dart file and the RenderParagraph class.
// see the paragraph.dart file and the RenderParagraph class.
final
ui
.
ParagraphBuilder
builder
=
ui
.
ParagraphBuilder
(
paragraphStyle
);
final
ui
.
ParagraphBuilder
builder
=
ui
.
ParagraphBuilder
(
paragraphStyle
);
builder
.
pushStyle
(
textStyle
);
builder
.
pushStyle
(
textStyle
);
builder
.
addText
(
builder
.
addText
(
message
);
'
$message$_kLine$message$_kLine$message$_kLine$message$_kLine$message$_kLine$message$_kLine
'
'
$message$_kLine$message$_kLine$message$_kLine$message$_kLine$message$_kLine$message
'
);
_paragraph
=
builder
.
build
();
_paragraph
=
builder
.
build
();
}
}
}
catch
(
e
)
{
}
catch
(
e
rror
)
{
// Intentionally left empty.
// Intentionally left empty.
}
}
}
}
...
@@ -82,39 +77,87 @@ class RenderErrorBox extends RenderBox {
...
@@ -82,39 +77,87 @@ class RenderErrorBox extends RenderBox {
size
=
constraints
.
constrain
(
const
Size
(
_kMaxWidth
,
_kMaxHeight
));
size
=
constraints
.
constrain
(
const
Size
(
_kMaxWidth
,
_kMaxHeight
));
}
}
/// The distance to place around the text.
///
/// This is intended to ensure that if the [RenderErrorBox] is placed at the top left
/// of the screen, under the system's status bar, the error text is still visible in
/// the area below the status bar.
///
/// The padding is ignored if the error box is smaller than the padding.
///
/// See also:
///
/// * [minimumWidth], which controls how wide the box must be before the
// horizontal padding is applied.
static
EdgeInsets
padding
=
const
EdgeInsets
.
fromLTRB
(
64.0
,
96.0
,
64.0
,
12.0
);
/// The width below which the horizontal padding is not applied.
///
/// If the left and right padding would reduce the available width to less than
/// this value, then the text is rendered flush with the left edge.
static
double
minimumWidth
=
200.0
;
/// The color to use when painting the background of [RenderErrorBox] objects.
/// The color to use when painting the background of [RenderErrorBox] objects.
static
Color
backgroundColor
=
const
Color
(
0xF0900000
);
///
/// Defaults to red in debug mode, a light gray otherwise.
static
Color
backgroundColor
=
_initBackgroundColor
();
static
Color
_initBackgroundColor
()
{
Color
result
=
const
Color
(
0xF0C0C0C0
);
assert
(()
{
result
=
const
Color
(
0xF0900000
);
return
true
;
}());
return
result
;
}
/// The text style to use when painting [RenderErrorBox] objects.
/// The text style to use when painting [RenderErrorBox] objects.
static
ui
.
TextStyle
textStyle
=
ui
.
TextStyle
(
///
color:
const
Color
(
0xFFFFFF66
),
/// Defaults to a yellow monospace font in debug mode, and a dark gray
fontFamily:
'monospace'
,
/// sans-serif font otherwise.
fontSize:
14.0
,
static
ui
.
TextStyle
textStyle
=
_initTextStyle
();
fontWeight:
FontWeight
.
bold
,
);
static
ui
.
TextStyle
_initTextStyle
()
{
ui
.
TextStyle
result
=
ui
.
TextStyle
(
color:
const
Color
(
0xFF303030
),
fontFamily:
'sans-serif'
,
fontSize:
18.0
,
);
assert
(()
{
result
=
ui
.
TextStyle
(
color:
const
Color
(
0xFFFFFF66
),
fontFamily:
'monospace'
,
fontSize:
14.0
,
fontWeight:
FontWeight
.
bold
,
);
return
true
;
}());
return
result
;
}
/// The paragraph style to use when painting [RenderErrorBox] objects.
/// The paragraph style to use when painting [RenderErrorBox] objects.
static
ui
.
ParagraphStyle
paragraphStyle
=
ui
.
ParagraphStyle
(
static
ui
.
ParagraphStyle
paragraphStyle
=
ui
.
ParagraphStyle
(
height:
1.0
,
textDirection:
TextDirection
.
ltr
,
textAlign:
TextAlign
.
left
,
);
);
@override
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
try
{
try
{
context
.
canvas
.
drawRect
(
offset
&
size
,
Paint
()
..
color
=
backgroundColor
);
context
.
canvas
.
drawRect
(
offset
&
size
,
Paint
()
..
color
=
backgroundColor
);
double
width
;
if
(
_paragraph
!=
null
)
{
if
(
_paragraph
!=
null
)
{
// See the comment in the RenderErrorBox constructor. This is not the
double
width
=
size
.
width
;
// code you want to be copying and pasting. :-)
double
left
=
0.0
;
if
(
parent
is
RenderBox
)
{
double
top
=
0.0
;
final
RenderBox
parentBox
=
parent
;
if
(
width
>
padding
.
left
+
minimumWidth
+
padding
.
right
)
{
width
=
parentBox
.
size
.
width
;
width
-=
padding
.
left
+
padding
.
right
;
}
else
{
left
+=
padding
.
left
;
width
=
size
.
width
;
}
}
_paragraph
.
layout
(
ui
.
ParagraphConstraints
(
width:
width
));
_paragraph
.
layout
(
ui
.
ParagraphConstraints
(
width:
width
));
if
(
size
.
height
>
padding
.
top
+
_paragraph
.
height
+
padding
.
bottom
)
{
context
.
canvas
.
drawParagraph
(
_paragraph
,
offset
);
top
+=
padding
.
top
;
}
context
.
canvas
.
drawParagraph
(
_paragraph
,
offset
+
Offset
(
left
,
top
));
}
}
}
catch
(
e
)
{
}
catch
(
e
)
{
// Intentionally left empty.
// Intentionally left empty.
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
b75abd9f
...
@@ -3810,13 +3810,71 @@ typedef ErrorWidgetBuilder = Widget Function(FlutterErrorDetails details);
...
@@ -3810,13 +3810,71 @@ typedef ErrorWidgetBuilder = Widget Function(FlutterErrorDetails details);
/// where the problem lies. Exceptions are also logged to the console, which you
/// where the problem lies. Exceptions are also logged to the console, which you
/// can read using `flutter logs`. The console will also include additional
/// can read using `flutter logs`. The console will also include additional
/// information such as the stack trace for the exception.
/// information such as the stack trace for the exception.
///
/// It is possible to override this widget.
///
/// {@tool snippet --template=freeform}
/// ```dart
/// import 'package:flutter/material.dart';
///
/// void main() {
/// ErrorWidget.builder = (FlutterErrorDetails details) {
/// bool inDebug = false;
/// assert(() { inDebug = true; return true; }());
/// // In debug mode, use the normal error widget which shows
/// // the error message:
/// if (inDebug)
/// return ErrorWidget(details.exception);
/// // In release builds, show a yellow-on-blue message instead:
/// return Container(
/// alignment: Alignment.center,
/// child: Text(
/// 'Error!',
/// style: TextStyle(color: Colors.yellow),
/// textDirection: TextDirection.ltr,
/// ),
/// );
/// };
/// // Here we would normally runApp() the root widget, but to demonstrate
/// // the error handling we artificially fail:
/// return runApp(Builder(
/// builder: (BuildContext context) {
/// throw 'oh no, an error';
/// },
/// ));
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [FlutterError.onError], which can be set to a method that exits the
/// application if that is preferable to showing an error message.
/// * <https://flutter.dev/docs/testing/errors>, more information about error
/// handling in Flutter.
class
ErrorWidget
extends
LeafRenderObjectWidget
{
class
ErrorWidget
extends
LeafRenderObjectWidget
{
/// Creates a widget that displays the given error message.
/// Creates a widget that displays the given exception.
///
/// The message will be the stringification of the given exception, unless
/// computing that value itself throws an exception, in which case it will
/// be the string "Error".
///
/// If this object is inspected from an IDE or the devtools, and the original
/// exception is a [FlutterError] object, the original exception itself will
/// be shown in the inspection output.
ErrorWidget
(
Object
exception
)
ErrorWidget
(
Object
exception
)
:
message
=
_stringify
(
exception
),
:
message
=
_stringify
(
exception
),
_flutterError
=
exception
is
FlutterError
?
exception
:
null
,
_flutterError
=
exception
is
FlutterError
?
exception
:
null
,
super
(
key:
UniqueKey
());
super
(
key:
UniqueKey
());
/// Creates a widget that displays the given error message.
///
/// An explicit [FlutterError] can be provided to be reported to inspection
/// tools. It need not match the message.
ErrorWidget
.
withDetails
({
this
.
message
=
''
,
FlutterError
error
})
:
_flutterError
=
error
,
super
(
key:
UniqueKey
());
/// The configurable factory for [ErrorWidget].
/// The configurable factory for [ErrorWidget].
///
///
/// When an error occurs while building a widget, the broken widget is
/// When an error occurs while building a widget, the broken widget is
...
@@ -3835,17 +3893,28 @@ class ErrorWidget extends LeafRenderObjectWidget {
...
@@ -3835,17 +3893,28 @@ class ErrorWidget extends LeafRenderObjectWidget {
/// corresponds to a [RenderBox] that can handle the most absurd of incoming
/// corresponds to a [RenderBox] that can handle the most absurd of incoming
/// constraints. The default constructor maps to a [RenderErrorBox].
/// constraints. The default constructor maps to a [RenderErrorBox].
///
///
/// The default behavior is to show the exception's message in debug mode,
/// and to show nothing but a gray background in release builds.
///
/// See also:
/// See also:
///
///
/// * [FlutterError.onError], which is typically called with the same
/// * [FlutterError.onError], which is typically called with the same
/// [FlutterErrorDetails] object immediately prior to this callback being
/// [FlutterErrorDetails] object immediately prior to this callback being
/// invoked, and which can also be configured to control how errors are
/// invoked, and which can also be configured to control how errors are
/// reported.
/// reported.
static
ErrorWidgetBuilder
builder
=
(
FlutterErrorDetails
details
)
=>
ErrorWidget
(
details
.
exception
);
/// * <https://flutter.dev/docs/testing/errors>, more information about error
/// handling in Flutter.
static
ErrorWidgetBuilder
builder
=
_defaultErrorWidgetBuilder
;
/// The message to display.
static
Widget
_defaultErrorWidgetBuilder
(
FlutterErrorDetails
details
)
{
final
String
message
;
String
message
=
''
;
final
FlutterError
_flutterError
;
assert
(()
{
message
=
_stringify
(
details
.
exception
)
+
'
\n
See also: https://flutter.dev/docs/testing/errors'
;
return
true
;
}());
final
Object
exception
=
details
.
exception
;
return
ErrorWidget
.
withDetails
(
message:
message
,
error:
exception
is
FlutterError
?
exception
:
null
);
}
static
String
_stringify
(
Object
exception
)
{
static
String
_stringify
(
Object
exception
)
{
try
{
try
{
...
@@ -3856,6 +3925,10 @@ class ErrorWidget extends LeafRenderObjectWidget {
...
@@ -3856,6 +3925,10 @@ class ErrorWidget extends LeafRenderObjectWidget {
return
'Error'
;
return
'Error'
;
}
}
/// The message to display.
final
String
message
;
final
FlutterError
_flutterError
;
@override
@override
RenderBox
createRenderObject
(
BuildContext
context
)
=>
RenderErrorBox
(
message
);
RenderBox
createRenderObject
(
BuildContext
context
)
=>
RenderErrorBox
(
message
);
...
...
packages/flutter/test/rendering/error_test.dart
View file @
b75abd9f
...
@@ -14,9 +14,45 @@ void main() {
...
@@ -14,9 +14,45 @@ void main() {
testWidgets
(
'test draw error paragraph'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'test draw error paragraph'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
ErrorWidget
(
Exception
(
errorMessage
)));
await
tester
.
pumpWidget
(
ErrorWidget
(
Exception
(
errorMessage
)));
expect
(
find
.
byType
(
ErrorWidget
),
paints
..
rect
(
rect:
const
Rect
.
fromLTWH
(
0.0
,
0.0
,
800.0
,
600.0
))
..
paragraph
(
offset:
const
Offset
(
64.0
,
96.0
)));
final
Widget
_error
=
Builder
(
builder:
(
BuildContext
context
)
=>
throw
'pillow'
);
await
tester
.
pumpWidget
(
Center
(
child:
SizedBox
(
width:
100.0
,
child:
_error
)));
expect
(
tester
.
takeException
(),
'pillow'
);
expect
(
find
.
byType
(
ErrorWidget
),
paints
..
rect
(
rect:
const
Rect
.
fromLTWH
(
0.0
,
0.0
,
100.0
,
600.0
))
..
paragraph
(
offset:
const
Offset
(
0.0
,
96.0
)));
await
tester
.
pumpWidget
(
Center
(
child:
SizedBox
(
height:
100.0
,
child:
_error
)));
expect
(
tester
.
takeException
(),
null
);
await
tester
.
pumpWidget
(
Center
(
child:
SizedBox
(
key:
UniqueKey
(),
height:
100.0
,
child:
_error
)));
expect
(
tester
.
takeException
(),
'pillow'
);
expect
(
find
.
byType
(
ErrorWidget
),
paints
..
rect
(
rect:
const
Rect
.
fromLTWH
(
0.0
,
0.0
,
800.0
,
100.0
))
..
paragraph
(
offset:
const
Offset
(
64.0
,
0.0
)));
RenderErrorBox
.
minimumWidth
=
800.0
;
await
tester
.
pumpWidget
(
Center
(
child:
_error
));
expect
(
tester
.
takeException
(),
'pillow'
);
expect
(
find
.
byType
(
ErrorWidget
),
paints
expect
(
find
.
byType
(
ErrorWidget
),
paints
..
rect
(
rect:
const
Rect
.
fromLTWH
(
0.0
,
0.0
,
800.0
,
600.0
))
..
rect
(
rect:
const
Rect
.
fromLTWH
(
0.0
,
0.0
,
800.0
,
600.0
))
..
paragraph
(
offset:
Offset
.
zero
));
..
paragraph
(
offset:
const
Offset
(
0.0
,
96.0
)));
await
tester
.
pumpWidget
(
Center
(
child:
_error
));
expect
(
tester
.
takeException
(),
null
);
expect
(
find
.
byType
(
ErrorWidget
),
paints
..
rect
(
color:
const
Color
(
0xF0900000
))
..
paragraph
());
RenderErrorBox
.
backgroundColor
=
const
Color
(
0xFF112233
);
await
tester
.
pumpWidget
(
Center
(
child:
_error
));
expect
(
tester
.
takeException
(),
null
);
expect
(
find
.
byType
(
ErrorWidget
),
paints
..
rect
(
color:
const
Color
(
0xFF112233
))
..
paragraph
());
});
});
}
}
packages/flutter/test/widgets/error_widget_builder_test.dart
View file @
b75abd9f
...
@@ -5,6 +5,8 @@
...
@@ -5,6 +5,8 @@
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'../rendering/mock_canvas.dart'
;
void
main
(
)
{
void
main
(
)
{
testWidgets
(
'ErrorWidget.builder'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'ErrorWidget.builder'
,
(
WidgetTester
tester
)
async
{
final
ErrorWidgetBuilder
oldBuilder
=
ErrorWidget
.
builder
;
final
ErrorWidgetBuilder
oldBuilder
=
ErrorWidget
.
builder
;
...
@@ -24,4 +26,23 @@ void main() {
...
@@ -24,4 +26,23 @@ void main() {
expect
(
find
.
text
(
'oopsie!'
),
findsOneWidget
);
expect
(
find
.
text
(
'oopsie!'
),
findsOneWidget
);
ErrorWidget
.
builder
=
oldBuilder
;
ErrorWidget
.
builder
=
oldBuilder
;
});
});
testWidgets
(
'ErrorWidget.builder'
,
(
WidgetTester
tester
)
async
{
final
ErrorWidgetBuilder
oldBuilder
=
ErrorWidget
.
builder
;
ErrorWidget
.
builder
=
(
FlutterErrorDetails
details
)
{
return
ErrorWidget
(
''
);
};
await
tester
.
pumpWidget
(
SizedBox
(
child:
Builder
(
builder:
(
BuildContext
context
)
{
throw
'test'
;
},
),
),
);
expect
(
tester
.
takeException
().
toString
(),
'test'
);
expect
(
find
.
byType
(
ErrorWidget
),
isNot
(
paints
..
paragraph
()));
ErrorWidget
.
builder
=
oldBuilder
;
});
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment