Commit 7cf2dbdf authored by Ian Hickson's avatar Ian Hickson

Fix crash when dumping the app if it uses RichText

Specifically:

* Handle null styles in TextSpan without crashing in toString().

* Handle null children in TextSpan child lists without crashing in
  toString().

* Handle entirely empty TextSpans in toString() explicitly.

* Assert that TextSpans don't contain nulls in various places. This is
  done more often than one might think necessary, because it turns out
  that TextSpan takes a (mutable) List for one of its arguments, so
  who knows what it will contain at any given time. By asserting all
  over the place, hopefully we'll catch it near the change if they do
  change it.

* Add a RichText example to Stocks to exercise RichText and TextSpans.

See also: https://github.com/flutter/flutter/issues/2514, https://github.com/flutter/flutter/issues/2519
parent 3dc22dce
......@@ -46,6 +46,19 @@ class StockSymbolView extends StatelessComponent {
),
new Text('Market Cap', style: headings),
new Text('${stock.marketCap}'),
new Container(
height: 8.0
),
new RichText(
text: new TextSpan(
style: DefaultTextStyle.of(context).merge(new TextStyle(fontSize: 8.0)),
text: 'Prices may be delayed by ',
children: <TextSpan>[
new TextSpan(text: 'several', style: new TextStyle(fontStyle: FontStyle.italic)),
new TextSpan(text: ' years.'),
]
)
),
],
justifyContent: FlexJustifyContent.collapse
)
......
......@@ -12,6 +12,7 @@
library services;
export 'src/services/activity.dart';
export 'src/services/assertions.dart';
export 'src/services/asset_bundle.dart';
export 'src/services/binding.dart';
export 'src/services/fetch.dart';
......
......@@ -5,6 +5,7 @@
import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphStyle, TextBox;
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'basic_types.dart';
import 'text_editing.dart';
......@@ -51,6 +52,7 @@ class TextSpan {
final GestureRecognizer recognizer;
void build(ui.ParagraphBuilder builder) {
assert(debugAssertValid());
final bool hasStyle = style != null;
if (hasStyle)
builder.pushStyle(style.textStyle);
......@@ -81,6 +83,7 @@ class TextSpan {
}
TextSpan getSpanForPosition(TextPosition position) {
assert(debugAssertValid());
TextAffinity affinity = position.affinity;
int targetOffset = position.offset;
int offset = 0;
......@@ -101,6 +104,7 @@ class TextSpan {
}
String toPlainText() {
assert(debugAssertValid());
StringBuffer buffer = new StringBuffer();
visitTextSpan((TextSpan span) {
buffer.write(span.text);
......@@ -113,15 +117,47 @@ class TextSpan {
StringBuffer buffer = new StringBuffer();
buffer.writeln('$prefix$runtimeType:');
String indent = '$prefix ';
if (style != null)
buffer.writeln(style.toString(indent));
if (text != null)
buffer.writeln('$indent"$text"');
if (children != null)
for (TextSpan child in children)
buffer.writeln(child.toString(indent));
if (children != null) {
for (TextSpan child in children) {
if (child != null) {
buffer.write(child.toString(indent));
} else {
buffer.writeln('$indent<null>');
}
}
}
if (style == null && text == null && children == null)
buffer.writeln('$indent(empty)');
return buffer.toString();
}
bool debugAssertValid() {
assert(() {
if (!visitTextSpan((TextSpan span) {
if (span.children != null) {
for (TextSpan child in span.children) {
if (child == null)
return false;
}
}
return true;
})) {
throw new FlutterError(
'TextSpan contains a null child.\n'
'A TextSpan object with a non-null child list should not have any nulls in its child list.\n'
'The full text in question was:\n'
'${toString(" ")}'
);
}
return true;
});
return true;
}
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
......@@ -149,6 +185,7 @@ class TextPainter {
/// The (potentially styled) text to paint.
TextSpan get text => _text;
void set text(TextSpan value) {
assert(value == null || value.debugAssertValid());
if (_text == value)
return;
_text = value;
......
......@@ -15,6 +15,7 @@ class RenderParagraph extends RenderBox {
TextSpan text
) : _textPainter = new TextPainter(text) {
assert(text != null);
assert(text.debugAssertValid());
}
final TextPainter _textPainter;
......@@ -24,6 +25,7 @@ class RenderParagraph extends RenderBox {
/// The text to display
TextSpan get text => _textPainter.text;
void set text(TextSpan value) {
assert(value.debugAssertValid());
if (_textPainter.text == value)
return;
_textPainter.text = value;
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
class FlutterError extends AssertionError {
FlutterError(this.message);
final String message;
String toString() => message;
}
......@@ -27,4 +27,38 @@ void main() {
expect(b1 == a2, isFalse);
expect(c1 == b2, isFalse);
});
test("TextSpan ", () {
final TextSpan test = new TextSpan(
text: 'a',
style: new TextStyle(
fontSize: 10.0
),
children: <TextSpan>[
new TextSpan(
text: 'b',
children: <TextSpan>[
new TextSpan()
]
),
null,
new TextSpan(
text: 'c'
),
]
);
expect(test.toString(), equals(
'TextSpan:\n'
' inherit: true\n'
' size: 10.0\n'
' "a"\n'
' TextSpan:\n'
' "b"\n'
' TextSpan:\n'
' (empty)\n'
' <null>\n'
' TextSpan:\n'
' "c"\n'
));
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment