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());
 }