Text Painting Fuzzer (#12813)

Various improvements (in particular a new painting fuzzer) to the text manual test.

Some additional documentation.

A fix to Stack to remove an LTR bias: make unpositioned children apply "alignment".

Some more debugging information on RichText and Text.

A fix to the flutter tool to not crash when an RPC call throws an exception.
diff --git a/dev/manual_tests/lib/text.dart b/dev/manual_tests/lib/text.dart
index 2b90e2f..11b3a63 100644
--- a/dev/manual_tests/lib/text.dart
+++ b/dev/manual_tests/lib/text.dart
@@ -6,6 +6,7 @@
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
 import 'package:flutter/scheduler.dart';
 
 int seed = 0;
@@ -18,7 +19,8 @@
       'underlines': (BuildContext context) => const Underlines(),
       'fallback': (BuildContext context) => const Fallback(),
       'fuzzer': (BuildContext context) => new Fuzzer(seed: seed),
-      'zalgo': (BuildContext context) => const Zalgo(),
+      'zalgo': (BuildContext context) => new Zalgo(seed: seed),
+      'painting': (BuildContext context) => new Painting(seed: seed),
     },
   ));
 }
@@ -64,6 +66,12 @@
                   textColor: Colors.white,
                   onPressed: () { Navigator.of(context).pushNamed('zalgo'); },
                 ),
+                new FlatButton(
+                  child: const Text('Painting Fuzzer'),
+                  color: Colors.blueGrey.shade200,
+                  textColor: Colors.black,
+                  onPressed: () { Navigator.of(context).pushNamed('painting'); },
+                ),
               ],
             ),
           ),
@@ -329,7 +337,7 @@
   }
 
   String _createRandomText() {
-    switch (_random.nextInt(80)) {
+    switch (_random.nextInt(90)) {
       case 0:
       case 1:
       case 2:
@@ -448,6 +456,25 @@
         return '⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌';
       case 73:
         return 'コンニチハ';
+      case 74:
+      case 75:
+      case 76:
+      case 77:
+      case 78:
+      case 79:
+      case 80:
+      case 81:
+      case 82:
+        final StringBuffer buffer = new StringBuffer();
+        final int targetLength = _random.nextInt(8) + 1;
+        for (int index = 0; index < targetLength; index += 1) {
+          if (_random.nextInt(20) > 0) {
+            buffer.writeCharCode(randomCharacter(_random));
+          } else {
+            buffer.write(zalgo(_random, _random.nextInt(2) + 1, includeSpacingCombiningMarks: true));
+          }
+        }
+        return buffer.toString();
     }
     return null;
   }
@@ -641,7 +668,7 @@
           ),
           new Material(
             child: new Padding(
-              padding: const EdgeInsets.symmetric(horizontal: 20.0),
+              padding: const EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0),
               child: new Row(
                 crossAxisAlignment: CrossAxisAlignment.end,
                 children: <Widget>[
@@ -701,10 +728,16 @@
   }
 
   bool _allowSpacing = false;
+  bool _varyBase = false;
 
   void _update(Duration duration) {
     setState(() {
-      _text = zalgo(_random, 6 + _random.nextInt(10), includeSpacingCombiningMarks: _allowSpacing);
+      _text = zalgo(
+        _random,
+        6 + _random.nextInt(10),
+        includeSpacingCombiningMarks: _allowSpacing,
+        base: _varyBase ? null : 'O',
+      );
     });
   }
 
@@ -750,6 +783,17 @@
                   onChanged: (bool value) {
                     setState(() {
                       _allowSpacing = value;
+                      _random = new math.Random(widget.seed); // reset for reproducability
+                    });
+                  },
+                ),
+                new SwitchListTile(
+                  title: const Text('Vary base character'),
+                  value: _varyBase,
+                  onChanged: (bool value) {
+                    setState(() {
+                      _varyBase = value;
+                      _random = new math.Random(widget.seed); // reset for reproducability
                     });
                   },
                 ),
@@ -762,7 +806,179 @@
   }
 }
 
