1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle, TextStyle;
6
7import 'package:flutter/foundation.dart';
8
9import 'box.dart';
10import 'object.dart';
11
12const double _kMaxWidth = 100000.0;
13const double _kMaxHeight = 100000.0;
14
15/// A render object used as a placeholder when an error occurs.
16///
17/// The box will be painted in the color given by the
18/// [RenderErrorBox.backgroundColor] static property.
19///
20/// A message can be provided. To simplify the class and thus help reduce the
21/// likelihood of this class itself being the source of errors, the message
22/// cannot be changed once the object has been created. If provided, the text
23/// will be painted on top of the background, using the styles given by the
24/// [RenderErrorBox.textStyle] and [RenderErrorBox.paragraphStyle] static
25/// properties.
26///
27/// Again to help simplify the class, if the parent has left the constraints
28/// unbounded, this box tries to be 100000.0 pixels wide and high, to
29/// approximate being infinitely high but without using infinities.
30class RenderErrorBox extends RenderBox {
31 /// Creates a RenderErrorBox render object.
32 ///
33 /// A message can optionally be provided. If a message is provided, an attempt
34 /// will be made to render the message when the box paints.
35 RenderErrorBox([ this.message = '' ]) {
36 try {
37 if (message != '') {
38 // This class is intentionally doing things using the low-level
39 // primitives to avoid depending on any subsystems that may have ended
40 // up in an unstable state -- after all, this class is mainly used when
41 // things have gone wrong.
42 //
43 // Generally, the much better way to draw text in a RenderObject is to
44 // use the TextPainter class. If you're looking for code to crib from,
45 // see the paragraph.dart file and the RenderParagraph class.
46 final ui.ParagraphBuilder builder = ui.ParagraphBuilder(paragraphStyle);
47 builder.pushStyle(textStyle);
48 builder.addText(message);
49 _paragraph = builder.build();
50 } else {
51 _paragraph = null;
52 }
53 } catch (error) {
54 // If an error happens here we're in a terrible state, so we really should
55 // just forget about it and let the developer deal with the already-reported
56 // errors. It's unlikely that these errors are going to help with that.
57 }
58 }
59
60 /// The message to attempt to display at paint time.
61 final String message;
62
63 late final ui.Paragraph? _paragraph;
64
65 @override
66 double computeMaxIntrinsicWidth(double height) {
67 return _kMaxWidth;
68 }
69
70 @override
71 double computeMaxIntrinsicHeight(double width) {
72 return _kMaxHeight;
73 }
74
75 @override
76 bool get sizedByParent => true;
77
78 @override
79 bool hitTestSelf(Offset position) => true;
80
81 @override
82 @protected
83 Size computeDryLayout(covariant BoxConstraints constraints) {
84 return constraints.constrain(const Size(_kMaxWidth, _kMaxHeight));
85 }
86
87 /// The distance to place around the text.
88 ///
89 /// This is intended to ensure that if the [RenderErrorBox] is placed at the top left
90 /// of the screen, under the system's status bar, the error text is still visible in
91 /// the area below the status bar.
92 ///
93 /// The padding is ignored if the error box is smaller than the padding.
94 ///
95 /// See also:
96 ///
97 /// * [minimumWidth], which controls how wide the box must be before the
98 /// horizontal padding is applied.
99 static EdgeInsets padding = const EdgeInsets.fromLTRB(64.0, 96.0, 64.0, 12.0);
100
101 /// The width below which the horizontal padding is not applied.
102 ///
103 /// If the left and right padding would reduce the available width to less than
104 /// this value, then the text is rendered flush with the left edge.
105 static double minimumWidth = 200.0;
106
107 /// The color to use when painting the background of [RenderErrorBox] objects.
108 ///
109 /// Defaults to red in debug mode, a light gray otherwise.
110 static Color backgroundColor = _initBackgroundColor();
111
112 static Color _initBackgroundColor() {
113 Color result = const Color(0xF0C0C0C0);
114 assert(() {
115 result = const Color(0xF0900000);
116 return true;
117 }());
118 return result;
119 }
120
121 /// The text style to use when painting [RenderErrorBox] objects.
122 ///
123 /// Defaults to a yellow monospace font in debug mode, and a dark gray
124 /// sans-serif font otherwise.
125 static ui.TextStyle textStyle = _initTextStyle();
126
127 static ui.TextStyle _initTextStyle() {
128 ui.TextStyle result = ui.TextStyle(
129 color: const Color(0xFF303030),
130 fontFamily: 'sans-serif',
131 fontSize: 18.0,
132 );
133 assert(() {
134 result = ui.TextStyle(
135 color: const Color(0xFFFFFF66),
136 fontFamily: 'monospace',
137 fontSize: 14.0,
138 fontWeight: FontWeight.bold,
139 );
140 return true;
141 }());
142 return result;
143 }
144
145 /// The paragraph style to use when painting [RenderErrorBox] objects.
146 static ui.ParagraphStyle paragraphStyle = ui.ParagraphStyle(
147 textDirection: TextDirection.ltr,
148 textAlign: TextAlign.left,
149 );
150
151 @override
152 void paint(PaintingContext context, Offset offset) {
153 try {
154 context.canvas.drawRect(offset & size, Paint() .. color = backgroundColor);
155 if (_paragraph != null) {
156 double width = size.width;
157 double left = 0.0;
158 double top = 0.0;
159 if (width > padding.left + minimumWidth + padding.right) {
160 width -= padding.left + padding.right;
161 left += padding.left;
162 }
163 _paragraph.layout(ui.ParagraphConstraints(width: width));
164 if (size.height > padding.top + _paragraph.height + padding.bottom) {
165 top += padding.top;
166 }
167 context.canvas.drawParagraph(_paragraph, offset + Offset(left, top));
168 }
169 } catch (error) {
170 // If an error happens here we're in a terrible state, so we really should
171 // just forget about it and let the developer deal with the already-reported
172 // errors. It's unlikely that these errors are going to help with that.
173 }
174 }
175}
176