Further improve error reporting by wrapping messages.
diff --git a/packages/flutter/lib/src/gestures/binding.dart b/packages/flutter/lib/src/gestures/binding.dart
index b1d9ee56..567d08a 100644
--- a/packages/flutter/lib/src/gestures/binding.dart
+++ b/packages/flutter/lib/src/gestures/binding.dart
@@ -135,7 +135,7 @@
this.event,
this.hitTestEntry,
FlutterInformationCollector informationCollector,
- bool silent
+ bool silent: false
}) : super(
exception: exception,
stack: stack,
diff --git a/packages/flutter/lib/src/gestures/pointer_router.dart b/packages/flutter/lib/src/gestures/pointer_router.dart
index b791b55..37d5cf7 100644
--- a/packages/flutter/lib/src/gestures/pointer_router.dart
+++ b/packages/flutter/lib/src/gestures/pointer_router.dart
@@ -90,7 +90,7 @@
this.route,
this.event,
FlutterInformationCollector informationCollector,
- bool silent
+ bool silent: false
}) : super(
exception: exception,
stack: stack,
diff --git a/packages/flutter/lib/src/material/debug.dart b/packages/flutter/lib/src/material/debug.dart
index ce85329..c890f0c 100644
--- a/packages/flutter/lib/src/material/debug.dart
+++ b/packages/flutter/lib/src/material/debug.dart
@@ -20,7 +20,7 @@
'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.\n'
'The specific widget that could not find a Material ancestor was:\n'
- ' ${context.widget}'
+ ' ${context.widget}\n'
'The ownership chain for the affected widget is:\n'
' ${element.debugGetCreatorChain(10)}'
);
@@ -39,7 +39,7 @@
'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}'
+ ' ${context.widget}\n'
'The ownership chain for the affected widget is:\n'
' ${element.debugGetCreatorChain(10)}'
);
diff --git a/packages/flutter/lib/src/rendering/custom_layout.dart b/packages/flutter/lib/src/rendering/custom_layout.dart
index e87c145..57b73c2 100644
--- a/packages/flutter/lib/src/rendering/custom_layout.dart
+++ b/packages/flutter/lib/src/rendering/custom_layout.dart
@@ -46,7 +46,7 @@
assert(() {
if (child == null) {
throw new FlutterError(
- 'The $this custom multichild layout delegate tried to lay out a non-existent child:\n'
+ 'The $this custom multichild layout delegate tried to lay out a non-existent child.\n'
'There is no child with the id "$childId".'
);
}
@@ -60,7 +60,7 @@
assert(constraints.debugAssertIsNormalized);
} on AssertionError catch (exception) {
throw new FlutterError(
- 'The $this custom multichild layout delegate provided invalid box constraints for the child with id "$childId":\n'
+ 'The $this custom multichild layout delegate provided invalid box constraints for the child with id "$childId".\n'
'$exception\n'
'The minimum width and height must be greater than or equal to zero.\n'
'The maximum width must be greater than or equal to the minimum width.\n'
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index f38fc66..439a7ac 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -889,7 +889,7 @@
informationCollector: (StringBuffer information) {
information.writeln('The following RenderObject was being processed when the exception was fired:\n${this}');
if (debugCreator != null)
- information.writeln('This RenderObject had the following creator:\n$debugCreator');
+ information.writeln('This RenderObject had the following creator:\n $debugCreator');
List<String> descendants = <String>[];
const int maxDepth = 5;
int depth = 0;
@@ -897,8 +897,8 @@
int lines = 0;
void visitor(RenderObject child) {
if (lines < maxLines) {
- descendants.add('${" " * depth}$child');
depth += 1;
+ descendants.add('${" " * depth}$child');
if (depth < maxDepth)
child.visitChildren(visitor);
depth -= 1;
@@ -2155,7 +2155,7 @@
String context,
this.renderObject,
FlutterInformationCollector informationCollector,
- bool silent
+ bool silent: false
}) : super(
exception: exception,
stack: stack,
diff --git a/packages/flutter/lib/src/services/assertions.dart b/packages/flutter/lib/src/services/assertions.dart
index d312ebb..9cc2b5b 100644
--- a/packages/flutter/lib/src/services/assertions.dart
+++ b/packages/flutter/lib/src/services/assertions.dart
@@ -124,6 +124,8 @@
static int _errorCount = 0;
+ static const int _kWrapWidth = 120;
+
/// Prints the given exception details to the console.
///
/// The first time this is called, it dumps a very verbose message to the
@@ -135,7 +137,7 @@
static void dumpErrorToConsole(FlutterErrorDetails details) {
assert(details != null);
assert(details.exception != null);
- bool reportError = !details.silent;
+ bool reportError = details.silent != true; // could be null
assert(() {
// In checked mode, we ignore the "silent" flag.
reportError = true;
@@ -144,19 +146,26 @@
if (!reportError)
return;
if (_errorCount == 0) {
- final String header = '-- EXCEPTION CAUGHT BY ${details.library} '.toUpperCase();
- const String footer = '------------------------------------------------------------------------';
- debugPrint('$header${"-" * (footer.length - header.length)}');
- debugPrint('The following exception was raised${ details.context != null ? " ${details.context}" : ""}:');
- debugPrint('${details.exception}');
+ final String header = '\u2550\u2550\u2561 EXCEPTION CAUGHT BY ${details.library} \u255E'.toUpperCase();
+ final String footer = '\u2501' * _kWrapWidth;
+ debugPrint('$header${"\u2550" * (footer.length - header.length)}');
+ debugPrint('The following exception was raised${ details.context != null ? " ${details.context}" : ""}:', wrapWidth: _kWrapWidth);
+ debugPrint('${details.exception}', wrapWidth: _kWrapWidth);
+ if ((details.exception is AssertionError) && (details.exception is! FlutterError)) {
+ debugPrint('Either the assertion indicates an error in the framework itself, or we should '
+ 'provide substantially more information in this error message to help you determine '
+ 'and fix the underlying cause.', wrapWidth: _kWrapWidth);
+ debugPrint('In either case, please report this assertion by filing a bug on GitHub:', wrapWidth: _kWrapWidth);
+ debugPrint(' https://github.com/flutter/flutter/issues/new');
+ }
if (details.informationCollector != null) {
StringBuffer information = new StringBuffer();
details.informationCollector(information);
- debugPrint(information.toString());
+ debugPrint(information.toString(), wrapWidth: _kWrapWidth);
}
if (details.stack != null) {
- debugPrint('Stack trace:');
- debugPrint('${details.stack}$footer');
+ debugPrint('Stack trace:', wrapWidth: _kWrapWidth);
+ debugPrint('${details.stack}$footer'); // StackTrace objects include a trailing newline
} else {
debugPrint(footer);
}
diff --git a/packages/flutter/lib/src/services/print.dart b/packages/flutter/lib/src/services/print.dart
index 4d594a6..c96ad37 100644
--- a/packages/flutter/lib/src/services/print.dart
+++ b/packages/flutter/lib/src/services/print.dart
@@ -8,13 +8,20 @@
/// Prints a message to the console, which you can access using the "flutter"
/// tool's "logs" command ("flutter logs").
///
+/// If a wrapWidth is provided, each line of the message is word-wrapped to that
+/// width. (Lines may be separated by newline characters, as in '\n'.)
+///
/// This function very crudely attempts to throttle the rate at which messages
/// are sent to avoid data loss on Android. This means that interleaving calls
/// to this function (directly or indirectly via [debugDumpRenderTree] or
/// [debugDumpApp]) and to the Dart [print] method can result in out-of-order
/// messages in the logs.
-void debugPrint(String message) {
- _debugPrintBuffer.addAll(message.split('\n'));
+void debugPrint(String message, { int wrapWidth }) {
+ if (wrapWidth != null) {
+ _debugPrintBuffer.addAll(message.split('\n').expand((String line) => _wordWrap(line, wrapWidth)));
+ } else {
+ _debugPrintBuffer.addAll(message.split('\n'));
+ }
if (!_debugPrintScheduled)
_debugPrintTask();
}
@@ -44,7 +51,81 @@
_debugPrintStopwatch.start();
}
}
+final RegExp _indentPattern = new RegExp('^ *(?:[-+*] |[0-9]+[.):] )?');
+enum _WordWrapParseMode { inSpace, inWord, atBreak }
+Iterable<String> _wordWrap(String message, int width) sync* {
+ if (message.length < width) {
+ yield message;
+ return;
+ }
+ Match prefixMatch = _indentPattern.matchAsPrefix(message);
+ String prefix = ' ' * prefixMatch.group(0).length;
+ int start = 0;
+ int startForLengthCalculations = 0;
+ bool addPrefix = false;
+ int index = prefix.length;
+ _WordWrapParseMode mode = _WordWrapParseMode.inSpace;
+ int lastWordStart;
+ int lastWordEnd;
+ while (true) {
+ switch (mode) {
+ case _WordWrapParseMode.inSpace: // at start of break point (or start of line); can't break until next break
+ while ((index < message.length) && (message[index] == ' '))
+ index += 1;
+ lastWordStart = index;
+ mode = _WordWrapParseMode.inWord;
+ break;
+ case _WordWrapParseMode.inWord: // looking for a good break point
+ while ((index < message.length) && (message[index] != ' '))
+ index += 1;
+ mode = _WordWrapParseMode.atBreak;
+ break;
+ case _WordWrapParseMode.atBreak: // at start of break point
+ if ((index - startForLengthCalculations > width) || (index == message.length)) {
+ // we are over the width line, so break
+ if ((index - startForLengthCalculations <= width) || (lastWordEnd == null)) {
+ // we should use this point, before either it doesn't actually go over the end (last line), or it does, but there was no earlier break point
+ lastWordEnd = index;
+ }
+ if (addPrefix) {
+ yield prefix + message.substring(start, lastWordEnd);
+ } else {
+ yield message.substring(start, lastWordEnd);
+ addPrefix = true;
+ }
+ if (lastWordEnd >= message.length)
+ return;
+ // just yielded a line
+ if (lastWordEnd == index) {
+ // we broke at current position
+ // eat all the spaces, then set our start point
+ while ((index < message.length) && (message[index] == ' '))
+ index += 1;
+ start = index;
+ mode = _WordWrapParseMode.inWord;
+ } else {
+ // we broke at the previous break point, and we're at the start of a new one
+ assert(lastWordStart > lastWordEnd);
+ start = lastWordStart;
+ mode = _WordWrapParseMode.atBreak;
+ }
+ startForLengthCalculations = start - prefix.length;
+ assert(addPrefix);
+ lastWordEnd = null;
+ } else {
+ // save this break point, we're not yet over the line width
+ lastWordEnd = index;
+ // skip to the end of this break point
+ mode = _WordWrapParseMode.inSpace;
+ }
+ break;
+ }
+ }
+}
+/// Dump the current stack to the console using [debugPrint].
+///
+/// The current stack is obtained using [StackTrace.current].
void debugPrintStack() {
debugPrint(StackTrace.current.toString());
}