-String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks: false }) {
+class Painting extends StatefulWidget {
+  const Painting({ Key key, this.seed }) : super(key: key);
+
+  final int seed;
+
+  @override
+  _PaintingState createState() => new _PaintingState();
+}
+
+class _PaintingState extends State<Painting> with SingleTickerProviderStateMixin {
+  String _text;
+  Ticker _ticker;
+  math.Random _random;
+
+  @override
+  void initState() {
+    super.initState();
+    _random = new math.Random(widget.seed); // providing a seed is important for reproducability
+    _ticker = createTicker(_update)..start();
+    _update(null);
+  }
+
+  @override
+  void dispose() {
+    _ticker.dispose();
+    super.dispose();
+  }
+
+  final GlobalKey intrinsicKey = new GlobalKey();
+  final GlobalKey controlKey = new GlobalKey();
+
+  bool _ellipsize = false;
+
+  void _update(Duration duration) {
+    setState(() {
+      final StringBuffer buffer = new StringBuffer();
+      final int targetLength = _random.nextInt(20) + (_ellipsize ? MediaQuery.of(context).size.width.round() : 1);
+      for (int index = 0; index < targetLength; index += 1) {
+        if (_random.nextInt(5) > 0) {
+          buffer.writeCharCode(randomCharacter(_random));
+        } else {
+          buffer.write(zalgo(_random, _random.nextInt(2) + 1, includeSpacingCombiningMarks: true));
+        }
+      }
+      _text = buffer.toString();
+    });
+    SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
+      if (mounted && intrinsicKey.currentContext.size.height != controlKey.currentContext.size.height) {
+        debugPrint('Found some text that unexpectedly renders at different heights.');
+        debugPrint('Text: $_text');
+        debugPrint(_text.runes.map((int index) => 'U+' + index.toRadixString(16).padLeft(4, '0')).join(' '));
+        setState(() {
+          _ticker.stop();
+        });
+      }
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final Size size = MediaQuery.of(context).size;
+    return new Container(
+      color: Colors.black,
+      child: new Column(
+        crossAxisAlignment: CrossAxisAlignment.stretch,
+        children: <Widget>[
+          new Expanded(
+            child: new Padding(
+              padding: new EdgeInsets.only(top: size.height * 0.1),
+              child: new Stack(
+                alignment: Alignment.center,
+                children: <Widget>[
+                  new Positioned(
+                    top: 0.0,
+                    left: 0.0,
+                    right: 0.0,
+                    child: new Align(
+                      alignment: Alignment.topCenter,
+                      child: new IntrinsicWidth( // to test shrink-wrap vs rendering
+                        child: new RichText(
+                          key: intrinsicKey,
+                          textAlign: TextAlign.center,
+                          overflow: _ellipsize ? TextOverflow.ellipsis : TextOverflow.clip,
+                          text: new TextSpan(
+                            text: _text,
+                            style: const TextStyle(
+                              inherit: false,
+                              fontSize: 28.0,
+                              color: Colors.red,
+                            ),
+                          ),
+                        ),
+                      ),
+                    ),
+                  ),
+                  new Positioned(
+                    top: 0.0,
+                    left: 0.0,
+                    right: 0.0,
+                    child: new RichText(
+                      key: controlKey,
+                      textAlign: TextAlign.center,
+                      overflow: _ellipsize ? TextOverflow.ellipsis : TextOverflow.clip,
+                      text: new TextSpan(
+                        text: _text,
+                        style: const TextStyle(
+                          inherit: false,
+                          fontSize: 28.0,
+                          color: Colors.white,
+                        ),
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ),
+          new Material(
+            child: new Column(
+              children: <Widget>[
+                new SwitchListTile(
+                  title: const Text('Enable Fuzzer'),
+                  value: _ticker.isActive,
+                  onChanged: (bool value) {
+                    setState(() {
+                      if (value) {
+                        _ticker.start();
+                      } else {
+                        _ticker.stop();
+                      }
+                    });
+                  },
+                ),
+                new SwitchListTile(
+                  title: const Text('Enable Ellipses'),
+                  value: _ellipsize,
+                  onChanged: (bool value) {
+                    setState(() {
+                      _ellipsize = value;
+                      _random = new math.Random(widget.seed); // reset for reproducability
+                      if (!_ticker.isActive)
+                        _update(null);
+                    });
+                  },
+                ),
+                const ListTile(
+                  title: const Text('There should be no red visible.'),
+                ),
+                new ButtonBar(
+                  children: <Widget>[
+                    new FlatButton(
+                      onPressed: _ticker.isActive ? null : () => _update(null),
+                      child: const Text('ITERATE'),
+                    ),
+                    new FlatButton(
+                      onPressed: _ticker.isActive ? null : () {
+                        print('The currently visible text is: $_text');
+                        print(_text.runes.map((int value) => 'U+${value.toRadixString(16).padLeft(4, '0').toUpperCase()}').join(' '));
+                      },
+                      child: const Text('DUMP TEXT TO LOGS'),
+                    ),
+                  ],
+                ),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+String zalgo(math.Random random, int targetLength, { bool includeSpacingCombiningMarks: false, String base }) {
   // The following three tables are derived from UnicodeData.txt:
   //   http://unicode.org/Public/UNIDATA/UnicodeData.txt
   // There are three groups, character classes Mc, Me, and Mn.
@@ -1088,7 +1304,7 @@
   int combiningCount = enclosingCombiningMarks.length + nonspacingCombiningMarks.length;
   if (includeSpacingCombiningMarks)
     combiningCount += spacingCombiningMarks.length;
-  for (int count = 0; count < target; count += 1) {
+  for (int count = 0; count < targetLength; count += 1) {
     int characterCode = random.nextInt(combiningCount);
     if (characterCode < enclosingCombiningMarks.length) {
       these.add(enclosingCombiningMarks[characterCode]);
@@ -1102,11 +1318,717 @@
       }
     }
   }
-  final List<int> characters = <int>[0x41 + random.nextInt(26)];
+  base ??= new String.fromCharCode(randomCharacter(random));
+  final List<int> characters = <int>[];
   characters.addAll(these);
-  return new String.fromCharCodes(characters);
+  return base + new String.fromCharCodes(characters);
 }
 
 T pickFromList<T>(math.Random random, List<T> list) {
   return list[random.nextInt(list.length)];
 }
+
+class Range {
+  const Range(this.start, this.end);
+  final int start;
+  final int end;
+}
+
+int randomCharacter(math.Random random) {
+  // all ranges of non-control, non-combining characters
+  const List<Range> characterRanges = const <Range>[
+    const Range(0x00020, 0x0007e),
+    const Range(0x000a0, 0x000ac),
+    const Range(0x000ae, 0x002ff),
+    const Range(0x00370, 0x00377),
+    const Range(0x0037a, 0x0037f),
+    const Range(0x00384, 0x0038a),
+    const Range(0x0038c, 0x0038c),
+    const Range(0x0038e, 0x003a1),
+    const Range(0x003a3, 0x00482),
+    const Range(0x0048a, 0x0052f),
+    const Range(0x00531, 0x00556),
+    const Range(0x00559, 0x0055f),
+    const Range(0x00561, 0x00587),
+    const Range(0x00589, 0x0058a),
+    const Range(0x0058d, 0x0058f),
+    const Range(0x005be, 0x005be),
+    const Range(0x005c0, 0x005c0),
+    const Range(0x005c3, 0x005c3),
+    const Range(0x005c6, 0x005c6),
+    const Range(0x005d0, 0x005ea),
+    const Range(0x005f0, 0x005f4),
+    const Range(0x00606, 0x0060f),
+    const Range(0x0061b, 0x0061b),
+    const Range(0x0061e, 0x0064a),
+    const Range(0x00660, 0x0066f),
+    const Range(0x00671, 0x006d5),
+    const Range(0x006de, 0x006de),
+    const Range(0x006e5, 0x006e6),
+    const Range(0x006e9, 0x006e9),
+    const Range(0x006ee, 0x0070d),
+    const Range(0x00710, 0x00710),
+    const Range(0x00712, 0x0072f),
+    const Range(0x0074d, 0x007a5),
+    const Range(0x007b1, 0x007b1),
+    const Range(0x007c0, 0x007ea),
+    const Range(0x007f4, 0x007fa),
+    const Range(0x00800, 0x00815),
+    const Range(0x0081a, 0x0081a),
+    const Range(0x00824, 0x00824),
+    const Range(0x00828, 0x00828),
+    const Range(0x00830, 0x0083e),
+    const Range(0x00840, 0x00858),
+    const Range(0x0085e, 0x0085e),
+    const Range(0x00860, 0x0086a),
+    const Range(0x008a0, 0x008b4),
+    const Range(0x008b6, 0x008bd),
+    const Range(0x00904, 0x00939),
+    const Range(0x0093d, 0x0093d),
+    const Range(0x00950, 0x00950),
+    const Range(0x00958, 0x00961),
+    const Range(0x00964, 0x00980),
+    const Range(0x00985, 0x0098c),
+    const Range(0x0098f, 0x00990),
+    const Range(0x00993, 0x009a8),
+    const Range(0x009aa, 0x009b0),
+    const Range(0x009b2, 0x009b2),
+    const Range(0x009b6, 0x009b9),
+    const Range(0x009bd, 0x009bd),
+    const Range(0x009ce, 0x009ce),
+    const Range(0x009dc, 0x009dd),
+    const Range(0x009df, 0x009e1),
+    const Range(0x009e6, 0x009fd),
+    const Range(0x00a05, 0x00a0a),
+    const Range(0x00a0f, 0x00a10),
+    const Range(0x00a13, 0x00a28),
+    const Range(0x00a2a, 0x00a30),
+    const Range(0x00a32, 0x00a33),
+    const Range(0x00a35, 0x00a36),
+    const Range(0x00a38, 0x00a39),
+    const Range(0x00a59, 0x00a5c),
+    const Range(0x00a5e, 0x00a5e),
+    const Range(0x00a66, 0x00a6f),
+    const Range(0x00a72, 0x00a74),
+    const Range(0x00a85, 0x00a8d),
+    const Range(0x00a8f, 0x00a91),
+    const Range(0x00a93, 0x00aa8),
+    const Range(0x00aaa, 0x00ab0),
+    const Range(0x00ab2, 0x00ab3),
+    const Range(0x00ab5, 0x00ab9),
+    const Range(0x00abd, 0x00abd),
+    const Range(0x00ad0, 0x00ad0),
+    const Range(0x00ae0, 0x00ae1),
+    const Range(0x00ae6, 0x00af1),
+    const Range(0x00af9, 0x00af9),
+    const Range(0x00b05, 0x00b0c),
+    const Range(0x00b0f, 0x00b10),
+    const Range(0x00b13, 0x00b28),
+    const Range(0x00b2a, 0x00b30),
+    const Range(0x00b32, 0x00b33),
+    const Range(0x00b35, 0x00b39),
+    const Range(0x00b3d, 0x00b3d),
+    const Range(0x00b5c, 0x00b5d),
+    const Range(0x00b5f, 0x00b61),
+    const Range(0x00b66, 0x00b77),
+    const Range(0x00b83, 0x00b83),
+    const Range(0x00b85, 0x00b8a),
+    const Range(0x00b8e, 0x00b90),
+    const Range(0x00b92, 0x00b95),
+    const Range(0x00b99, 0x00b9a),
+    const Range(0x00b9c, 0x00b9c),
+    const Range(0x00b9e, 0x00b9f),
+    const Range(0x00ba3, 0x00ba4),
+    const Range(0x00ba8, 0x00baa),
+    const Range(0x00bae, 0x00bb9),
+    const Range(0x00bd0, 0x00bd0),
+    const Range(0x00be6, 0x00bfa),
+    const Range(0x00c05, 0x00c0c),
+    const Range(0x00c0e, 0x00c10),
+    const Range(0x00c12, 0x00c28),
+    const Range(0x00c2a, 0x00c39),
+    const Range(0x00c3d, 0x00c3d),
+    const Range(0x00c58, 0x00c5a),
+    const Range(0x00c60, 0x00c61),
+    const Range(0x00c66, 0x00c6f),
+    const Range(0x00c78, 0x00c80),
+    const Range(0x00c85, 0x00c8c),
+    const Range(0x00c8e, 0x00c90),
+    const Range(0x00c92, 0x00ca8),
+    const Range(0x00caa, 0x00cb3),
+    const Range(0x00cb5, 0x00cb9),
+    const Range(0x00cbd, 0x00cbd),
+    const Range(0x00cde, 0x00cde),
+    const Range(0x00ce0, 0x00ce1),
+    const Range(0x00ce6, 0x00cef),
+    const Range(0x00cf1, 0x00cf2),
+    const Range(0x00d05, 0x00d0c),
+    const Range(0x00d0e, 0x00d10),
+    const Range(0x00d12, 0x00d3a),
+    const Range(0x00d3d, 0x00d3d),
+    const Range(0x00d4e, 0x00d4f),
+    const Range(0x00d54, 0x00d56),
+    const Range(0x00d58, 0x00d61),
+    const Range(0x00d66, 0x00d7f),
+    const Range(0x00d85, 0x00d96),
+    const Range(0x00d9a, 0x00db1),
+    const Range(0x00db3, 0x00dbb),
+    const Range(0x00dbd, 0x00dbd),
+    const Range(0x00dc0, 0x00dc6),
+    const Range(0x00de6, 0x00def),
+    const Range(0x00df4, 0x00df4),
+    const Range(0x00e01, 0x00e30),
+    const Range(0x00e32, 0x00e33),
+    const Range(0x00e3f, 0x00e46),
+    const Range(0x00e4f, 0x00e5b),
+    const Range(0x00e81, 0x00e82),
+    const Range(0x00e84, 0x00e84),
+    const Range(0x00e87, 0x00e88),
+    const Range(0x00e8a, 0x00e8a),
+    const Range(0x00e8d, 0x00e8d),
+    const Range(0x00e94, 0x00e97),
+    const Range(0x00e99, 0x00e9f),
+    const Range(0x00ea1, 0x00ea3),
+    const Range(0x00ea5, 0x00ea5),
+    const Range(0x00ea7, 0x00ea7),
+    const Range(0x00eaa, 0x00eab),
+    const Range(0x00ead, 0x00eb0),
+    const Range(0x00eb2, 0x00eb3),
+    const Range(0x00ebd, 0x00ebd),
+    const Range(0x00ec0, 0x00ec4),
+    const Range(0x00ec6, 0x00ec6),
+    const Range(0x00ed0, 0x00ed9),
+    const Range(0x00edc, 0x00edf),
+    const Range(0x00f00, 0x00f17),
+    const Range(0x00f1a, 0x00f34),
+    const Range(0x00f36, 0x00f36),
+    const Range(0x00f38, 0x00f38),
+    const Range(0x00f3a, 0x00f3d),
+    const Range(0x00f40, 0x00f47),
+    const Range(0x00f49, 0x00f6c),
+    const Range(0x00f85, 0x00f85),
+    const Range(0x00f88, 0x00f8c),
+    const Range(0x00fbe, 0x00fc5),
+    const Range(0x00fc7, 0x00fcc),
+    const Range(0x00fce, 0x00fda),
+    const Range(0x01000, 0x0102a),
+    const Range(0x0103f, 0x01055),
+    const Range(0x0105a, 0x0105d),
+    const Range(0x01061, 0x01061),
+    const Range(0x01065, 0x01066),
+    const Range(0x0106e, 0x01070),
+    const Range(0x01075, 0x01081),
+    const Range(0x0108e, 0x0108e),
+    const Range(0x01090, 0x01099),
+    const Range(0x0109e, 0x010c5),
+    const Range(0x010c7, 0x010c7),
+    const Range(0x010cd, 0x010cd),
+    const Range(0x010d0, 0x01248),
+    const Range(0x0124a, 0x0124d),
+    const Range(0x01250, 0x01256),
+    const Range(0x01258, 0x01258),
+    const Range(0x0125a, 0x0125d),
+    const Range(0x01260, 0x01288),
+    const Range(0x0128a, 0x0128d),
+    const Range(0x01290, 0x012b0),
+    const Range(0x012b2, 0x012b5),
+    const Range(0x012b8, 0x012be),
+    const Range(0x012c0, 0x012c0),
+    const Range(0x012c2, 0x012c5),
+    const Range(0x012c8, 0x012d6),
+    const Range(0x012d8, 0x01310),
+    const Range(0x01312, 0x01315),
+    const Range(0x01318, 0x0135a),
+    const Range(0x01360, 0x0137c),
+    const Range(0x01380, 0x01399),
+    const Range(0x013a0, 0x013f5),
+    const Range(0x013f8, 0x013fd),
+    const Range(0x01400, 0x0169c),
+    const Range(0x016a0, 0x016f8),
+    const Range(0x01700, 0x0170c),
+    const Range(0x0170e, 0x01711),
+    const Range(0x01720, 0x01731),
+    const Range(0x01735, 0x01736),
+    const Range(0x01740, 0x01751),
+    const Range(0x01760, 0x0176c),
+    const Range(0x0176e, 0x01770),
+    const Range(0x01780, 0x017b3),
+    const Range(0x017d4, 0x017dc),
+    const Range(0x017e0, 0x017e9),
+    const Range(0x017f0, 0x017f9),
+    const Range(0x01800, 0x0180a),
+    const Range(0x01810, 0x01819),
+    const Range(0x01820, 0x01877),
+    const Range(0x01880, 0x01884),
+    const Range(0x01887, 0x018a8),
+    const Range(0x018aa, 0x018aa),
+    const Range(0x018b0, 0x018f5),
+    const Range(0x01900, 0x0191e),
+    const Range(0x01940, 0x01940),
+    const Range(0x01944, 0x0196d),
+    const Range(0x01970, 0x01974),
+    const Range(0x01980, 0x019ab),
+    const Range(0x019b0, 0x019c9),
+    const Range(0x019d0, 0x019da),
+    const Range(0x019de, 0x01a16),
+    const Range(0x01a1e, 0x01a54),
+    const Range(0x01a80, 0x01a89),
+    const Range(0x01a90, 0x01a99),
+    const Range(0x01aa0, 0x01aad),
+    const Range(0x01b05, 0x01b33),
+    const Range(0x01b45, 0x01b4b),
+    const Range(0x01b50, 0x01b6a),
+    const Range(0x01b74, 0x01b7c),
+    const Range(0x01b83, 0x01ba0),
+    const Range(0x01bae, 0x01be5),
+    const Range(0x01bfc, 0x01c23),
+    const Range(0x01c3b, 0x01c49),
+    const Range(0x01c4d, 0x01c88),
+    const Range(0x01cc0, 0x01cc7),
+    const Range(0x01cd3, 0x01cd3),
+    const Range(0x01ce9, 0x01cec),
+    const Range(0x01cee, 0x01cf1),
+    const Range(0x01cf5, 0x01cf6),
+    const Range(0x01d00, 0x01dbf),
+    const Range(0x01e00, 0x01f15),
+    const Range(0x01f18, 0x01f1d),
+    const Range(0x01f20, 0x01f45),
+    const Range(0x01f48, 0x01f4d),
+    const Range(0x01f50, 0x01f57),
+    const Range(0x01f59, 0x01f59),
+    const Range(0x01f5b, 0x01f5b),
+    const Range(0x01f5d, 0x01f5d),
+    const Range(0x01f5f, 0x01f7d),
+    const Range(0x01f80, 0x01fb4),
+    const Range(0x01fb6, 0x01fc4),
+    const Range(0x01fc6, 0x01fd3),
+    const Range(0x01fd6, 0x01fdb),
+    const Range(0x01fdd, 0x01fef),
+    const Range(0x01ff2, 0x01ff4),
+    const Range(0x01ff6, 0x01ffe),
+    const Range(0x02000, 0x0200a),
+    const Range(0x02010, 0x02029),
+    const Range(0x0202f, 0x0205f),
+    const Range(0x02070, 0x02071),
+    const Range(0x02074, 0x0208e),
+    const Range(0x02090, 0x0209c),
+    const Range(0x020a0, 0x020bf),
+    const Range(0x02100, 0x0218b),
+    const Range(0x02190, 0x02426),
+    const Range(0x02440, 0x0244a),
+    const Range(0x02460, 0x02b73),
+    const Range(0x02b76, 0x02b95),
+    const Range(0x02b98, 0x02bb9),
+    const Range(0x02bbd, 0x02bc8),
+    const Range(0x02bca, 0x02bd2),
+    const Range(0x02bec, 0x02bef),
+    const Range(0x02c00, 0x02c2e),
+    const Range(0x02c30, 0x02c5e),
+    const Range(0x02c60, 0x02cee),
+    const Range(0x02cf2, 0x02cf3),
+    const Range(0x02cf9, 0x02d25),
+    const Range(0x02d27, 0x02d27),
+    const Range(0x02d2d, 0x02d2d),
+    const Range(0x02d30, 0x02d67),
+    const Range(0x02d6f, 0x02d70),
+    const Range(0x02d80, 0x02d96),
+    const Range(0x02da0, 0x02da6),
+    const Range(0x02da8, 0x02dae),
+    const Range(0x02db0, 0x02db6),
+    const Range(0x02db8, 0x02dbe),
+    const Range(0x02dc0, 0x02dc6),
+    const Range(0x02dc8, 0x02dce),
+    const Range(0x02dd0, 0x02dd6),
+    const Range(0x02dd8, 0x02dde),
+    const Range(0x02e00, 0x02e49),
+    const Range(0x02e80, 0x02e99),
+    const Range(0x02e9b, 0x02ef3),
+    const Range(0x02f00, 0x02fd5),
+    const Range(0x02ff0, 0x02ffb),
+    const Range(0x03000, 0x03029),
+    const Range(0x03030, 0x0303f),
+    const Range(0x03041, 0x03096),
+    const Range(0x0309b, 0x030ff),
+    const Range(0x03105, 0x0312e),
+    const Range(0x03131, 0x0318e),
+    const Range(0x03190, 0x031ba),
+    const Range(0x031c0, 0x031e3),
+    const Range(0x031f0, 0x0321e),
+    const Range(0x03220, 0x032fe),
+    const Range(0x03300, 0x04db5),
+    const Range(0x04dc0, 0x09fea),
+    const Range(0x0a000, 0x0a48c),
+    const Range(0x0a490, 0x0a4c6),
+    const Range(0x0a4d0, 0x0a62b),
+    const Range(0x0a640, 0x0a66e),
+    const Range(0x0a673, 0x0a673),
+    const Range(0x0a67e, 0x0a69d),
+    const Range(0x0a6a0, 0x0a6ef),
+    const Range(0x0a6f2, 0x0a6f7),
+    const Range(0x0a700, 0x0a7ae),
+    const Range(0x0a7b0, 0x0a7b7),
+    const Range(0x0a7f7, 0x0a801),
+    const Range(0x0a803, 0x0a805),
+    const Range(0x0a807, 0x0a80a),
+    const Range(0x0a80c, 0x0a822),
+    const Range(0x0a828, 0x0a82b),
+    const Range(0x0a830, 0x0a839),
+    const Range(0x0a840, 0x0a877),
+    const Range(0x0a882, 0x0a8b3),
+    const Range(0x0a8ce, 0x0a8d9),
+    const Range(0x0a8f2, 0x0a8fd),
+    const Range(0x0a900, 0x0a925),
+    const Range(0x0a92e, 0x0a946),
+    const Range(0x0a95f, 0x0a97c),
+    const Range(0x0a984, 0x0a9b2),
+    const Range(0x0a9c1, 0x0a9cd),
+    const Range(0x0a9cf, 0x0a9d9),
+    const Range(0x0a9de, 0x0a9e4),
+    const Range(0x0a9e6, 0x0a9fe),
+    const Range(0x0aa00, 0x0aa28),
+    const Range(0x0aa40, 0x0aa42),
+    const Range(0x0aa44, 0x0aa4b),
+    const Range(0x0aa50, 0x0aa59),
+    const Range(0x0aa5c, 0x0aa7a),
+    const Range(0x0aa7e, 0x0aaaf),
+    const Range(0x0aab1, 0x0aab1),
+    const Range(0x0aab5, 0x0aab6),
+    const Range(0x0aab9, 0x0aabd),
+    const Range(0x0aac0, 0x0aac0),
+    const Range(0x0aac2, 0x0aac2),
+    const Range(0x0aadb, 0x0aaea),
+    const Range(0x0aaf0, 0x0aaf4),
+    const Range(0x0ab01, 0x0ab06),
+    const Range(0x0ab09, 0x0ab0e),
+    const Range(0x0ab11, 0x0ab16),
+    const Range(0x0ab20, 0x0ab26),
+    const Range(0x0ab28, 0x0ab2e),
+    const Range(0x0ab30, 0x0ab65),
+    const Range(0x0ab70, 0x0abe2),
+    const Range(0x0abeb, 0x0abeb),
+    const Range(0x0abf0, 0x0abf9),
+    const Range(0x0ac00, 0x0d7a3),
+    const Range(0x0d7b0, 0x0d7c6),
+    const Range(0x0d7cb, 0x0d7fb),
+    const Range(0x0f900, 0x0fa6d),
+    const Range(0x0fa70, 0x0fad9),
+    const Range(0x0fb00, 0x0fb06),
+    const Range(0x0fb13, 0x0fb17),
+    const Range(0x0fb1d, 0x0fb1d),
+    const Range(0x0fb1f, 0x0fb36),
+    const Range(0x0fb38, 0x0fb3c),
+    const Range(0x0fb3e, 0x0fb3e),
+    const Range(0x0fb40, 0x0fb41),
+    const Range(0x0fb43, 0x0fb44),
+    const Range(0x0fb46, 0x0fbc1),
+    const Range(0x0fbd3, 0x0fd3f),
+    const Range(0x0fd50, 0x0fd8f),
+    const Range(0x0fd92, 0x0fdc7),
+    const Range(0x0fdf0, 0x0fdfd),
+    const Range(0x0fe10, 0x0fe19),
+    const Range(0x0fe30, 0x0fe52),
+    const Range(0x0fe54, 0x0fe66),
+    const Range(0x0fe68, 0x0fe6b),
+    const Range(0x0fe70, 0x0fe74),
+    const Range(0x0fe76, 0x0fefc),
+    const Range(0x0ff01, 0x0ffbe),
+    const Range(0x0ffc2, 0x0ffc7),
+    const Range(0x0ffca, 0x0ffcf),
+    const Range(0x0ffd2, 0x0ffd7),
+    const Range(0x0ffda, 0x0ffdc),
+    const Range(0x0ffe0, 0x0ffe6),
+    const Range(0x0ffe8, 0x0ffee),
+    const Range(0x0fffc, 0x0fffd),
+    const Range(0x10000, 0x1000b),
+    const Range(0x1000d, 0x10026),
+    const Range(0x10028, 0x1003a),
+    const Range(0x1003c, 0x1003d),
+    const Range(0x1003f, 0x1004d),
+    const Range(0x10050, 0x1005d),
+    const Range(0x10080, 0x100fa),
+    const Range(0x10100, 0x10102),
+    const Range(0x10107, 0x10133),
+    const Range(0x10137, 0x1018e),
+    const Range(0x10190, 0x1019b),
+    const Range(0x101a0, 0x101a0),
+    const Range(0x101d0, 0x101fc),
+    const Range(0x10280, 0x1029c),
+    const Range(0x102a0, 0x102d0),
+    const Range(0x102e1, 0x102fb),
+    const Range(0x10300, 0x10323),
+    const Range(0x1032d, 0x1034a),
+    const Range(0x10350, 0x10375),
+    const Range(0x10380, 0x1039d),
+    const Range(0x1039f, 0x103c3),
+    const Range(0x103c8, 0x103d5),
+    const Range(0x10400, 0x1049d),
+    const Range(0x104a0, 0x104a9),
+    const Range(0x104b0, 0x104d3),
+    const Range(0x104d8, 0x104fb),
+    const Range(0x10500, 0x10527),
+    const Range(0x10530, 0x10563),
+    const Range(0x1056f, 0x1056f),
+    const Range(0x10600, 0x10736),
+    const Range(0x10740, 0x10755),
+    const Range(0x10760, 0x10767),
+    const Range(0x10800, 0x10805),
+    const Range(0x10808, 0x10808),
+    const Range(0x1080a, 0x10835),
+    const Range(0x10837, 0x10838),
+    const Range(0x1083c, 0x1083c),
+    const Range(0x1083f, 0x10855),
+    const Range(0x10857, 0x1089e),
+    const Range(0x108a7, 0x108af),
+    const Range(0x108e0, 0x108f2),
+    const Range(0x108f4, 0x108f5),
+    const Range(0x108fb, 0x1091b),
+    const Range(0x1091f, 0x10939),
+    const Range(0x1093f, 0x1093f),
+    const Range(0x10980, 0x109b7),
+    const Range(0x109bc, 0x109cf),
+    const Range(0x109d2, 0x10a00),
+    const Range(0x10a10, 0x10a13),
+    const Range(0x10a15, 0x10a17),
+    const Range(0x10a19, 0x10a33),
+    const Range(0x10a40, 0x10a47),
+    const Range(0x10a50, 0x10a58),
+    const Range(0x10a60, 0x10a9f),
+    const Range(0x10ac0, 0x10ae4),
+    const Range(0x10aeb, 0x10af6),
+    const Range(0x10b00, 0x10b35),
+    const Range(0x10b39, 0x10b55),
+    const Range(0x10b58, 0x10b72),
+    const Range(0x10b78, 0x10b91),
+    const Range(0x10b99, 0x10b9c),
+    const Range(0x10ba9, 0x10baf),
+    const Range(0x10c00, 0x10c48),
+    const Range(0x10c80, 0x10cb2),
+    const Range(0x10cc0, 0x10cf2),
+    const Range(0x10cfa, 0x10cff),
+    const Range(0x10e60, 0x10e7e),
+    const Range(0x11003, 0x11037),
+    const Range(0x11047, 0x1104d),
+    const Range(0x11052, 0x1106f),
+    const Range(0x11083, 0x110af),
+    const Range(0x110bb, 0x110bc),
+    const Range(0x110be, 0x110c1),
+    const Range(0x110d0, 0x110e8),
+    const Range(0x110f0, 0x110f9),
+    const Range(0x11103, 0x11126),
+    const Range(0x11136, 0x11143),
+    const Range(0x11150, 0x11172),
+    const Range(0x11174, 0x11176),
+    const Range(0x11183, 0x111b2),
+    const Range(0x111c1, 0x111c9),
+    const Range(0x111cd, 0x111cd),
+    const Range(0x111d0, 0x111df),
+    const Range(0x111e1, 0x111f4),
+    const Range(0x11200, 0x11211),
+    const Range(0x11213, 0x1122b),
+    const Range(0x11238, 0x1123d),
+    const Range(0x11280, 0x11286),
+    const Range(0x11288, 0x11288),
+    const Range(0x1128a, 0x1128d),
+    const Range(0x1128f, 0x1129d),
+    const Range(0x1129f, 0x112a9),
+    const Range(0x112b0, 0x112de),
+    const Range(0x112f0, 0x112f9),
+    const Range(0x11305, 0x1130c),
+    const Range(0x1130f, 0x11310),
+    const Range(0x11313, 0x11328),
+    const Range(0x1132a, 0x11330),
+    const Range(0x11332, 0x11333),
+    const Range(0x11335, 0x11339),
+    const Range(0x1133d, 0x1133d),
+    const Range(0x11350, 0x11350),
+    const Range(0x1135d, 0x11361),
+    const Range(0x11400, 0x11434),
+    const Range(0x11447, 0x11459),
+    const Range(0x1145b, 0x1145b),
+    const Range(0x1145d, 0x1145d),
+    const Range(0x11480, 0x114af),
+    const Range(0x114c4, 0x114c7),
+    const Range(0x114d0, 0x114d9),
+    const Range(0x11580, 0x115ae),
+    const Range(0x115c1, 0x115db),
+    const Range(0x11600, 0x1162f),
+    const Range(0x11641, 0x11644),
+    const Range(0x11650, 0x11659),
+    const Range(0x11660, 0x1166c),
+    const Range(0x11680, 0x116aa),
+    const Range(0x116c0, 0x116c9),
+    const Range(0x11700, 0x11719),
+    const Range(0x11730, 0x1173f),
+    const Range(0x118a0, 0x118f2),
+    const Range(0x118ff, 0x118ff),
+    const Range(0x11a00, 0x11a00),
+    const Range(0x11a0b, 0x11a32),
+    const Range(0x11a3a, 0x11a3a),
+    const Range(0x11a3f, 0x11a46),
+    const Range(0x11a50, 0x11a50),
+    const Range(0x11a5c, 0x11a83),
+    const Range(0x11a86, 0x11a89),
+    const Range(0x11a9a, 0x11a9c),
+    const Range(0x11a9e, 0x11aa2),
+    const Range(0x11ac0, 0x11af8),
+    const Range(0x11c00, 0x11c08),
+    const Range(0x11c0a, 0x11c2e),
+    const Range(0x11c40, 0x11c45),
+    const Range(0x11c50, 0x11c6c),
+    const Range(0x11c70, 0x11c8f),
+    const Range(0x11d00, 0x11d06),
+    const Range(0x11d08, 0x11d09),
+    const Range(0x11d0b, 0x11d30),
+    const Range(0x11d46, 0x11d46),
+    const Range(0x11d50, 0x11d59),
+    const Range(0x12000, 0x12399),
+    const Range(0x12400, 0x1246e),
+    const Range(0x12470, 0x12474),
+    const Range(0x12480, 0x12543),
+    const Range(0x13000, 0x1342e),
+    const Range(0x14400, 0x14646),
+    const Range(0x16800, 0x16a38),
+    const Range(0x16a40, 0x16a5e),
+    const Range(0x16a60, 0x16a69),
+    const Range(0x16a6e, 0x16a6f),
+    const Range(0x16ad0, 0x16aed),
+    const Range(0x16af5, 0x16af5),
+    const Range(0x16b00, 0x16b2f),
+    const Range(0x16b37, 0x16b45),
+    const Range(0x16b50, 0x16b59),
+    const Range(0x16b5b, 0x16b61),
+    const Range(0x16b63, 0x16b77),
+    const Range(0x16b7d, 0x16b8f),
+    const Range(0x16f00, 0x16f44),
+    const Range(0x16f50, 0x16f50),
+    const Range(0x16f93, 0x16f9f),
+    const Range(0x16fe0, 0x16fe1),
+    const Range(0x17000, 0x187ec),
+    const Range(0x18800, 0x18af2),
+    const Range(0x1b000, 0x1b11e),
+    const Range(0x1b170, 0x1b2fb),
+    const Range(0x1bc00, 0x1bc6a),
+    const Range(0x1bc70, 0x1bc7c),
+    const Range(0x1bc80, 0x1bc88),
+    const Range(0x1bc90, 0x1bc99),
+    const Range(0x1bc9c, 0x1bc9c),
+    const Range(0x1bc9f, 0x1bc9f),
+    const Range(0x1d000, 0x1d0f5),
+    const Range(0x1d100, 0x1d126),
+    const Range(0x1d129, 0x1d164),
+    const Range(0x1d16a, 0x1d16c),
+    const Range(0x1d183, 0x1d184),
+    const Range(0x1d18c, 0x1d1a9),
+    const Range(0x1d1ae, 0x1d1e8),
+    const Range(0x1d200, 0x1d241),
+    const Range(0x1d245, 0x1d245),
+    const Range(0x1d300, 0x1d356),
+    const Range(0x1d360, 0x1d371),
+    const Range(0x1d400, 0x1d454),
+    const Range(0x1d456, 0x1d49c),
+    const Range(0x1d49e, 0x1d49f),
+    const Range(0x1d4a2, 0x1d4a2),
+    const Range(0x1d4a5, 0x1d4a6),
+    const Range(0x1d4a9, 0x1d4ac),
+    const Range(0x1d4ae, 0x1d4b9),
+    const Range(0x1d4bb, 0x1d4bb),
+    const Range(0x1d4bd, 0x1d4c3),
+    const Range(0x1d4c5, 0x1d505),
+    const Range(0x1d507, 0x1d50a),
+    const Range(0x1d50d, 0x1d514),
+    const Range(0x1d516, 0x1d51c),
+    const Range(0x1d51e, 0x1d539),
+    const Range(0x1d53b, 0x1d53e),
+    const Range(0x1d540, 0x1d544),
+    const Range(0x1d546, 0x1d546),
+    const Range(0x1d54a, 0x1d550),
+    const Range(0x1d552, 0x1d6a5),
+    const Range(0x1d6a8, 0x1d7cb),
+    const Range(0x1d7ce, 0x1d9ff),
+    const Range(0x1da37, 0x1da3a),
+    const Range(0x1da6d, 0x1da74),
+    const Range(0x1da76, 0x1da83),
+    const Range(0x1da85, 0x1da8b),
+    const Range(0x1e800, 0x1e8c4),
+    const Range(0x1e8c7, 0x1e8cf),
+    const Range(0x1e900, 0x1e943),
+    const Range(0x1e950, 0x1e959),
+    const Range(0x1e95e, 0x1e95f),
+    const Range(0x1ee00, 0x1ee03),
+    const Range(0x1ee05, 0x1ee1f),
+    const Range(0x1ee21, 0x1ee22),
+    const Range(0x1ee24, 0x1ee24),
+    const Range(0x1ee27, 0x1ee27),
+    const Range(0x1ee29, 0x1ee32),
+    const Range(0x1ee34, 0x1ee37),
+    const Range(0x1ee39, 0x1ee39),
+    const Range(0x1ee3b, 0x1ee3b),
+    const Range(0x1ee42, 0x1ee42),
+    const Range(0x1ee47, 0x1ee47),
+    const Range(0x1ee49, 0x1ee49),
+    const Range(0x1ee4b, 0x1ee4b),
+    const Range(0x1ee4d, 0x1ee4f),
+    const Range(0x1ee51, 0x1ee52),
+    const Range(0x1ee54, 0x1ee54),
+    const Range(0x1ee57, 0x1ee57),
+    const Range(0x1ee59, 0x1ee59),
+    const Range(0x1ee5b, 0x1ee5b),
+    const Range(0x1ee5d, 0x1ee5d),
+    const Range(0x1ee5f, 0x1ee5f),
+    const Range(0x1ee61, 0x1ee62),
+    const Range(0x1ee64, 0x1ee64),
+    const Range(0x1ee67, 0x1ee6a),
+    const Range(0x1ee6c, 0x1ee72),
+    const Range(0x1ee74, 0x1ee77),
+    const Range(0x1ee79, 0x1ee7c),
+    const Range(0x1ee7e, 0x1ee7e),
+    const Range(0x1ee80, 0x1ee89),
+    const Range(0x1ee8b, 0x1ee9b),
+    const Range(0x1eea1, 0x1eea3),
+    const Range(0x1eea5, 0x1eea9),
+    const Range(0x1eeab, 0x1eebb),
+    const Range(0x1eef0, 0x1eef1),
+    const Range(0x1f000, 0x1f02b),
+    const Range(0x1f030, 0x1f093),
+    const Range(0x1f0a0, 0x1f0ae),
+    const Range(0x1f0b1, 0x1f0bf),
+    const Range(0x1f0c1, 0x1f0cf),
+    const Range(0x1f0d1, 0x1f0f5),
+    const Range(0x1f100, 0x1f10c),
+    const Range(0x1f110, 0x1f12e),
+    const Range(0x1f130, 0x1f16b),
+    const Range(0x1f170, 0x1f1ac),
+    const Range(0x1f1e6, 0x1f202),
+    const Range(0x1f210, 0x1f23b),
+    const Range(0x1f240, 0x1f248),
+    const Range(0x1f250, 0x1f251),
+    const Range(0x1f260, 0x1f265),
+    const Range(0x1f300, 0x1f6d4),
+    const Range(0x1f6e0, 0x1f6ec),
+    const Range(0x1f6f0, 0x1f6f8),
+    const Range(0x1f700, 0x1f773),
+    const Range(0x1f780, 0x1f7d4),
+    const Range(0x1f800, 0x1f80b),
+    const Range(0x1f810, 0x1f847),
+    const Range(0x1f850, 0x1f859),
+    const Range(0x1f860, 0x1f887),
+    const Range(0x1f890, 0x1f8ad),
+    const Range(0x1f900, 0x1f90b),
+    const Range(0x1f910, 0x1f93e),
+    const Range(0x1f940, 0x1f94c),
+    const Range(0x1f950, 0x1f96b),
+    const Range(0x1f980, 0x1f997),
+    const Range(0x1f9c0, 0x1f9c0),
+    const Range(0x1f9d0, 0x1f9e6),
+    const Range(0x20000, 0x2a6d6),
+    const Range(0x2a700, 0x2b734),
+    const Range(0x2b740, 0x2b81d),
+    const Range(0x2b820, 0x2cea1),
+    const Range(0x2ceb0, 0x2ebe0),
+    const Range(0x2f800, 0x2fa1d),
+  ];
+  final Range range = pickFromList(random, characterRanges);
+  if (range.start == range.end)
+    return range.start;
+  return range.start + random.nextInt(range.end - range.start);
+}
\ No newline at end of file
diff --git a/packages/flutter/lib/src/painting/images.dart b/packages/flutter/lib/src/painting/images.dart
index b63c1e9..e02b5a3 100644
--- a/packages/flutter/lib/src/painting/images.dart
+++ b/packages/flutter/lib/src/painting/images.dart
@@ -187,6 +187,20 @@
   ImageStream _imageStream;
   ImageInfo _image;
 
+  /// Draw the image onto the given canvas.
+  ///
+  /// The image is drawn at the position and size given by the `rect` argument.
+  ///
+  /// The image is clipped to the given `clipPath`, if any.
+  ///
+  /// The `configuration` object is used to resolve the image (e.g. to pick
+  /// resolution-specific assets), and to implement the
+  /// [DecorationImage.matchTextDirection] feature.
+  ///
+  /// If the image needs to be painted again, e.g. because it is animated or
+  /// because it had not yet been loaded the first time this method was called,
+  /// then the `onChanged` callback passed to [DecorationImage.createPainter]
+  /// will be called.
   void paint(Canvas canvas, Rect rect, Path clipPath, ImageConfiguration configuration) {
     if (_details == null)
       return;
diff --git a/packages/flutter/lib/src/rendering/list_body.dart b/packages/flutter/lib/src/rendering/list_body.dart
index cfe3cb2..76fc6dc 100644
--- a/packages/flutter/lib/src/rendering/list_body.dart
+++ b/packages/flutter/lib/src/rendering/list_body.dart
@@ -43,6 +43,10 @@
       child.parentData = new ListBodyParentData();
   }
 
+  /// The direction in which the children are laid out.
+  ///
+  /// For example, if the [axisDirection] is [AxisDirection.down], each child
+  /// will be laid out below the next, vertically.
   AxisDirection get axisDirection => _axisDirection;
   AxisDirection _axisDirection;
   set axisDirection(AxisDirection value) {
@@ -53,6 +57,8 @@
     markNeedsLayout();
   }
 
+  /// The axis (horizontal or vertical) corresponding to the current
+  /// [axisDirection].
   Axis get mainAxis => axisDirectionToAxis(axisDirection);
 
   @override
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index b0cb1a6..3bf20e5 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -2114,6 +2114,17 @@
     owner.requestVisualUpdate();
   }
 
+  /// Report the semantics of this node, for example for accessibility purposes.
+  ///
+  /// This method should be overridden by subclasses that have interesting
+  /// semantic information.
+  ///
+  /// The given [SemanticsConfiguration] object is mutable and should be
+  /// annotated in a manner that describes the current state. No reference
+  /// should be kept to that object; mutating it outside of the context of the
+  /// [describeSemanticsConfiguration] call (for example as a result of
+  /// asynchronous computation) will at best have no useful effect and at worse
+  /// will cause crashes as the data will be in an inconsistent state.
   @protected
   void describeSemanticsConfiguration(SemanticsConfiguration config) {
     // Nothing to do by default.
diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart
index a0d7097..0fd3675 100644
--- a/packages/flutter/lib/src/rendering/paragraph.dart
+++ b/packages/flutter/lib/src/rendering/paragraph.dart
@@ -403,6 +403,20 @@
     return _textPainter.getWordBoundary(position);
   }
 
+  /// Returns the size of the text as laid out.
+  ///
+  /// This can differ from [size] if the text overflowed or if the [constraints]
+  /// provided by the parent [RenderObject] forced the layout to be bigger than
+  /// necessary for the given [text].
+  ///
+  /// This returns the [TextPainter.size] of the underlying [TextPainter].
+  ///
+  /// Valid only after [layout].
+  Size get textSize {
+    assert(!debugNeedsLayout);
+    return _textPainter.size;
+  }
+
   @override
   void describeSemanticsConfiguration(SemanticsConfiguration config) {
     super.describeSemanticsConfiguration(config);
diff --git a/packages/flutter/lib/src/rendering/semantics.dart b/packages/flutter/lib/src/rendering/semantics.dart
index 2ede294..e8e2bc0 100644
--- a/packages/flutter/lib/src/rendering/semantics.dart
+++ b/packages/flutter/lib/src/rendering/semantics.dart
@@ -596,9 +596,18 @@
 
   static final SemanticsConfiguration _kEmptyConfig = new SemanticsConfiguration();
 
+  /// Reconfigures the properties of this object to describe the configuration
+  /// provided in the `config` argument and the children listen in the
+  /// `childrenInInversePaintOrder` argument.
+  ///
+  /// The arguments may be null; this represents an empty configuration (all
+  /// values at their defaults, no children).
+  ///
+  /// No reference is kept to the [SemanticsConfiguration] object, but the child
+  /// list is used as-is and should therefore not be changed after this call.
   void updateWith({
     @required SemanticsConfiguration config,
-    @required  List<SemanticsNode> childrenInInversePaintOrder,
+    @required List<SemanticsNode> childrenInInversePaintOrder,
   }) {
     config ??= _kEmptyConfig;
     if (_isDifferentFromCurrentSemanticAnnotation(config))
diff --git a/packages/flutter/lib/src/rendering/stack.dart b/packages/flutter/lib/src/rendering/stack.dart
index 36b7029..0d72ef0 100644
--- a/packages/flutter/lib/src/rendering/stack.dart
+++ b/packages/flutter/lib/src/rendering/stack.dart
@@ -332,14 +332,20 @@
     markNeedsLayout();
   }
 
-  /// How to align the non-positioned children in the stack.
+  /// How to align the non-positioned or partially-positioned children in the
+  /// stack.
   ///
   /// The non-positioned children are placed relative to each other such that
   /// the points determined by [alignment] are co-located. For example, if the
   /// [alignment] is [Alignment.topLeft], then the top left corner of
   /// each non-positioned child will be located at the same global coordinate.
   ///
-  /// If this is set to a [AlignmentDirectional] object, then [textDirection]
+  /// Partially-positioned children, those that do not specify an alignment in a
+  /// particular axis (e.g. that have neither `top` nor `bottom` set), use the
+  /// alignment to determine how they should be positioned in that
+  /// under-specified axis.
+  ///
+  /// If this is set to an [AlignmentDirectional] object, then [textDirection]
   /// must not be null.
   AlignmentGeometry get alignment => _alignment;
   AlignmentGeometry _alignment;
@@ -504,20 +510,26 @@
 
         child.layout(childConstraints, parentUsesSize: true);
 
-        double x = 0.0;
-        if (childParentData.left != null)
+        double x;
+        if (childParentData.left != null) {
           x = childParentData.left;
-        else if (childParentData.right != null)
+        } else if (childParentData.right != null) {
           x = size.width - childParentData.right - child.size.width;
+        } else {
+          x = _resolvedAlignment.alongOffset(size - child.size).dx;
+        }
 
         if (x < 0.0 || x + child.size.width > size.width)
           _hasVisualOverflow = true;
 
-        double y = 0.0;
-        if (childParentData.top != null)
+        double y;
+        if (childParentData.top != null) {
           y = childParentData.top;
-        else if (childParentData.bottom != null)
+        } else if (childParentData.bottom != null) {
           y = size.height - childParentData.bottom - child.size.height;
+        } else {
+          y = _resolvedAlignment.alongOffset(size - child.size).dy;
+        }
 
         if (y < 0.0 || y + child.size.height > size.height)
           _hasVisualOverflow = true;
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index a000c6a..90114c8 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -2239,9 +2239,10 @@
 /// Positioned children are those wrapped in a [Positioned] widget that has at
 /// least one non-null property. The stack sizes itself to contain all the
 /// non-positioned children, which are positioned according to [alignment]
-/// (which defaults to the top-left corner). The positioned children are then
-/// placed relative to the stack according to their top, right, bottom, and left
-/// properties.
+/// (which defaults to the top-left corner in left-to-right environments and the
+/// top-right corner in right-to-left environments). The positioned children are
+/// then placed relative to the stack according to their top, right, bottom, and
+/// left properties.
 ///
 /// The stack paints its children in order with the first child being at the
 /// bottom. If you want to change the order in which the children paint, you
@@ -2282,12 +2283,18 @@
     List<Widget> children: const <Widget>[],
   }) : super(key: key, children: children);
 
-  /// How to align the non-positioned children in the stack.
+  /// How to align the non-positioned and partially-positioned children in the
+  /// stack.
   ///
   /// The non-positioned children are placed relative to each other such that
   /// the points determined by [alignment] are co-located. For example, if the
   /// [alignment] is [Alignment.topLeft], then the top left corner of
   /// each non-positioned child will be located at the same global coordinate.
+  ///
+  /// Partially-positioned children, those that do not specify an alignment in a
+  /// particular axis (e.g. that have neither `top` nor `bottom` set), use the
+  /// alignment to determine how they should be positioned in that
+  /// under-specified axis.
   final AlignmentGeometry alignment;
 
   /// The text direction with which to resolve [alignment].
@@ -2398,6 +2405,12 @@
 /// [height] properties can be used to give the dimensions, with one
 /// corresponding position property (e.g. [top] and [height]).
 ///
+/// If all three values on a particular axis are null, then the
+/// [Stack.alignment] property is used to position the child.
+///
+/// If all six values are null, the child is a non-positioned child. The [Stack]
+/// uses only the non-positioned children to size itself.
+///
 /// See also:
 ///
 ///  * [PositionedDirectional], which adapts to the ambient [Directionality].
@@ -2413,6 +2426,8 @@
   ///
   ///  * [Positioned.directional], which specifies the widget's horizontal
   ///    position using `start` and `end` rather than `left` and `right`.
+  ///  * [PositionedDirectional], which is similar to [Positioned.directional]
+  ///    but adapts to the ambient [Directionality].
   const Positioned({
     Key key,
     this.left,
@@ -2530,36 +2545,54 @@
   ///
   /// Only two out of the three horizontal values ([left], [right], [width]) can be
   /// set. The third must be null.
+  ///
+  /// If all three are null, the [Stack.alignment] is used to position the child
+  /// horizontally.
   final double left;
 
   /// The distance that the child's top edge is inset from the top of the stack.
   ///
   /// Only two out of the three vertical values ([top], [bottom], [height]) can be
   /// set. The third must be null.
+  ///
+  /// If all three are null, the [Stack.alignment] is used to position the child
+  /// vertically.
   final double top;
 
   /// The distance that the child's right edge is inset from the right of the stack.
   ///
   /// Only two out of the three horizontal values ([left], [right], [width]) can be
   /// set. The third must be null.
+  ///
+  /// If all three are null, the [Stack.alignment] is used to position the child
+  /// horizontally.
   final double right;
 
   /// The distance that the child's bottom edge is inset from the bottom of the stack.
   ///
   /// Only two out of the three vertical values ([top], [bottom], [height]) can be
   /// set. The third must be null.
+  ///
+  /// If all three are null, the [Stack.alignment] is used to position the child
+  /// vertically.
   final double bottom;
 
   /// The child's width.
   ///
   /// Only two out of the three horizontal values ([left], [right], [width]) can be
   /// set. The third must be null.
+  ///
+  /// If all three are null, the [Stack.alignment] is used to position the child
+  /// horizontally.
   final double width;
 
   /// The child's height.
   ///
   /// Only two out of the three vertical values ([top], [bottom], [height]) can be
   /// set. The third must be null.
+  ///
+  /// If all three are null, the [Stack.alignment] is used to position the child
+  /// vertically.
   final double height;
 
   @override
@@ -3897,6 +3930,18 @@
       ..textScaleFactor = textScaleFactor
       ..maxLines = maxLines;
   }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder description) {
+    super.debugFillProperties(description);
+    description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: TextAlign.start));
+    description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
+    description.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
+    description.add(new EnumProperty<TextOverflow>('overflow', overflow, defaultValue: TextOverflow.clip));
+    description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: 1.0));
+    description.add(new IntProperty('maxLines', maxLines, ifNull: 'unlimited'));
+    description.add(new StringProperty('text', text.toPlainText()));
+  }
 }
 
 /// A widget that displays a [dart:ui.Image] directly.
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index c047394..04be6d5 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -4519,6 +4519,11 @@
   void removeChildRenderObject(RenderObject child) {
     assert(false);
   }
+
+  @override
+  List<DiagnosticsNode> debugDescribeChildren() {
+    return widget.debugDescribeChildren();
+  }
 }
 
 /// An [Element] that uses a [SingleChildRenderObjectWidget] as its configuration.
diff --git a/packages/flutter/lib/src/widgets/icon_data.dart b/packages/flutter/lib/src/widgets/icon_data.dart
index 98c4a4d..c34a754 100644
--- a/packages/flutter/lib/src/widgets/icon_data.dart
+++ b/packages/flutter/lib/src/widgets/icon_data.dart
@@ -29,7 +29,14 @@
   /// The font family from which the glyph for the [codePoint] will be selected.
   final String fontFamily;
 
-  // The name of the package from which the font family is included.
+  /// The name of the package from which the font family is included.
+  ///
+  /// The name is used by the [Icon] widget when configuring the [TextStyle] so
+  /// that the given [fontFamily] is obtained from the appropriate asset.
+  ///
+  /// See also:
+  ///
+  ///  * [TextStyle], which describes how to use fonts from other packages.
   final String fontPackage;
 
   @override
diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart
index b8165f7..a2ff2df 100644
--- a/packages/flutter/lib/src/widgets/text.dart
+++ b/packages/flutter/lib/src/widgets/text.dart
@@ -292,5 +292,11 @@
     super.debugFillProperties(description);
     description.add(new StringProperty('data', data, showName: false));
     style?.debugFillProperties(description);
+    description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
+    description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
+    description.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
+    description.add(new EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
+    description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
+    description.add(new IntProperty('maxLines', maxLines, defaultValue: null));
   }
 }
diff --git a/packages/flutter/test/painting/text_painter_rtl_test.dart b/packages/flutter/test/painting/text_painter_rtl_test.dart
index e1edb61..065d7ac 100644
--- a/packages/flutter/test/painting/text_painter_rtl_test.dart
+++ b/packages/flutter/test/painting/text_painter_rtl_test.dart
@@ -46,6 +46,9 @@
     expect(painter.text.text.length, 28);
     painter.layout();
 
+    // The skips here are because the old rendering code considers the bidi formatting characters
+    // to be part of the word sometimes and not others, which is fine, but we'd mildly prefer if
+    // we were consistently considering them part of words always.
     final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4, affinity: TextAffinity.downstream));
     expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs);
     final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14, affinity: TextAffinity.downstream));
diff --git a/packages/flutter/test/widgets/stack_test.dart b/packages/flutter/test/widgets/stack_test.dart
index 62652e4..a3e4cdf 100644
--- a/packages/flutter/test/widgets/stack_test.dart
+++ b/packages/flutter/test/widgets/stack_test.dart
@@ -629,4 +629,122 @@
       ),
     );
   });
