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/// @docImport 'package:flutter/material.dart';
6library;
7
8import 'package:flutter/foundation.dart';
9import 'package:flutter/painting.dart' hide Border;
10
11/// Border specification for [Table] widgets.
12///
13/// This is like [Border], with the addition of two sides: the inner horizontal
14/// borders between rows and the inner vertical borders between columns.
15///
16/// The sides are represented by [BorderSide] objects.
17@immutable
18class TableBorder {
19 /// Creates a border for a table.
20 ///
21 /// All the sides of the border default to [BorderSide.none].
22 const TableBorder({
23 this.top = BorderSide.none,
24 this.right = BorderSide.none,
25 this.bottom = BorderSide.none,
26 this.left = BorderSide.none,
27 this.horizontalInside = BorderSide.none,
28 this.verticalInside = BorderSide.none,
29 this.borderRadius = BorderRadius.zero,
30 });
31
32 /// A uniform border with all sides the same color and width.
33 ///
34 /// The sides default to black solid borders, one logical pixel wide.
35 factory TableBorder.all({
36 Color color = const Color(0xFF000000),
37 double width = 1.0,
38 BorderStyle style = BorderStyle.solid,
39 BorderRadius borderRadius = BorderRadius.zero,
40 }) {
41 final BorderSide side = BorderSide(color: color, width: width, style: style);
42 return TableBorder(
43 top: side,
44 right: side,
45 bottom: side,
46 left: side,
47 horizontalInside: side,
48 verticalInside: side,
49 borderRadius: borderRadius,
50 );
51 }
52
53 /// Creates a border for a table where all the interior sides use the same
54 /// styling and all the exterior sides use the same styling.
55 const TableBorder.symmetric({
56 BorderSide inside = BorderSide.none,
57 BorderSide outside = BorderSide.none,
58 this.borderRadius = BorderRadius.zero,
59 }) : top = outside,
60 right = outside,
61 bottom = outside,
62 left = outside,
63 horizontalInside = inside,
64 verticalInside = inside;
65
66 /// The top side of this border.
67 final BorderSide top;
68
69 /// The right side of this border.
70 final BorderSide right;
71
72 /// The bottom side of this border.
73 final BorderSide bottom;
74
75 /// The left side of this border.
76 final BorderSide left;
77
78 /// The horizontal interior sides of this border.
79 final BorderSide horizontalInside;
80
81 /// The vertical interior sides of this border.
82 final BorderSide verticalInside;
83
84 /// The [BorderRadius] to use when painting the corners of this border.
85 ///
86 /// It is also applied to [DataTable]'s [Material].
87 final BorderRadius borderRadius;
88
89 /// The widths of the sides of this border represented as an [EdgeInsets].
90 ///
91 /// This can be used, for example, with a [Padding] widget to inset a box by
92 /// the size of these borders.
93 EdgeInsets get dimensions {
94 return EdgeInsets.fromLTRB(left.width, top.width, right.width, bottom.width);
95 }
96
97 /// Whether all the sides of the border (outside and inside) are identical.
98 /// Uniform borders are typically more efficient to paint.
99 bool get isUniform {
100 final Color topColor = top.color;
101 if (right.color != topColor ||
102 bottom.color != topColor ||
103 left.color != topColor ||
104 horizontalInside.color != topColor ||
105 verticalInside.color != topColor) {
106 return false;
107 }
108
109 final double topWidth = top.width;
110 if (right.width != topWidth ||
111 bottom.width != topWidth ||
112 left.width != topWidth ||
113 horizontalInside.width != topWidth ||
114 verticalInside.width != topWidth) {
115 return false;
116 }
117
118 final BorderStyle topStyle = top.style;
119 if (right.style != topStyle ||
120 bottom.style != topStyle ||
121 left.style != topStyle ||
122 horizontalInside.style != topStyle ||
123 verticalInside.style != topStyle) {
124 return false;
125 }
126
127 return true;
128 }
129
130 /// Creates a copy of this border but with the widths scaled by the factor `t`.
131 ///
132 /// The `t` argument represents the multiplicand, or the position on the
133 /// timeline for an interpolation from nothing to `this`, with 0.0 meaning
134 /// that the object returned should be the nil variant of this object, 1.0
135 /// meaning that no change should be applied, returning `this` (or something
136 /// equivalent to `this`), and other values meaning that the object should be
137 /// multiplied by `t`. Negative values are treated like zero.
138 ///
139 /// Values for `t` are usually obtained from an [Animation<double>], such as
140 /// an [AnimationController].
141 ///
142 /// See also:
143 ///
144 /// * [BorderSide.scale], which is used to implement this method.
145 TableBorder scale(double t) {
146 return TableBorder(
147 top: top.scale(t),
148 right: right.scale(t),
149 bottom: bottom.scale(t),
150 left: left.scale(t),
151 horizontalInside: horizontalInside.scale(t),
152 verticalInside: verticalInside.scale(t),
153 );
154 }
155
156 /// Linearly interpolate between two table borders.
157 ///
158 /// If a border is null, it is treated as having only [BorderSide.none]
159 /// borders.
160 ///
161 /// {@macro dart.ui.shadow.lerp}
162 static TableBorder? lerp(TableBorder? a, TableBorder? b, double t) {
163 if (identical(a, b)) {
164 return a;
165 }
166 if (a == null) {
167 return b!.scale(t);
168 }
169 if (b == null) {
170 return a.scale(1.0 - t);
171 }
172 return TableBorder(
173 top: BorderSide.lerp(a.top, b.top, t),
174 right: BorderSide.lerp(a.right, b.right, t),
175 bottom: BorderSide.lerp(a.bottom, b.bottom, t),
176 left: BorderSide.lerp(a.left, b.left, t),
177 horizontalInside: BorderSide.lerp(a.horizontalInside, b.horizontalInside, t),
178 verticalInside: BorderSide.lerp(a.verticalInside, b.verticalInside, t),
179 );
180 }
181
182 /// Paints the border around the given [Rect] on the given [Canvas], with the
183 /// given rows and columns.
184 ///
185 /// Uniform borders are more efficient to paint than more complex borders.
186 ///
187 /// The `rows` argument specifies the vertical positions between the rows,
188 /// relative to the given rectangle. For example, if the table contained two
189 /// rows of height 100.0 each, then `rows` would contain a single value,
190 /// 100.0, which is the vertical position between the two rows (relative to
191 /// the top edge of `rect`).
192 ///
193 /// The `columns` argument specifies the horizontal positions between the
194 /// columns, relative to the given rectangle. For example, if the table
195 /// contained two columns of height 100.0 each, then `columns` would contain a
196 /// single value, 100.0, which is the vertical position between the two
197 /// columns (relative to the left edge of `rect`).
198 ///
199 /// The [verticalInside] border is only drawn if there are at least two
200 /// columns. The [horizontalInside] border is only drawn if there are at least
201 /// two rows. The horizontal borders are drawn after the vertical borders.
202 ///
203 /// The outer borders (in the order [top], [right], [bottom], [left], with
204 /// [left] above the others) are painted after the inner borders.
205 ///
206 /// The paint order is particularly notable in the case of
207 /// partially-transparent borders.
208 void paint(
209 Canvas canvas,
210 Rect rect, {
211 required Iterable<double> rows,
212 required Iterable<double> columns,
213 }) {
214 // properties can't be null
215
216 // arguments can't be null
217 assert(rows.isEmpty || (rows.first >= 0.0 && rows.last <= rect.height));
218 assert(columns.isEmpty || (columns.first >= 0.0 && columns.last <= rect.width));
219
220 if (columns.isNotEmpty || rows.isNotEmpty) {
221 final Paint paint = Paint();
222 final Path path = Path();
223
224 if (columns.isNotEmpty) {
225 switch (verticalInside.style) {
226 case BorderStyle.solid:
227 paint
228 ..color = verticalInside.color
229 ..strokeWidth = verticalInside.width
230 ..style = PaintingStyle.stroke;
231 path.reset();
232 for (final double x in columns) {
233 path.moveTo(rect.left + x, rect.top);
234 path.lineTo(rect.left + x, rect.bottom);
235 }
236 canvas.drawPath(path, paint);
237 case BorderStyle.none:
238 break;
239 }
240 }
241
242 if (rows.isNotEmpty) {
243 switch (horizontalInside.style) {
244 case BorderStyle.solid:
245 paint
246 ..color = horizontalInside.color
247 ..strokeWidth = horizontalInside.width
248 ..style = PaintingStyle.stroke;
249 path.reset();
250 for (final double y in rows) {
251 path.moveTo(rect.left, rect.top + y);
252 path.lineTo(rect.right, rect.top + y);
253 }
254 canvas.drawPath(path, paint);
255 case BorderStyle.none:
256 break;
257 }
258 }
259 }
260 if (!isUniform || borderRadius == BorderRadius.zero) {
261 paintBorder(canvas, rect, top: top, right: right, bottom: bottom, left: left);
262 } else {
263 final RRect outer = borderRadius.toRRect(rect);
264 final RRect inner = outer.deflate(top.width);
265 final Paint paint = Paint()..color = top.color;
266 canvas.drawDRRect(outer, inner, paint);
267 }
268 }
269
270 @override
271 bool operator ==(Object other) {
272 if (identical(this, other)) {
273 return true;
274 }
275 if (other.runtimeType != runtimeType) {
276 return false;
277 }
278 return other is TableBorder &&
279 other.top == top &&
280 other.right == right &&
281 other.bottom == bottom &&
282 other.left == left &&
283 other.horizontalInside == horizontalInside &&
284 other.verticalInside == verticalInside &&
285 other.borderRadius == borderRadius;
286 }
287
288 @override
289 int get hashCode =>
290 Object.hash(top, right, bottom, left, horizontalInside, verticalInside, borderRadius);
291
292 @override
293 String toString() =>
294 'TableBorder($top, $right, $bottom, $left, $horizontalInside, $verticalInside, $borderRadius)';
295}
296

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com