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 | |
5 | import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle, TextStyle; |
6 | |
7 | import 'package:flutter/foundation.dart'; |
8 | |
9 | import 'box.dart'; |
10 | import 'object.dart'; |
11 | |
12 | const double _kMaxWidth = 100000.0; |
13 | const 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. |
30 | class 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 | |