+
+  testWidgets('Alignment with partially-positioned children', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      new Directionality(
+        textDirection: TextDirection.rtl,
+        child: new Stack(
+          alignment: Alignment.center,
+          children: <Widget>[
+            const SizedBox(width: 100.0, height: 100.0),
+            const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+          ],
+        ),
+      ),
+    );
+    expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+
+    await tester.pumpWidget(
+      new Directionality(
+        textDirection: TextDirection.ltr,
+        child: new Stack(
+          alignment: Alignment.center,
+          children: <Widget>[
+            const SizedBox(width: 100.0, height: 100.0),
+            const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+          ],
+        ),
+      ),
+    );
+    expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
+
+    await tester.pumpWidget(
+      new Directionality(
+        textDirection: TextDirection.ltr,
+        child: new Stack(
+          alignment: Alignment.bottomRight,
+          children: <Widget>[
+            const SizedBox(width: 100.0, height: 100.0),
+            const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+          ],
+        ),
+      ),
+    );
+    expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
+
+    await tester.pumpWidget(
+      new Directionality(
+        textDirection: TextDirection.ltr,
+        child: new Stack(
+          alignment: Alignment.topLeft,
+          children: <Widget>[
+            const SizedBox(width: 100.0, height: 100.0),
+            const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+            const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
+          ],
+        ),
+      ),
+    );
+    expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
+    expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
+  });
 }
diff --git a/packages/flutter/test/widgets/table_test.dart b/packages/flutter/test/widgets/table_test.dart
index 4d8c8a9..5ce9a7f 100644
--- a/packages/flutter/test/widgets/table_test.dart
+++ b/packages/flutter/test/widgets/table_test.dart
@@ -765,23 +765,23 @@
       equalsIgnoringHashCodes(
         'Table-[GlobalKey#00000](renderObject: RenderTable#00000)\n'
         '├Text("A")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "A", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '├Text("B")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "B", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '├Text("C")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "C", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '├Text("D")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "D", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '├Text("EEE")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "EEE", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '├Text("F")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "F", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '├Text("G")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "G", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '├Text("H")\n'
-        '│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
+        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "H", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
         '└Text("III")\n'
-        ' └RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n',
+        ' └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "III", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
       ),
     );
   });
diff --git a/packages/flutter_test/test/widget_tester_test.dart b/packages/flutter_test/test/widget_tester_test.dart
index c9970d3..af4903b 100644
--- a/packages/flutter_test/test/widget_tester_test.dart
+++ b/packages/flutter_test/test/widget_tester_test.dart
@@ -52,7 +52,7 @@
       final String message = failure.message;
 
       expect(message, contains('Expected: no matching nodes in the widget tree\n'));
-      expect(message, contains('Actual: ?:<exactly one widget with text "foo": Text("foo")>\n'));
+      expect(message, contains('Actual: ?:<exactly one widget with text "foo": Text("foo", textDirection: ltr)>\n'));
       expect(message, contains('Which: means one was found but none were expected\n'));
     });
 
@@ -70,7 +70,7 @@
       final String message = failure.message;
 
       expect(message, contains('Expected: no matching nodes in the widget tree\n'));
-      expect(message, contains('Actual: ?:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo")>\n'));
+      expect(message, contains('Actual: ?:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo", textDirection: ltr)>\n'));
       expect(message, contains('Which: means one was found but none were expected\n'));
     });
 
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart
index 651c65a..7cfab01 100644
--- a/packages/flutter_tools/lib/src/vmservice.dart
+++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -738,6 +738,10 @@
     } on WebSocketChannelException catch (error) {
       throwToolExit('Error connecting to observatory: $error');
       return null;
+    } on rpc.RpcException catch (error) {
+      printError('Error ${error.code} received from application: ${error.message}');
+      printTrace('${error.data}');
+      return null;
     }
   }