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:math' as math; |
6 | import 'dart:ui' as ui show Image, ImageFilter, TextHeightBehavior; |
7 | |
8 | import 'package:flutter/animation.dart'; |
9 | import 'package:flutter/foundation.dart'; |
10 | import 'package:flutter/gestures.dart'; |
11 | import 'package:flutter/rendering.dart'; |
12 | import 'package:flutter/services.dart'; |
13 | |
14 | import 'binding.dart'; |
15 | import 'debug.dart'; |
16 | import 'framework.dart'; |
17 | import 'localizations.dart'; |
18 | import 'visibility.dart'; |
19 | import 'widget_span.dart'; |
20 | |
21 | export 'package:flutter/animation.dart'; |
22 | export 'package:flutter/foundation.dart' show |
23 | ChangeNotifier, |
24 | FlutterErrorDetails, |
25 | Listenable, |
26 | TargetPlatform, |
27 | ValueNotifier; |
28 | export 'package:flutter/painting.dart'; |
29 | export 'package:flutter/rendering.dart' show |
30 | AlignmentGeometryTween, |
31 | AlignmentTween, |
32 | Axis, |
33 | BoxConstraints, |
34 | BoxConstraintsTransform, |
35 | CrossAxisAlignment, |
36 | CustomClipper, |
37 | CustomPainter, |
38 | CustomPainterSemantics, |
39 | DecorationPosition, |
40 | FlexFit, |
41 | FlowDelegate, |
42 | FlowPaintingContext, |
43 | FractionalOffsetTween, |
44 | HitTestBehavior, |
45 | LayerLink, |
46 | MainAxisAlignment, |
47 | MainAxisSize, |
48 | MouseCursor, |
49 | MultiChildLayoutDelegate, |
50 | PaintingContext, |
51 | PointerCancelEvent, |
52 | PointerCancelEventListener, |
53 | PointerDownEvent, |
54 | PointerDownEventListener, |
55 | PointerEvent, |
56 | PointerMoveEvent, |
57 | PointerMoveEventListener, |
58 | PointerUpEvent, |
59 | PointerUpEventListener, |
60 | RelativeRect, |
61 | SemanticsBuilderCallback, |
62 | ShaderCallback, |
63 | ShapeBorderClipper, |
64 | SingleChildLayoutDelegate, |
65 | StackFit, |
66 | SystemMouseCursors, |
67 | TextOverflow, |
68 | ValueChanged, |
69 | ValueGetter, |
70 | WrapAlignment, |
71 | WrapCrossAlignment; |
72 | export 'package:flutter/services.dart' show |
73 | AssetBundle; |
74 | |
75 | // Examples can assume: |
76 | // class TestWidget extends StatelessWidget { const TestWidget({super.key}); @override Widget build(BuildContext context) => const Placeholder(); } |
77 | // late WidgetTester tester; |
78 | // late bool _visible; |
79 | // class Sky extends CustomPainter { @override void paint(Canvas c, Size s) {} @override bool shouldRepaint(Sky s) => false; } |
80 | // late BuildContext context; |
81 | // String userAvatarUrl = ''; |
82 | |
83 | // BIDIRECTIONAL TEXT SUPPORT |
84 | |
85 | /// An [InheritedElement] that has hundreds of dependencies but will |
86 | /// infrequently change. This provides a performance tradeoff where building |
87 | /// the [Widget]s is faster but performing updates is slower. |
88 | /// |
89 | /// | | _UbiquitousInheritedElement | InheritedElement | |
90 | /// |---------------------|------------------------------|------------------| |
91 | /// | insert (best case) | O(1) | O(1) | |
92 | /// | insert (worst case) | O(1) | O(n) | |
93 | /// | search (best case) | O(n) | O(1) | |
94 | /// | search (worst case) | O(n) | O(n) | |
95 | /// |
96 | /// Insert happens when building the [Widget] tree, search happens when updating |
97 | /// [Widget]s. |
98 | class _UbiquitousInheritedElement extends InheritedElement { |
99 | /// Creates an element that uses the given widget as its configuration. |
100 | _UbiquitousInheritedElement(super.widget); |
101 | |
102 | @override |
103 | void setDependencies(Element dependent, Object? value) { |
104 | // This is where the cost of [InheritedElement] is incurred during build |
105 | // time of the widget tree. Omitting this bookkeeping is where the |
106 | // performance savings come from. |
107 | assert(value == null); |
108 | } |
109 | |
110 | @override |
111 | Object? getDependencies(Element dependent) { |
112 | return null; |
113 | } |
114 | |
115 | @override |
116 | void notifyClients(InheritedWidget oldWidget) { |
117 | _recurseChildren(this, (Element element) { |
118 | if (element.doesDependOnInheritedElement(this)) { |
119 | notifyDependent(oldWidget, element); |
120 | } |
121 | }); |
122 | } |
123 | |
124 | static void _recurseChildren(Element element, ElementVisitor visitor) { |
125 | element.visitChildren((Element child) { |
126 | _recurseChildren(child, visitor); |
127 | }); |
128 | visitor(element); |
129 | } |
130 | } |
131 | |
132 | /// See also: |
133 | /// |
134 | /// * [_UbiquitousInheritedElement], the [Element] for [_UbiquitousInheritedWidget]. |
135 | abstract class _UbiquitousInheritedWidget extends InheritedWidget { |
136 | const _UbiquitousInheritedWidget({super.key, required super.child}); |
137 | |
138 | @override |
139 | InheritedElement createElement() => _UbiquitousInheritedElement(this); |
140 | } |
141 | |
142 | /// A widget that determines the ambient directionality of text and |
143 | /// text-direction-sensitive render objects. |
144 | /// |
145 | /// For example, [Padding] depends on the [Directionality] to resolve |
146 | /// [EdgeInsetsDirectional] objects into absolute [EdgeInsets] objects. |
147 | class Directionality extends _UbiquitousInheritedWidget { |
148 | /// Creates a widget that determines the directionality of text and |
149 | /// text-direction-sensitive render objects. |
150 | const Directionality({ |
151 | super.key, |
152 | required this.textDirection, |
153 | required super.child, |
154 | }); |
155 | |
156 | /// The text direction for this subtree. |
157 | final TextDirection textDirection; |
158 | |
159 | /// The text direction from the closest instance of this class that encloses |
160 | /// the given context. |
161 | /// |
162 | /// If there is no [Directionality] ancestor widget in the tree at the given |
163 | /// context, then this will throw a descriptive [FlutterError] in debug mode |
164 | /// and an exception in release mode. |
165 | /// |
166 | /// Typical usage is as follows: |
167 | /// |
168 | /// ```dart |
169 | /// TextDirection textDirection = Directionality.of(context); |
170 | /// ``` |
171 | /// |
172 | /// See also: |
173 | /// |
174 | /// * [maybeOf], which will return null if no [Directionality] ancestor |
175 | /// widget is in the tree. |
176 | static TextDirection of(BuildContext context) { |
177 | assert(debugCheckHasDirectionality(context)); |
178 | final Directionality widget = context.dependOnInheritedWidgetOfExactType<Directionality>()!; |
179 | return widget.textDirection; |
180 | } |
181 | |
182 | /// The text direction from the closest instance of this class that encloses |
183 | /// the given context. |
184 | /// |
185 | /// If there is no [Directionality] ancestor widget in the tree at the given |
186 | /// context, then this will return null. |
187 | /// |
188 | /// Typical usage is as follows: |
189 | /// |
190 | /// ```dart |
191 | /// TextDirection? textDirection = Directionality.maybeOf(context); |
192 | /// ``` |
193 | /// |
194 | /// See also: |
195 | /// |
196 | /// * [of], which will throw if no [Directionality] ancestor widget is in the |
197 | /// tree. |
198 | static TextDirection? maybeOf(BuildContext context) { |
199 | final Directionality? widget = context.dependOnInheritedWidgetOfExactType<Directionality>(); |
200 | return widget?.textDirection; |
201 | } |
202 | |
203 | @override |
204 | bool updateShouldNotify(Directionality oldWidget) => textDirection != oldWidget.textDirection; |
205 | |
206 | @override |
207 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
208 | super.debugFillProperties(properties); |
209 | properties.add(EnumProperty<TextDirection>('textDirection' , textDirection)); |
210 | } |
211 | } |
212 | |
213 | |
214 | // PAINTING NODES |
215 | |
216 | /// A widget that makes its child partially transparent. |
217 | /// |
218 | /// This class paints its child into an intermediate buffer and then blends the |
219 | /// child back into the scene partially transparent. |
220 | /// |
221 | /// For values of opacity other than 0.0 and 1.0, this class is relatively |
222 | /// expensive because it requires painting the child into an intermediate |
223 | /// buffer. For the value 0.0, the child is not painted at all. For the |
224 | /// value 1.0, the child is painted immediately without an intermediate buffer. |
225 | /// |
226 | /// The presence of the intermediate buffer which has a transparent background |
227 | /// by default may cause some child widgets to behave differently. For example |
228 | /// a [BackdropFilter] child will only be able to apply its filter to the content |
229 | /// between this widget and the backdrop child and may require adjusting the |
230 | /// [BackdropFilter.blendMode] property to produce the desired results. |
231 | /// |
232 | /// {@youtube 560 315 https://www.youtube.com/watch?v=9hltevOHQBw} |
233 | /// |
234 | /// {@tool snippet} |
235 | /// |
236 | /// This example shows some [Text] when the `_visible` member field is true, and |
237 | /// hides it when it is false: |
238 | /// |
239 | /// ```dart |
240 | /// Opacity( |
241 | /// opacity: _visible ? 1.0 : 0.0, |
242 | /// child: const Text("Now you see me, now you don't!"), |
243 | /// ) |
244 | /// ``` |
245 | /// {@end-tool} |
246 | /// |
247 | /// This is more efficient than adding and removing the child widget from the |
248 | /// tree on demand. |
249 | /// |
250 | /// ## Performance considerations for opacity animation |
251 | /// |
252 | /// Animating an [Opacity] widget directly causes the widget (and possibly its |
253 | /// subtree) to rebuild each frame, which is not very efficient. Consider using |
254 | /// one of these alternative widgets instead: |
255 | /// |
256 | /// * [AnimatedOpacity], which uses an animation internally to efficiently |
257 | /// animate opacity. |
258 | /// * [FadeTransition], which uses a provided animation to efficiently animate |
259 | /// opacity. |
260 | /// |
261 | /// ## Transparent image |
262 | /// |
263 | /// If only a single [Image] or [Color] needs to be composited with an opacity |
264 | /// between 0.0 and 1.0, it's much faster to directly use them without [Opacity] |
265 | /// widgets. |
266 | /// |
267 | /// For example, `Container(color: Color.fromRGBO(255, 0, 0, 0.5))` is much |
268 | /// faster than `Opacity(opacity: 0.5, child: Container(color: Colors.red))`. |
269 | /// |
270 | /// {@tool snippet} |
271 | /// |
272 | /// The following example draws an [Image] with 0.5 opacity without using |
273 | /// [Opacity]: |
274 | /// |
275 | /// ```dart |
276 | /// Image.network( |
277 | /// 'https://raw.githubusercontent.com/flutter/assets-for-api-docs/master/packages/diagrams/assets/blend_mode_destination.jpeg', |
278 | /// color: const Color.fromRGBO(255, 255, 255, 0.5), |
279 | /// colorBlendMode: BlendMode.modulate |
280 | /// ) |
281 | /// ``` |
282 | /// |
283 | /// {@end-tool} |
284 | /// |
285 | /// Directly drawing an [Image] or [Color] with opacity is faster than using |
286 | /// [Opacity] on top of them because [Opacity] could apply the opacity to a |
287 | /// group of widgets and therefore a costly offscreen buffer will be used. |
288 | /// Drawing content into the offscreen buffer may also trigger render target |
289 | /// switches and such switching is particularly slow in older GPUs. |
290 | /// |
291 | /// ## Hit testing |
292 | /// |
293 | /// Setting the [opacity] to zero does not prevent hit testing from being applied |
294 | /// to the descendants of the [Opacity] widget. This can be confusing for the |
295 | /// user, who may not see anything, and may believe the area of the interface |
296 | /// where the [Opacity] is hiding a widget to be non-interactive. |
297 | /// |
298 | /// With certain widgets, such as [Flow], that compute their positions only when |
299 | /// they are painted, this can actually lead to bugs (from unexpected geometry |
300 | /// to exceptions), because those widgets are not painted by the [Opacity] |
301 | /// widget at all when the [opacity] is zero. |
302 | /// |
303 | /// To avoid such problems, it is generally a good idea to use an |
304 | /// [IgnorePointer] widget when setting the [opacity] to zero. This prevents |
305 | /// interactions with any children in the subtree. |
306 | /// |
307 | /// See also: |
308 | /// |
309 | /// * [Visibility], which can hide a child more efficiently (albeit less |
310 | /// subtly, because it is either visible or hidden, rather than allowing |
311 | /// fractional opacity values). Specifically, the [Visibility.maintain] |
312 | /// constructor is equivalent to using an opacity widget with values of |
313 | /// `0.0` or `1.0`. |
314 | /// * [ShaderMask], which can apply more elaborate effects to its child. |
315 | /// * [Transform], which applies an arbitrary transform to its child widget at |
316 | /// paint time. |
317 | /// * [SliverOpacity], the sliver version of this widget. |
318 | class Opacity extends SingleChildRenderObjectWidget { |
319 | /// Creates a widget that makes its child partially transparent. |
320 | /// |
321 | /// The [opacity] argument must be between zero and one, inclusive. |
322 | const Opacity({ |
323 | super.key, |
324 | required this.opacity, |
325 | this.alwaysIncludeSemantics = false, |
326 | super.child, |
327 | }) : assert(opacity >= 0.0 && opacity <= 1.0); |
328 | |
329 | /// The fraction to scale the child's alpha value. |
330 | /// |
331 | /// An opacity of one is fully opaque. An opacity of zero is fully transparent |
332 | /// (i.e., invisible). |
333 | /// |
334 | /// Values one and zero are painted with a fast path. Other values require |
335 | /// painting the child into an intermediate buffer, which is expensive. |
336 | final double opacity; |
337 | |
338 | /// Whether the semantic information of the children is always included. |
339 | /// |
340 | /// Defaults to false. |
341 | /// |
342 | /// When true, regardless of the opacity settings the child semantic |
343 | /// information is exposed as if the widget were fully visible. This is |
344 | /// useful in cases where labels may be hidden during animations that |
345 | /// would otherwise contribute relevant semantics. |
346 | final bool alwaysIncludeSemantics; |
347 | |
348 | @override |
349 | RenderOpacity createRenderObject(BuildContext context) { |
350 | return RenderOpacity( |
351 | opacity: opacity, |
352 | alwaysIncludeSemantics: alwaysIncludeSemantics, |
353 | ); |
354 | } |
355 | |
356 | @override |
357 | void updateRenderObject(BuildContext context, RenderOpacity renderObject) { |
358 | renderObject |
359 | ..opacity = opacity |
360 | ..alwaysIncludeSemantics = alwaysIncludeSemantics; |
361 | } |
362 | |
363 | @override |
364 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
365 | super.debugFillProperties(properties); |
366 | properties.add(DoubleProperty('opacity' , opacity)); |
367 | properties.add(FlagProperty('alwaysIncludeSemantics' , value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics' )); |
368 | } |
369 | } |
370 | |
371 | /// A widget that applies a mask generated by a [Shader] to its child. |
372 | /// |
373 | /// For example, [ShaderMask] can be used to gradually fade out the edge |
374 | /// of a child by using a [ui.Gradient.linear] mask. |
375 | /// |
376 | /// {@youtube 560 315 https://www.youtube.com/watch?v=7sUL66pTQ7Q} |
377 | /// |
378 | /// {@tool snippet} |
379 | /// |
380 | /// This example makes the text look like it is on fire: |
381 | /// |
382 | /// ```dart |
383 | /// ShaderMask( |
384 | /// shaderCallback: (Rect bounds) { |
385 | /// return RadialGradient( |
386 | /// center: Alignment.topLeft, |
387 | /// radius: 1.0, |
388 | /// colors: <Color>[Colors.yellow, Colors.deepOrange.shade900], |
389 | /// tileMode: TileMode.mirror, |
390 | /// ).createShader(bounds); |
391 | /// }, |
392 | /// child: const Text( |
393 | /// 'I’m burning the memories', |
394 | /// style: TextStyle(color: Colors.white), |
395 | /// ), |
396 | /// ) |
397 | /// ``` |
398 | /// {@end-tool} |
399 | /// |
400 | /// See also: |
401 | /// |
402 | /// * [Opacity], which can apply a uniform alpha effect to its child. |
403 | /// * [CustomPaint], which lets you draw directly on the canvas. |
404 | /// * [DecoratedBox], for another approach at decorating child widgets. |
405 | /// * [BackdropFilter], which applies an image filter to the background. |
406 | class ShaderMask extends SingleChildRenderObjectWidget { |
407 | /// Creates a widget that applies a mask generated by a [Shader] to its child. |
408 | const ShaderMask({ |
409 | super.key, |
410 | required this.shaderCallback, |
411 | this.blendMode = BlendMode.modulate, |
412 | super.child, |
413 | }); |
414 | |
415 | /// Called to create the [dart:ui.Shader] that generates the mask. |
416 | /// |
417 | /// The shader callback is called with the current size of the child so that |
418 | /// it can customize the shader to the size and location of the child. |
419 | /// |
420 | /// Typically this will use a [LinearGradient], [RadialGradient], or |
421 | /// [SweepGradient] to create the [dart:ui.Shader], though the |
422 | /// [dart:ui.ImageShader] class could also be used. |
423 | final ShaderCallback shaderCallback; |
424 | |
425 | /// The [BlendMode] to use when applying the shader to the child. |
426 | /// |
427 | /// The default, [BlendMode.modulate], is useful for applying an alpha blend |
428 | /// to the child. Other blend modes can be used to create other effects. |
429 | final BlendMode blendMode; |
430 | |
431 | @override |
432 | RenderShaderMask createRenderObject(BuildContext context) { |
433 | return RenderShaderMask( |
434 | shaderCallback: shaderCallback, |
435 | blendMode: blendMode, |
436 | ); |
437 | } |
438 | |
439 | @override |
440 | void updateRenderObject(BuildContext context, RenderShaderMask renderObject) { |
441 | renderObject |
442 | ..shaderCallback = shaderCallback |
443 | ..blendMode = blendMode; |
444 | } |
445 | } |
446 | |
447 | /// A widget that applies a filter to the existing painted content and then |
448 | /// paints [child]. |
449 | /// |
450 | /// The filter will be applied to all the area within its parent or ancestor |
451 | /// widget's clip. If there's no clip, the filter will be applied to the full |
452 | /// screen. |
453 | /// |
454 | /// The results of the filter will be blended back into the background using |
455 | /// the [blendMode] parameter. |
456 | /// {@template flutter.widgets.BackdropFilter.blendMode} |
457 | /// The only value for [blendMode] that is supported on all platforms is |
458 | /// [BlendMode.srcOver] which works well for most scenes. But that value may |
459 | /// produce surprising results when a parent of the [BackdropFilter] uses a |
460 | /// temporary buffer, or save layer, as does an [Opacity] widget. In that |
461 | /// situation, a value of [BlendMode.src] can produce more pleasing results, |
462 | /// but at the cost of incompatibility with some platforms, most notably the |
463 | /// html renderer for web applications. |
464 | /// {@endtemplate} |
465 | /// |
466 | /// {@youtube 560 315 https://www.youtube.com/watch?v=dYRs7Q1vfYI} |
467 | /// |
468 | /// {@tool snippet} |
469 | /// If the [BackdropFilter] needs to be applied to an area that exactly matches |
470 | /// its child, wraps the [BackdropFilter] with a clip widget that clips exactly |
471 | /// to that child. |
472 | /// |
473 | /// ```dart |
474 | /// Stack( |
475 | /// fit: StackFit.expand, |
476 | /// children: <Widget>[ |
477 | /// Text('0' * 10000), |
478 | /// Center( |
479 | /// child: ClipRect( // <-- clips to the 200x200 [Container] below |
480 | /// child: BackdropFilter( |
481 | /// filter: ui.ImageFilter.blur( |
482 | /// sigmaX: 5.0, |
483 | /// sigmaY: 5.0, |
484 | /// ), |
485 | /// child: Container( |
486 | /// alignment: Alignment.center, |
487 | /// width: 200.0, |
488 | /// height: 200.0, |
489 | /// child: const Text('Hello World'), |
490 | /// ), |
491 | /// ), |
492 | /// ), |
493 | /// ), |
494 | /// ], |
495 | /// ) |
496 | /// ``` |
497 | /// {@end-tool} |
498 | /// |
499 | /// This effect is relatively expensive, especially if the filter is non-local, |
500 | /// such as a blur. |
501 | /// |
502 | /// If all you want to do is apply an [ImageFilter] to a single widget |
503 | /// (as opposed to applying the filter to everything _beneath_ a widget), use |
504 | /// [ImageFiltered] instead. For that scenario, [ImageFiltered] is both |
505 | /// easier to use and less expensive than [BackdropFilter]. |
506 | /// |
507 | /// {@tool snippet} |
508 | /// |
509 | /// This example shows how the common case of applying a [BackdropFilter] blur |
510 | /// to a single sibling can be replaced with an [ImageFiltered] widget. This code |
511 | /// is generally simpler and the performance will be improved dramatically for |
512 | /// complex filters like blurs. |
513 | /// |
514 | /// The implementation below is unnecessarily expensive. |
515 | /// |
516 | /// ```dart |
517 | /// Widget buildBackdrop() { |
518 | /// return Stack( |
519 | /// children: <Widget>[ |
520 | /// Positioned.fill(child: Image.asset('image.png')), |
521 | /// Positioned.fill( |
522 | /// child: BackdropFilter( |
523 | /// filter: ui.ImageFilter.blur(sigmaX: 6, sigmaY: 6), |
524 | /// ), |
525 | /// ), |
526 | /// ], |
527 | /// ); |
528 | /// } |
529 | /// ``` |
530 | /// {@end-tool} |
531 | /// {@tool snippet} |
532 | /// Instead consider the following approach which directly applies a blur |
533 | /// to the child widget. |
534 | /// |
535 | /// ```dart |
536 | /// Widget buildFilter() { |
537 | /// return ImageFiltered( |
538 | /// imageFilter: ui.ImageFilter.blur(sigmaX: 6, sigmaY: 6), |
539 | /// child: Image.asset('image.png'), |
540 | /// ); |
541 | /// } |
542 | /// ``` |
543 | /// |
544 | /// {@end-tool} |
545 | /// |
546 | /// See also: |
547 | /// |
548 | /// * [ImageFiltered], which applies an [ImageFilter] to its child. |
549 | /// * [DecoratedBox], which draws a background under (or over) a widget. |
550 | /// * [Opacity], which changes the opacity of the widget itself. |
551 | /// * https://flutter.dev/go/ios-platformview-backdrop-filter-blur for details and restrictions when an iOS PlatformView needs to be blurred. |
552 | class BackdropFilter extends SingleChildRenderObjectWidget { |
553 | /// Creates a backdrop filter. |
554 | /// |
555 | /// The [blendMode] argument will default to [BlendMode.srcOver] and must not be |
556 | /// null if provided. |
557 | const BackdropFilter({ |
558 | super.key, |
559 | required this.filter, |
560 | super.child, |
561 | this.blendMode = BlendMode.srcOver, |
562 | }); |
563 | |
564 | /// The image filter to apply to the existing painted content before painting the child. |
565 | /// |
566 | /// For example, consider using [ImageFilter.blur] to create a backdrop |
567 | /// blur effect. |
568 | final ui.ImageFilter filter; |
569 | |
570 | /// The blend mode to use to apply the filtered background content onto the background |
571 | /// surface. |
572 | /// |
573 | /// {@macro flutter.widgets.BackdropFilter.blendMode} |
574 | final BlendMode blendMode; |
575 | |
576 | @override |
577 | RenderBackdropFilter createRenderObject(BuildContext context) { |
578 | return RenderBackdropFilter(filter: filter, blendMode: blendMode); |
579 | } |
580 | |
581 | @override |
582 | void updateRenderObject(BuildContext context, RenderBackdropFilter renderObject) { |
583 | renderObject |
584 | ..filter = filter |
585 | ..blendMode = blendMode; |
586 | } |
587 | } |
588 | |
589 | /// A widget that provides a canvas on which to draw during the paint phase. |
590 | /// |
591 | /// When asked to paint, [CustomPaint] first asks its [painter] to paint on the |
592 | /// current canvas, then it paints its child, and then, after painting its |
593 | /// child, it asks its [foregroundPainter] to paint. The coordinate system of the |
594 | /// canvas matches the coordinate system of the [CustomPaint] object. The |
595 | /// painters are expected to paint within a rectangle starting at the origin and |
596 | /// encompassing a region of the given size. (If the painters paint outside |
597 | /// those bounds, there might be insufficient memory allocated to rasterize the |
598 | /// painting commands and the resulting behavior is undefined.) To enforce |
599 | /// painting within those bounds, consider wrapping this [CustomPaint] with a |
600 | /// [ClipRect] widget. |
601 | /// |
602 | /// Painters are implemented by subclassing [CustomPainter]. |
603 | /// |
604 | /// {@youtube 560 315 https://www.youtube.com/watch?v=kp14Y4uHpHs} |
605 | /// |
606 | /// Because custom paint calls its painters during paint, you cannot call |
607 | /// `setState` or `markNeedsLayout` during the callback (the layout for this |
608 | /// frame has already happened). |
609 | /// |
610 | /// Custom painters normally size themselves to their [child]. If they do not |
611 | /// have a child, they attempt to size themselves to the specified [size], which |
612 | /// defaults to [Size.zero]. The parent [may enforce constraints on this |
613 | /// size](https://docs.flutter.dev/ui/layout/constraints). |
614 | /// |
615 | /// The [isComplex] and [willChange] properties are hints to the compositor's |
616 | /// raster cache. |
617 | /// |
618 | /// {@tool snippet} |
619 | /// |
620 | /// This example shows how the sample custom painter shown at [CustomPainter] |
621 | /// could be used in a [CustomPaint] widget to display a background to some |
622 | /// text. |
623 | /// |
624 | /// ```dart |
625 | /// CustomPaint( |
626 | /// painter: Sky(), |
627 | /// child: const Center( |
628 | /// child: Text( |
629 | /// 'Once upon a time...', |
630 | /// style: TextStyle( |
631 | /// fontSize: 40.0, |
632 | /// fontWeight: FontWeight.w900, |
633 | /// color: Color(0xFFFFFFFF), |
634 | /// ), |
635 | /// ), |
636 | /// ), |
637 | /// ) |
638 | /// ``` |
639 | /// {@end-tool} |
640 | /// |
641 | /// See also: |
642 | /// |
643 | /// * [CustomPainter], the class to extend when creating custom painters. |
644 | /// * [Canvas], the class that a custom painter uses to paint. |
645 | class CustomPaint extends SingleChildRenderObjectWidget { |
646 | /// Creates a widget that delegates its painting. |
647 | const CustomPaint({ |
648 | super.key, |
649 | this.painter, |
650 | this.foregroundPainter, |
651 | this.size = Size.zero, |
652 | this.isComplex = false, |
653 | this.willChange = false, |
654 | super.child, |
655 | }) : assert(painter != null || foregroundPainter != null || (!isComplex && !willChange)); |
656 | |
657 | /// The painter that paints before the children. |
658 | final CustomPainter? painter; |
659 | |
660 | /// The painter that paints after the children. |
661 | final CustomPainter? foregroundPainter; |
662 | |
663 | /// The size that this [CustomPaint] should aim for, given the layout |
664 | /// constraints, if there is no child. |
665 | /// |
666 | /// Defaults to [Size.zero]. |
667 | /// |
668 | /// If there's a child, this is ignored, and the size of the child is used |
669 | /// instead. |
670 | final Size size; |
671 | |
672 | /// Whether the painting is complex enough to benefit from caching. |
673 | /// |
674 | /// The compositor contains a raster cache that holds bitmaps of layers in |
675 | /// order to avoid the cost of repeatedly rendering those layers on each |
676 | /// frame. If this flag is not set, then the compositor will apply its own |
677 | /// heuristics to decide whether the layer containing this widget is complex |
678 | /// enough to benefit from caching. |
679 | /// |
680 | /// This flag can't be set to true if both [painter] and [foregroundPainter] |
681 | /// are null because this flag will be ignored in such case. |
682 | final bool isComplex; |
683 | |
684 | /// Whether the raster cache should be told that this painting is likely |
685 | /// to change in the next frame. |
686 | /// |
687 | /// This hint tells the compositor not to cache the layer containing this |
688 | /// widget because the cache will not be used in the future. If this hint is |
689 | /// not set, the compositor will apply its own heuristics to decide whether |
690 | /// the layer is likely to be reused in the future. |
691 | /// |
692 | /// This flag can't be set to true if both [painter] and [foregroundPainter] |
693 | /// are null because this flag will be ignored in such case. |
694 | final bool willChange; |
695 | |
696 | @override |
697 | RenderCustomPaint createRenderObject(BuildContext context) { |
698 | return RenderCustomPaint( |
699 | painter: painter, |
700 | foregroundPainter: foregroundPainter, |
701 | preferredSize: size, |
702 | isComplex: isComplex, |
703 | willChange: willChange, |
704 | ); |
705 | } |
706 | |
707 | @override |
708 | void updateRenderObject(BuildContext context, RenderCustomPaint renderObject) { |
709 | renderObject |
710 | ..painter = painter |
711 | ..foregroundPainter = foregroundPainter |
712 | ..preferredSize = size |
713 | ..isComplex = isComplex |
714 | ..willChange = willChange; |
715 | } |
716 | |
717 | @override |
718 | void didUnmountRenderObject(RenderCustomPaint renderObject) { |
719 | renderObject |
720 | ..painter = null |
721 | ..foregroundPainter = null; |
722 | } |
723 | } |
724 | |
725 | /// A widget that clips its child using a rectangle. |
726 | /// |
727 | /// By default, [ClipRect] prevents its child from painting outside its |
728 | /// bounds, but the size and location of the clip rect can be customized using a |
729 | /// custom [clipper]. |
730 | /// |
731 | /// [ClipRect] is commonly used with these widgets, which commonly paint outside |
732 | /// their bounds: |
733 | /// |
734 | /// * [CustomPaint] |
735 | /// * [CustomSingleChildLayout] |
736 | /// * [CustomMultiChildLayout] |
737 | /// * [Align] and [Center] (e.g., if [Align.widthFactor] or |
738 | /// [Align.heightFactor] is less than 1.0). |
739 | /// * [OverflowBox] |
740 | /// * [SizedOverflowBox] |
741 | /// |
742 | /// {@tool snippet} |
743 | /// |
744 | /// For example, by combining a [ClipRect] with an [Align], one can show just |
745 | /// the top half of an [Image]: |
746 | /// |
747 | /// ```dart |
748 | /// ClipRect( |
749 | /// child: Align( |
750 | /// alignment: Alignment.topCenter, |
751 | /// heightFactor: 0.5, |
752 | /// child: Image.network(userAvatarUrl), |
753 | /// ), |
754 | /// ) |
755 | /// ``` |
756 | /// {@end-tool} |
757 | /// |
758 | /// See also: |
759 | /// |
760 | /// * [CustomClipper], for information about creating custom clips. |
761 | /// * [ClipRRect], for a clip with rounded corners. |
762 | /// * [ClipOval], for an elliptical clip. |
763 | /// * [ClipPath], for an arbitrarily shaped clip. |
764 | class ClipRect extends SingleChildRenderObjectWidget { |
765 | /// Creates a rectangular clip. |
766 | /// |
767 | /// If [clipper] is null, the clip will match the layout size and position of |
768 | /// the child. |
769 | /// |
770 | /// If [clipBehavior] is [Clip.none], no clipping will be applied. |
771 | const ClipRect({ |
772 | super.key, |
773 | this.clipper, |
774 | this.clipBehavior = Clip.hardEdge, |
775 | super.child, |
776 | }); |
777 | |
778 | /// If non-null, determines which clip to use. |
779 | final CustomClipper<Rect>? clipper; |
780 | |
781 | /// {@macro flutter.rendering.ClipRectLayer.clipBehavior} |
782 | /// |
783 | /// Defaults to [Clip.hardEdge]. |
784 | final Clip clipBehavior; |
785 | |
786 | @override |
787 | RenderClipRect createRenderObject(BuildContext context) { |
788 | return RenderClipRect(clipper: clipper, clipBehavior: clipBehavior); |
789 | } |
790 | |
791 | @override |
792 | void updateRenderObject(BuildContext context, RenderClipRect renderObject) { |
793 | renderObject |
794 | ..clipper = clipper |
795 | ..clipBehavior = clipBehavior; |
796 | } |
797 | |
798 | @override |
799 | void didUnmountRenderObject(RenderClipRect renderObject) { |
800 | renderObject.clipper = null; |
801 | } |
802 | |
803 | @override |
804 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
805 | super.debugFillProperties(properties); |
806 | properties.add(DiagnosticsProperty<CustomClipper<Rect>>('clipper' , clipper, defaultValue: null)); |
807 | } |
808 | } |
809 | |
810 | /// A widget that clips its child using a rounded rectangle. |
811 | /// |
812 | /// By default, [ClipRRect] uses its own bounds as the base rectangle for the |
813 | /// clip, but the size and location of the clip can be customized using a custom |
814 | /// [clipper]. |
815 | /// |
816 | /// {@youtube 560 315 https://www.youtube.com/watch?v=eI43jkQkrvs} |
817 | /// |
818 | /// {@tool dartpad} |
819 | /// This example shows various [ClipRRect]s applied to containers. |
820 | /// |
821 | /// ** See code in examples/api/lib/widgets/basic/clip_rrect.0.dart ** |
822 | /// {@end-tool} |
823 | /// |
824 | /// ## Troubleshooting |
825 | /// |
826 | /// ### Why doesn't my [ClipRRect] child have rounded corners? |
827 | /// |
828 | /// When a [ClipRRect] is bigger than the child it contains, its rounded corners |
829 | /// could be drawn in unexpected positions. Make sure that [ClipRRect] and its child |
830 | /// have the same bounds (by shrinking the [ClipRRect] with a [FittedBox] or by |
831 | /// growing the child). |
832 | /// |
833 | /// {@tool dartpad} |
834 | /// This example shows a [ClipRRect] that adds round corners to an image. |
835 | /// |
836 | /// ** See code in examples/api/lib/widgets/basic/clip_rrect.1.dart ** |
837 | /// {@end-tool} |
838 | /// |
839 | /// See also: |
840 | /// |
841 | /// * [CustomClipper], for information about creating custom clips. |
842 | /// * [ClipRect], for more efficient clips without rounded corners. |
843 | /// * [ClipOval], for an elliptical clip. |
844 | /// * [ClipPath], for an arbitrarily shaped clip. |
845 | class ClipRRect extends SingleChildRenderObjectWidget { |
846 | /// Creates a rounded-rectangular clip. |
847 | /// |
848 | /// The [borderRadius] defaults to [BorderRadius.zero], i.e. a rectangle with |
849 | /// right-angled corners. |
850 | /// |
851 | /// If [clipper] is non-null, then [borderRadius] is ignored. |
852 | /// |
853 | /// If [clipBehavior] is [Clip.none], no clipping will be applied. |
854 | const ClipRRect({ |
855 | super.key, |
856 | this.borderRadius = BorderRadius.zero, |
857 | this.clipper, |
858 | this.clipBehavior = Clip.antiAlias, |
859 | super.child, |
860 | }); |
861 | |
862 | /// The border radius of the rounded corners. |
863 | /// |
864 | /// Values are clamped so that horizontal and vertical radii sums do not |
865 | /// exceed width/height. |
866 | /// |
867 | /// This value is ignored if [clipper] is non-null. |
868 | final BorderRadiusGeometry borderRadius; |
869 | |
870 | /// If non-null, determines which clip to use. |
871 | final CustomClipper<RRect>? clipper; |
872 | |
873 | /// {@macro flutter.rendering.ClipRectLayer.clipBehavior} |
874 | /// |
875 | /// Defaults to [Clip.antiAlias]. |
876 | final Clip clipBehavior; |
877 | |
878 | @override |
879 | RenderClipRRect createRenderObject(BuildContext context) { |
880 | return RenderClipRRect( |
881 | borderRadius: borderRadius, |
882 | clipper: clipper, |
883 | clipBehavior: clipBehavior, |
884 | textDirection: Directionality.maybeOf(context), |
885 | ); |
886 | } |
887 | |
888 | @override |
889 | void updateRenderObject(BuildContext context, RenderClipRRect renderObject) { |
890 | renderObject |
891 | ..borderRadius = borderRadius |
892 | ..clipBehavior = clipBehavior |
893 | ..clipper = clipper |
894 | ..textDirection = Directionality.maybeOf(context); |
895 | } |
896 | |
897 | @override |
898 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
899 | super.debugFillProperties(properties); |
900 | properties.add(DiagnosticsProperty<BorderRadiusGeometry>('borderRadius' , borderRadius, showName: false, defaultValue: null)); |
901 | properties.add(DiagnosticsProperty<CustomClipper<RRect>>('clipper' , clipper, defaultValue: null)); |
902 | } |
903 | } |
904 | |
905 | /// A widget that clips its child using an oval. |
906 | /// |
907 | /// {@youtube 560 315 https://www.youtube.com/watch?v=vzWWDO6whIM} |
908 | /// |
909 | /// By default, inscribes an axis-aligned oval into its layout dimensions and |
910 | /// prevents its child from painting outside that oval, but the size and |
911 | /// location of the clip oval can be customized using a custom [clipper]. |
912 | /// |
913 | /// See also: |
914 | /// |
915 | /// * [CustomClipper], for information about creating custom clips. |
916 | /// * [ClipRect], for more efficient clips without rounded corners. |
917 | /// * [ClipRRect], for a clip with rounded corners. |
918 | /// * [ClipPath], for an arbitrarily shaped clip. |
919 | class ClipOval extends SingleChildRenderObjectWidget { |
920 | /// Creates an oval-shaped clip. |
921 | /// |
922 | /// If [clipper] is null, the oval will be inscribed into the layout size and |
923 | /// position of the child. |
924 | /// |
925 | /// If [clipBehavior] is [Clip.none], no clipping will be applied. |
926 | const ClipOval({ |
927 | super.key, |
928 | this.clipper, |
929 | this.clipBehavior = Clip.antiAlias, |
930 | super.child, |
931 | }); |
932 | |
933 | /// If non-null, determines which clip to use. |
934 | /// |
935 | /// The delegate returns a rectangle that describes the axis-aligned |
936 | /// bounding box of the oval. The oval's axes will themselves also |
937 | /// be axis-aligned. |
938 | /// |
939 | /// If the [clipper] delegate is null, then the oval uses the |
940 | /// widget's bounding box (the layout dimensions of the render |
941 | /// object) instead. |
942 | final CustomClipper<Rect>? clipper; |
943 | |
944 | /// {@macro flutter.rendering.ClipRectLayer.clipBehavior} |
945 | /// |
946 | /// Defaults to [Clip.antiAlias]. |
947 | final Clip clipBehavior; |
948 | |
949 | @override |
950 | RenderClipOval createRenderObject(BuildContext context) { |
951 | return RenderClipOval(clipper: clipper, clipBehavior: clipBehavior); |
952 | } |
953 | |
954 | @override |
955 | void updateRenderObject(BuildContext context, RenderClipOval renderObject) { |
956 | renderObject |
957 | ..clipper = clipper |
958 | ..clipBehavior = clipBehavior; |
959 | } |
960 | |
961 | @override |
962 | void didUnmountRenderObject(RenderClipOval renderObject) { |
963 | renderObject.clipper = null; |
964 | } |
965 | |
966 | @override |
967 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
968 | super.debugFillProperties(properties); |
969 | properties.add(DiagnosticsProperty<CustomClipper<Rect>>('clipper' , clipper, defaultValue: null)); |
970 | } |
971 | } |
972 | |
973 | /// A widget that clips its child using a path. |
974 | /// |
975 | /// Calls a callback on a delegate whenever the widget is to be |
976 | /// painted. The callback returns a path and the widget prevents the |
977 | /// child from painting outside the path. |
978 | /// |
979 | /// {@youtube 560 315 https://www.youtube.com/watch?v=oAUebVIb-7s} |
980 | /// |
981 | /// Clipping to a path is expensive. Certain shapes have more |
982 | /// optimized widgets: |
983 | /// |
984 | /// * To clip to a rectangle, consider [ClipRect]. |
985 | /// * To clip to an oval or circle, consider [ClipOval]. |
986 | /// * To clip to a rounded rectangle, consider [ClipRRect]. |
987 | /// |
988 | /// To clip to a particular [ShapeBorder], consider using either the |
989 | /// [ClipPath.shape] static method or the [ShapeBorderClipper] custom clipper |
990 | /// class. |
991 | class ClipPath extends SingleChildRenderObjectWidget { |
992 | /// Creates a path clip. |
993 | /// |
994 | /// If [clipper] is null, the clip will be a rectangle that matches the layout |
995 | /// size and location of the child. However, rather than use this default, |
996 | /// consider using a [ClipRect], which can achieve the same effect more |
997 | /// efficiently. |
998 | /// |
999 | /// If [clipBehavior] is [Clip.none], no clipping will be applied. |
1000 | const ClipPath({ |
1001 | super.key, |
1002 | this.clipper, |
1003 | this.clipBehavior = Clip.antiAlias, |
1004 | super.child, |
1005 | }); |
1006 | |
1007 | /// Creates a shape clip. |
1008 | /// |
1009 | /// Uses a [ShapeBorderClipper] to configure the [ClipPath] to clip to the |
1010 | /// given [ShapeBorder]. |
1011 | static Widget shape({ |
1012 | Key? key, |
1013 | required ShapeBorder shape, |
1014 | Clip clipBehavior = Clip.antiAlias, |
1015 | Widget? child, |
1016 | }) { |
1017 | return Builder( |
1018 | key: key, |
1019 | builder: (BuildContext context) { |
1020 | return ClipPath( |
1021 | clipper: ShapeBorderClipper( |
1022 | shape: shape, |
1023 | textDirection: Directionality.maybeOf(context), |
1024 | ), |
1025 | clipBehavior: clipBehavior, |
1026 | child: child, |
1027 | ); |
1028 | }, |
1029 | ); |
1030 | } |
1031 | |
1032 | /// If non-null, determines which clip to use. |
1033 | /// |
1034 | /// The default clip, which is used if this property is null, is the |
1035 | /// bounding box rectangle of the widget. [ClipRect] is a more |
1036 | /// efficient way of obtaining that effect. |
1037 | final CustomClipper<Path>? clipper; |
1038 | |
1039 | /// {@macro flutter.rendering.ClipRectLayer.clipBehavior} |
1040 | /// |
1041 | /// Defaults to [Clip.antiAlias]. |
1042 | final Clip clipBehavior; |
1043 | |
1044 | @override |
1045 | RenderClipPath createRenderObject(BuildContext context) { |
1046 | return RenderClipPath(clipper: clipper, clipBehavior: clipBehavior); |
1047 | } |
1048 | |
1049 | @override |
1050 | void updateRenderObject(BuildContext context, RenderClipPath renderObject) { |
1051 | renderObject |
1052 | ..clipper = clipper |
1053 | ..clipBehavior = clipBehavior; |
1054 | } |
1055 | |
1056 | @override |
1057 | void didUnmountRenderObject(RenderClipPath renderObject) { |
1058 | renderObject.clipper = null; |
1059 | } |
1060 | |
1061 | @override |
1062 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1063 | super.debugFillProperties(properties); |
1064 | properties.add(DiagnosticsProperty<CustomClipper<Path>>('clipper' , clipper, defaultValue: null)); |
1065 | } |
1066 | } |
1067 | |
1068 | /// A widget representing a physical layer that clips its children to a shape. |
1069 | /// |
1070 | /// {@youtube 560 315 https://www.youtube.com/watch?v=XgUOSS30OQk} |
1071 | /// |
1072 | /// Physical layers cast shadows based on an [elevation] which is nominally in |
1073 | /// logical pixels, coming vertically out of the rendering surface. |
1074 | /// |
1075 | /// For shapes that cannot be expressed as a rectangle with rounded corners use |
1076 | /// [PhysicalShape]. |
1077 | /// |
1078 | /// See also: |
1079 | /// |
1080 | /// * [AnimatedPhysicalModel], which animates property changes smoothly over |
1081 | /// a given duration. |
1082 | /// * [DecoratedBox], which can apply more arbitrary shadow effects. |
1083 | /// * [ClipRect], which applies a clip to its child. |
1084 | class PhysicalModel extends SingleChildRenderObjectWidget { |
1085 | /// Creates a physical model with a rounded-rectangular clip. |
1086 | /// |
1087 | /// The [color] is required; physical things have a color. |
1088 | /// |
1089 | /// The [shape], [elevation], [color], [clipBehavior], and [shadowColor] must |
1090 | /// not be null. Additionally, the [elevation] must be non-negative. |
1091 | const PhysicalModel({ |
1092 | super.key, |
1093 | this.shape = BoxShape.rectangle, |
1094 | this.clipBehavior = Clip.none, |
1095 | this.borderRadius, |
1096 | this.elevation = 0.0, |
1097 | required this.color, |
1098 | this.shadowColor = const Color(0xFF000000), |
1099 | super.child, |
1100 | }) : assert(elevation >= 0.0); |
1101 | |
1102 | /// The type of shape. |
1103 | final BoxShape shape; |
1104 | |
1105 | /// {@macro flutter.material.Material.clipBehavior} |
1106 | /// |
1107 | /// Defaults to [Clip.none]. |
1108 | final Clip clipBehavior; |
1109 | |
1110 | /// The border radius of the rounded corners. |
1111 | /// |
1112 | /// Values are clamped so that horizontal and vertical radii sums do not |
1113 | /// exceed width/height. |
1114 | /// |
1115 | /// This is ignored if the [shape] is not [BoxShape.rectangle]. |
1116 | final BorderRadius? borderRadius; |
1117 | |
1118 | /// The z-coordinate relative to the parent at which to place this physical |
1119 | /// object. |
1120 | /// |
1121 | /// The value is non-negative. |
1122 | final double elevation; |
1123 | |
1124 | /// The background color. |
1125 | final Color color; |
1126 | |
1127 | /// The shadow color. |
1128 | final Color shadowColor; |
1129 | |
1130 | @override |
1131 | RenderPhysicalModel createRenderObject(BuildContext context) { |
1132 | return RenderPhysicalModel( |
1133 | shape: shape, |
1134 | clipBehavior: clipBehavior, |
1135 | borderRadius: borderRadius, |
1136 | elevation: elevation, |
1137 | color: color, |
1138 | shadowColor: shadowColor, |
1139 | ); |
1140 | } |
1141 | |
1142 | @override |
1143 | void updateRenderObject(BuildContext context, RenderPhysicalModel renderObject) { |
1144 | renderObject |
1145 | ..shape = shape |
1146 | ..clipBehavior = clipBehavior |
1147 | ..borderRadius = borderRadius |
1148 | ..elevation = elevation |
1149 | ..color = color |
1150 | ..shadowColor = shadowColor; |
1151 | } |
1152 | |
1153 | @override |
1154 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1155 | super.debugFillProperties(properties); |
1156 | properties.add(EnumProperty<BoxShape>('shape' , shape)); |
1157 | properties.add(DiagnosticsProperty<BorderRadius>('borderRadius' , borderRadius)); |
1158 | properties.add(DoubleProperty('elevation' , elevation)); |
1159 | properties.add(ColorProperty('color' , color)); |
1160 | properties.add(ColorProperty('shadowColor' , shadowColor)); |
1161 | } |
1162 | } |
1163 | |
1164 | /// A widget representing a physical layer that clips its children to a path. |
1165 | /// |
1166 | /// Physical layers cast shadows based on an [elevation] which is nominally in |
1167 | /// logical pixels, coming vertically out of the rendering surface. |
1168 | /// |
1169 | /// [PhysicalModel] does the same but only supports shapes that can be expressed |
1170 | /// as rectangles with rounded corners. |
1171 | /// |
1172 | /// {@tool dartpad} |
1173 | /// This example shows how to use a [PhysicalShape] on a centered [SizedBox] |
1174 | /// to clip it to a rounded rectangle using a [ShapeBorderClipper] and give it |
1175 | /// an orange color along with a shadow. |
1176 | /// |
1177 | /// ** See code in examples/api/lib/widgets/basic/physical_shape.0.dart ** |
1178 | /// {@end-tool} |
1179 | /// |
1180 | /// See also: |
1181 | /// |
1182 | /// * [ShapeBorderClipper], which converts a [ShapeBorder] to a [CustomClipper], as |
1183 | /// needed by this widget. |
1184 | class PhysicalShape extends SingleChildRenderObjectWidget { |
1185 | /// Creates a physical model with an arbitrary shape clip. |
1186 | /// |
1187 | /// The [color] is required; physical things have a color. |
1188 | /// |
1189 | /// The [elevation] must be non-negative. |
1190 | const PhysicalShape({ |
1191 | super.key, |
1192 | required this.clipper, |
1193 | this.clipBehavior = Clip.none, |
1194 | this.elevation = 0.0, |
1195 | required this.color, |
1196 | this.shadowColor = const Color(0xFF000000), |
1197 | super.child, |
1198 | }) : assert(elevation >= 0.0); |
1199 | |
1200 | /// Determines which clip to use. |
1201 | /// |
1202 | /// If the path in question is expressed as a [ShapeBorder] subclass, |
1203 | /// consider using the [ShapeBorderClipper] delegate class to adapt the |
1204 | /// shape for use with this widget. |
1205 | final CustomClipper<Path> clipper; |
1206 | |
1207 | /// {@macro flutter.material.Material.clipBehavior} |
1208 | /// |
1209 | /// Defaults to [Clip.none]. |
1210 | final Clip clipBehavior; |
1211 | |
1212 | /// The z-coordinate relative to the parent at which to place this physical |
1213 | /// object. |
1214 | /// |
1215 | /// The value is non-negative. |
1216 | final double elevation; |
1217 | |
1218 | /// The background color. |
1219 | final Color color; |
1220 | |
1221 | /// When elevation is non zero the color to use for the shadow color. |
1222 | final Color shadowColor; |
1223 | |
1224 | @override |
1225 | RenderPhysicalShape createRenderObject(BuildContext context) { |
1226 | return RenderPhysicalShape( |
1227 | clipper: clipper, |
1228 | clipBehavior: clipBehavior, |
1229 | elevation: elevation, |
1230 | color: color, |
1231 | shadowColor: shadowColor, |
1232 | ); |
1233 | } |
1234 | |
1235 | @override |
1236 | void updateRenderObject(BuildContext context, RenderPhysicalShape renderObject) { |
1237 | renderObject |
1238 | ..clipper = clipper |
1239 | ..clipBehavior = clipBehavior |
1240 | ..elevation = elevation |
1241 | ..color = color |
1242 | ..shadowColor = shadowColor; |
1243 | } |
1244 | |
1245 | @override |
1246 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1247 | super.debugFillProperties(properties); |
1248 | properties.add(DiagnosticsProperty<CustomClipper<Path>>('clipper' , clipper)); |
1249 | properties.add(DoubleProperty('elevation' , elevation)); |
1250 | properties.add(ColorProperty('color' , color)); |
1251 | properties.add(ColorProperty('shadowColor' , shadowColor)); |
1252 | } |
1253 | } |
1254 | |
1255 | |
1256 | // POSITIONING AND SIZING NODES |
1257 | |
1258 | /// A widget that applies a transformation before painting its child. |
1259 | /// |
1260 | /// Unlike [RotatedBox], which applies a rotation prior to layout, this object |
1261 | /// applies its transformation just prior to painting, which means the |
1262 | /// transformation is not taken into account when calculating how much space |
1263 | /// this widget's child (and thus this widget) consumes. |
1264 | /// |
1265 | /// {@youtube 560 315 https://www.youtube.com/watch?v=9z_YNlRlWfA} |
1266 | /// |
1267 | /// {@tool snippet} |
1268 | /// |
1269 | /// This example rotates and skews an orange box containing text, keeping the |
1270 | /// top right corner pinned to its original position. |
1271 | /// |
1272 | /// ```dart |
1273 | /// ColoredBox( |
1274 | /// color: Colors.black, |
1275 | /// child: Transform( |
1276 | /// alignment: Alignment.topRight, |
1277 | /// transform: Matrix4.skewY(0.3)..rotateZ(-math.pi / 12.0), |
1278 | /// child: Container( |
1279 | /// padding: const EdgeInsets.all(8.0), |
1280 | /// color: const Color(0xFFE8581C), |
1281 | /// child: const Text('Apartment for rent!'), |
1282 | /// ), |
1283 | /// ), |
1284 | /// ) |
1285 | /// ``` |
1286 | /// {@end-tool} |
1287 | /// |
1288 | /// See also: |
1289 | /// |
1290 | /// * [RotatedBox], which rotates the child widget during layout, not just |
1291 | /// during painting. |
1292 | /// * [FractionalTranslation], which applies a translation to the child |
1293 | /// that is relative to the child's size. |
1294 | /// * [FittedBox], which sizes and positions its child widget to fit the parent |
1295 | /// according to a given [BoxFit] discipline. |
1296 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
1297 | class Transform extends SingleChildRenderObjectWidget { |
1298 | /// Creates a widget that transforms its child. |
1299 | const Transform({ |
1300 | super.key, |
1301 | required this.transform, |
1302 | this.origin, |
1303 | this.alignment, |
1304 | this.transformHitTests = true, |
1305 | this.filterQuality, |
1306 | super.child, |
1307 | }); |
1308 | |
1309 | /// Creates a widget that transforms its child using a rotation around the |
1310 | /// center. |
1311 | /// |
1312 | /// The `angle` argument gives the rotation in clockwise radians. |
1313 | /// |
1314 | /// {@tool snippet} |
1315 | /// |
1316 | /// This example rotates an orange box containing text around its center by |
1317 | /// fifteen degrees. |
1318 | /// |
1319 | /// ```dart |
1320 | /// Transform.rotate( |
1321 | /// angle: -math.pi / 12.0, |
1322 | /// child: Container( |
1323 | /// padding: const EdgeInsets.all(8.0), |
1324 | /// color: const Color(0xFFE8581C), |
1325 | /// child: const Text('Apartment for rent!'), |
1326 | /// ), |
1327 | /// ) |
1328 | /// ``` |
1329 | /// {@end-tool} |
1330 | /// |
1331 | /// See also: |
1332 | /// |
1333 | /// * [RotationTransition], which animates changes in rotation smoothly |
1334 | /// over a given duration. |
1335 | Transform.rotate({ |
1336 | super.key, |
1337 | required double angle, |
1338 | this.origin, |
1339 | this.alignment = Alignment.center, |
1340 | this.transformHitTests = true, |
1341 | this.filterQuality, |
1342 | super.child, |
1343 | }) : transform = _computeRotation(angle); |
1344 | |
1345 | /// Creates a widget that transforms its child using a translation. |
1346 | /// |
1347 | /// The `offset` argument specifies the translation. |
1348 | /// |
1349 | /// {@tool snippet} |
1350 | /// |
1351 | /// This example shifts the silver-colored child down by fifteen pixels. |
1352 | /// |
1353 | /// ```dart |
1354 | /// Transform.translate( |
1355 | /// offset: const Offset(0.0, 15.0), |
1356 | /// child: Container( |
1357 | /// padding: const EdgeInsets.all(8.0), |
1358 | /// color: const Color(0xFF7F7F7F), |
1359 | /// child: const Text('Quarter'), |
1360 | /// ), |
1361 | /// ) |
1362 | /// ``` |
1363 | /// {@end-tool} |
1364 | Transform.translate({ |
1365 | super.key, |
1366 | required Offset offset, |
1367 | this.transformHitTests = true, |
1368 | this.filterQuality, |
1369 | super.child, |
1370 | }) : transform = Matrix4.translationValues(offset.dx, offset.dy, 0.0), |
1371 | origin = null, |
1372 | alignment = null; |
1373 | |
1374 | /// Creates a widget that scales its child along the 2D plane. |
1375 | /// |
1376 | /// The `scaleX` argument provides the scalar by which to multiply the `x` |
1377 | /// axis, and the `scaleY` argument provides the scalar by which to multiply |
1378 | /// the `y` axis. Either may be omitted, in which case the scaling factor for |
1379 | /// that axis defaults to 1.0. |
1380 | /// |
1381 | /// For convenience, to scale the child uniformly, instead of providing |
1382 | /// `scaleX` and `scaleY`, the `scale` parameter may be used. |
1383 | /// |
1384 | /// At least one of `scale`, `scaleX`, and `scaleY` must be non-null. If |
1385 | /// `scale` is provided, the other two must be null; similarly, if it is not |
1386 | /// provided, one of the other two must be provided. |
1387 | /// |
1388 | /// The [alignment] controls the origin of the scale; by default, this is the |
1389 | /// center of the box. |
1390 | /// |
1391 | /// {@tool snippet} |
1392 | /// |
1393 | /// This example shrinks an orange box containing text such that each |
1394 | /// dimension is half the size it would otherwise be. |
1395 | /// |
1396 | /// ```dart |
1397 | /// Transform.scale( |
1398 | /// scale: 0.5, |
1399 | /// child: Container( |
1400 | /// padding: const EdgeInsets.all(8.0), |
1401 | /// color: const Color(0xFFE8581C), |
1402 | /// child: const Text('Bad Idea Bears'), |
1403 | /// ), |
1404 | /// ) |
1405 | /// ``` |
1406 | /// {@end-tool} |
1407 | /// |
1408 | /// See also: |
1409 | /// |
1410 | /// * [ScaleTransition], which animates changes in scale smoothly over a given |
1411 | /// duration. |
1412 | Transform.scale({ |
1413 | super.key, |
1414 | double? scale, |
1415 | double? scaleX, |
1416 | double? scaleY, |
1417 | this.origin, |
1418 | this.alignment = Alignment.center, |
1419 | this.transformHitTests = true, |
1420 | this.filterQuality, |
1421 | super.child, |
1422 | }) : assert(!(scale == null && scaleX == null && scaleY == null), "At least one of 'scale', 'scaleX' and 'scaleY' is required to be non-null" ), |
1423 | assert(scale == null || (scaleX == null && scaleY == null), "If 'scale' is non-null then 'scaleX' and 'scaleY' must be left null" ), |
1424 | transform = Matrix4.diagonal3Values(scale ?? scaleX ?? 1.0, scale ?? scaleY ?? 1.0, 1.0); |
1425 | |
1426 | /// Creates a widget that mirrors its child about the widget's center point. |
1427 | /// |
1428 | /// If `flipX` is true, the child widget will be flipped horizontally. Defaults to false. |
1429 | /// |
1430 | /// If `flipY` is true, the child widget will be flipped vertically. Defaults to false. |
1431 | /// |
1432 | /// If both are true, the child widget will be flipped both vertically and horizontally, equivalent to a 180 degree rotation. |
1433 | /// |
1434 | /// {@tool snippet} |
1435 | /// |
1436 | /// This example flips the text horizontally. |
1437 | /// |
1438 | /// ```dart |
1439 | /// Transform.flip( |
1440 | /// flipX: true, |
1441 | /// child: const Text('Horizontal Flip'), |
1442 | /// ) |
1443 | /// ``` |
1444 | /// {@end-tool} |
1445 | Transform.flip({ |
1446 | super.key, |
1447 | bool flipX = false, |
1448 | bool flipY = false, |
1449 | this.origin, |
1450 | this.transformHitTests = true, |
1451 | this.filterQuality, |
1452 | super.child, |
1453 | }) : alignment = Alignment.center, |
1454 | transform = Matrix4.diagonal3Values(flipX ? -1.0 : 1.0, flipY ? -1.0 : 1.0, 1.0); |
1455 | |
1456 | // Computes a rotation matrix for an angle in radians, attempting to keep rotations |
1457 | // at integral values for angles of 0, π/2, π, 3π/2. |
1458 | static Matrix4 _computeRotation(double radians) { |
1459 | assert(radians.isFinite, 'Cannot compute the rotation matrix for a non-finite angle: $radians' ); |
1460 | if (radians == 0.0) { |
1461 | return Matrix4.identity(); |
1462 | } |
1463 | final double sin = math.sin(radians); |
1464 | if (sin == 1.0) { |
1465 | return _createZRotation(1.0, 0.0); |
1466 | } |
1467 | if (sin == -1.0) { |
1468 | return _createZRotation(-1.0, 0.0); |
1469 | } |
1470 | final double cos = math.cos(radians); |
1471 | if (cos == -1.0) { |
1472 | return _createZRotation(0.0, -1.0); |
1473 | } |
1474 | return _createZRotation(sin, cos); |
1475 | } |
1476 | |
1477 | static Matrix4 _createZRotation(double sin, double cos) { |
1478 | final Matrix4 result = Matrix4.zero(); |
1479 | result.storage[0] = cos; |
1480 | result.storage[1] = sin; |
1481 | result.storage[4] = -sin; |
1482 | result.storage[5] = cos; |
1483 | result.storage[10] = 1.0; |
1484 | result.storage[15] = 1.0; |
1485 | return result; |
1486 | } |
1487 | |
1488 | /// The matrix to transform the child by during painting. |
1489 | final Matrix4 transform; |
1490 | |
1491 | /// The origin of the coordinate system (relative to the upper left corner of |
1492 | /// this render object) in which to apply the matrix. |
1493 | /// |
1494 | /// Setting an origin is equivalent to conjugating the transform matrix by a |
1495 | /// translation. This property is provided just for convenience. |
1496 | final Offset? origin; |
1497 | |
1498 | /// The alignment of the origin, relative to the size of the box. |
1499 | /// |
1500 | /// This is equivalent to setting an origin based on the size of the box. |
1501 | /// If it is specified at the same time as the [origin], both are applied. |
1502 | /// |
1503 | /// An [AlignmentDirectional.centerStart] value is the same as an [Alignment] |
1504 | /// whose [Alignment.x] value is `-1.0` if [Directionality.of] returns |
1505 | /// [TextDirection.ltr], and `1.0` if [Directionality.of] returns |
1506 | /// [TextDirection.rtl]. Similarly [AlignmentDirectional.centerEnd] is the |
1507 | /// same as an [Alignment] whose [Alignment.x] value is `1.0` if |
1508 | /// [Directionality.of] returns [TextDirection.ltr], and `-1.0` if |
1509 | /// [Directionality.of] returns [TextDirection.rtl]. |
1510 | final AlignmentGeometry? alignment; |
1511 | |
1512 | /// Whether to apply the transformation when performing hit tests. |
1513 | final bool transformHitTests; |
1514 | |
1515 | /// The filter quality with which to apply the transform as a bitmap operation. |
1516 | /// |
1517 | /// {@template flutter.widgets.Transform.optional.FilterQuality} |
1518 | /// The transform will be applied by re-rendering the child if [filterQuality] is null, |
1519 | /// otherwise it controls the quality of an [ImageFilter.matrix] applied to a bitmap |
1520 | /// rendering of the child. |
1521 | /// {@endtemplate} |
1522 | final FilterQuality? filterQuality; |
1523 | |
1524 | @override |
1525 | RenderTransform createRenderObject(BuildContext context) { |
1526 | return RenderTransform( |
1527 | transform: transform, |
1528 | origin: origin, |
1529 | alignment: alignment, |
1530 | textDirection: Directionality.maybeOf(context), |
1531 | transformHitTests: transformHitTests, |
1532 | filterQuality: filterQuality, |
1533 | ); |
1534 | } |
1535 | |
1536 | @override |
1537 | void updateRenderObject(BuildContext context, RenderTransform renderObject) { |
1538 | renderObject |
1539 | ..transform = transform |
1540 | ..origin = origin |
1541 | ..alignment = alignment |
1542 | ..textDirection = Directionality.maybeOf(context) |
1543 | ..transformHitTests = transformHitTests |
1544 | ..filterQuality = filterQuality; |
1545 | } |
1546 | } |
1547 | |
1548 | /// A widget that can be targeted by a [CompositedTransformFollower]. |
1549 | /// |
1550 | /// When this widget is composited during the compositing phase (which comes |
1551 | /// after the paint phase, as described in [WidgetsBinding.drawFrame]), it |
1552 | /// updates the [link] object so that any [CompositedTransformFollower] widgets |
1553 | /// that are subsequently composited in the same frame and were given the same |
1554 | /// [LayerLink] can position themselves at the same screen location. |
1555 | /// |
1556 | /// A single [CompositedTransformTarget] can be followed by multiple |
1557 | /// [CompositedTransformFollower] widgets. |
1558 | /// |
1559 | /// The [CompositedTransformTarget] must come earlier in the paint order than |
1560 | /// any linked [CompositedTransformFollower]s. |
1561 | /// |
1562 | /// See also: |
1563 | /// |
1564 | /// * [CompositedTransformFollower], the widget that can target this one. |
1565 | /// * [LeaderLayer], the layer that implements this widget's logic. |
1566 | class CompositedTransformTarget extends SingleChildRenderObjectWidget { |
1567 | /// Creates a composited transform target widget. |
1568 | /// |
1569 | /// The [link] property must not be currently used by any other |
1570 | /// [CompositedTransformTarget] object that is in the tree. |
1571 | const CompositedTransformTarget({ |
1572 | super.key, |
1573 | required this.link, |
1574 | super.child, |
1575 | }); |
1576 | |
1577 | /// The link object that connects this [CompositedTransformTarget] with one or |
1578 | /// more [CompositedTransformFollower]s. |
1579 | /// |
1580 | /// The link must not be associated with another [CompositedTransformTarget] |
1581 | /// that is also being painted. |
1582 | final LayerLink link; |
1583 | |
1584 | @override |
1585 | RenderLeaderLayer createRenderObject(BuildContext context) { |
1586 | return RenderLeaderLayer( |
1587 | link: link, |
1588 | ); |
1589 | } |
1590 | |
1591 | @override |
1592 | void updateRenderObject(BuildContext context, RenderLeaderLayer renderObject) { |
1593 | renderObject.link = link; |
1594 | } |
1595 | } |
1596 | |
1597 | /// A widget that follows a [CompositedTransformTarget]. |
1598 | /// |
1599 | /// When this widget is composited during the compositing phase (which comes |
1600 | /// after the paint phase, as described in [WidgetsBinding.drawFrame]), it |
1601 | /// applies a transformation that brings [targetAnchor] of the linked |
1602 | /// [CompositedTransformTarget] and [followerAnchor] of this widget together. |
1603 | /// The two anchor points will have the same global coordinates, unless [offset] |
1604 | /// is not [Offset.zero], in which case [followerAnchor] will be offset by |
1605 | /// [offset] in the linked [CompositedTransformTarget]'s coordinate space. |
1606 | /// |
1607 | /// The [LayerLink] object used as the [link] must be the same object as that |
1608 | /// provided to the matching [CompositedTransformTarget]. |
1609 | /// |
1610 | /// The [CompositedTransformTarget] must come earlier in the paint order than |
1611 | /// this [CompositedTransformFollower]. |
1612 | /// |
1613 | /// Hit testing on descendants of this widget will only work if the target |
1614 | /// position is within the box that this widget's parent considers to be |
1615 | /// hittable. If the parent covers the screen, this is trivially achievable, so |
1616 | /// this widget is usually used as the root of an [OverlayEntry] in an app-wide |
1617 | /// [Overlay] (e.g. as created by the [MaterialApp] widget's [Navigator]). |
1618 | /// |
1619 | /// See also: |
1620 | /// |
1621 | /// * [CompositedTransformTarget], the widget that this widget can target. |
1622 | /// * [FollowerLayer], the layer that implements this widget's logic. |
1623 | /// * [Transform], which applies an arbitrary transform to a child. |
1624 | class CompositedTransformFollower extends SingleChildRenderObjectWidget { |
1625 | /// Creates a composited transform target widget. |
1626 | /// |
1627 | /// If the [link] property was also provided to a [CompositedTransformTarget], |
1628 | /// that widget must come earlier in the paint order. |
1629 | /// |
1630 | /// The [showWhenUnlinked] and [offset] properties must also not be null. |
1631 | const CompositedTransformFollower({ |
1632 | super.key, |
1633 | required this.link, |
1634 | this.showWhenUnlinked = true, |
1635 | this.offset = Offset.zero, |
1636 | this.targetAnchor = Alignment.topLeft, |
1637 | this.followerAnchor = Alignment.topLeft, |
1638 | super.child, |
1639 | }); |
1640 | |
1641 | /// The link object that connects this [CompositedTransformFollower] with a |
1642 | /// [CompositedTransformTarget]. |
1643 | final LayerLink link; |
1644 | |
1645 | /// Whether to show the widget's contents when there is no corresponding |
1646 | /// [CompositedTransformTarget] with the same [link]. |
1647 | /// |
1648 | /// When the widget is linked, the child is positioned such that it has the |
1649 | /// same global position as the linked [CompositedTransformTarget]. |
1650 | /// |
1651 | /// When the widget is not linked, then: if [showWhenUnlinked] is true, the |
1652 | /// child is visible and not repositioned; if it is false, then child is |
1653 | /// hidden. |
1654 | final bool showWhenUnlinked; |
1655 | |
1656 | /// The anchor point on the linked [CompositedTransformTarget] that |
1657 | /// [followerAnchor] will line up with. |
1658 | /// |
1659 | /// {@template flutter.widgets.CompositedTransformFollower.targetAnchor} |
1660 | /// For example, when [targetAnchor] and [followerAnchor] are both |
1661 | /// [Alignment.topLeft], this widget will be top left aligned with the linked |
1662 | /// [CompositedTransformTarget]. When [targetAnchor] is |
1663 | /// [Alignment.bottomLeft] and [followerAnchor] is [Alignment.topLeft], this |
1664 | /// widget will be left aligned with the linked [CompositedTransformTarget], |
1665 | /// and its top edge will line up with the [CompositedTransformTarget]'s |
1666 | /// bottom edge. |
1667 | /// {@endtemplate} |
1668 | /// |
1669 | /// Defaults to [Alignment.topLeft]. |
1670 | final Alignment targetAnchor; |
1671 | |
1672 | /// The anchor point on this widget that will line up with [targetAnchor] on |
1673 | /// the linked [CompositedTransformTarget]. |
1674 | /// |
1675 | /// {@macro flutter.widgets.CompositedTransformFollower.targetAnchor} |
1676 | /// |
1677 | /// Defaults to [Alignment.topLeft]. |
1678 | final Alignment followerAnchor; |
1679 | |
1680 | /// The additional offset to apply to the [targetAnchor] of the linked |
1681 | /// [CompositedTransformTarget] to obtain this widget's [followerAnchor] |
1682 | /// position. |
1683 | final Offset offset; |
1684 | |
1685 | @override |
1686 | RenderFollowerLayer createRenderObject(BuildContext context) { |
1687 | return RenderFollowerLayer( |
1688 | link: link, |
1689 | showWhenUnlinked: showWhenUnlinked, |
1690 | offset: offset, |
1691 | leaderAnchor: targetAnchor, |
1692 | followerAnchor: followerAnchor, |
1693 | ); |
1694 | } |
1695 | |
1696 | @override |
1697 | void updateRenderObject(BuildContext context, RenderFollowerLayer renderObject) { |
1698 | renderObject |
1699 | ..link = link |
1700 | ..showWhenUnlinked = showWhenUnlinked |
1701 | ..offset = offset |
1702 | ..leaderAnchor = targetAnchor |
1703 | ..followerAnchor = followerAnchor; |
1704 | } |
1705 | } |
1706 | |
1707 | /// Scales and positions its child within itself according to [fit]. |
1708 | /// |
1709 | /// {@youtube 560 315 https://www.youtube.com/watch?v=T4Uehk3_wlY} |
1710 | /// |
1711 | /// {@tool dartpad} |
1712 | /// In this example, the [Placeholder] is stretched to fill the entire |
1713 | /// [Container]. Try changing the fit types to see the effect on the layout of |
1714 | /// the [Placeholder]. |
1715 | /// |
1716 | /// ** See code in examples/api/lib/widgets/basic/fitted_box.0.dart ** |
1717 | /// {@end-tool} |
1718 | /// |
1719 | /// See also: |
1720 | /// |
1721 | /// * [Transform], which applies an arbitrary transform to its child widget at |
1722 | /// paint time. |
1723 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
1724 | class FittedBox extends SingleChildRenderObjectWidget { |
1725 | /// Creates a widget that scales and positions its child within itself according to [fit]. |
1726 | const FittedBox({ |
1727 | super.key, |
1728 | this.fit = BoxFit.contain, |
1729 | this.alignment = Alignment.center, |
1730 | this.clipBehavior = Clip.none, |
1731 | super.child, |
1732 | }); |
1733 | |
1734 | /// How to inscribe the child into the space allocated during layout. |
1735 | final BoxFit fit; |
1736 | |
1737 | /// How to align the child within its parent's bounds. |
1738 | /// |
1739 | /// An alignment of (-1.0, -1.0) aligns the child to the top-left corner of its |
1740 | /// parent's bounds. An alignment of (1.0, 0.0) aligns the child to the middle |
1741 | /// of the right edge of its parent's bounds. |
1742 | /// |
1743 | /// Defaults to [Alignment.center]. |
1744 | /// |
1745 | /// See also: |
1746 | /// |
1747 | /// * [Alignment], a class with convenient constants typically used to |
1748 | /// specify an [AlignmentGeometry]. |
1749 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments |
1750 | /// relative to text direction. |
1751 | final AlignmentGeometry alignment; |
1752 | |
1753 | /// {@macro flutter.material.Material.clipBehavior} |
1754 | /// |
1755 | /// Defaults to [Clip.none]. |
1756 | final Clip clipBehavior; |
1757 | |
1758 | @override |
1759 | RenderFittedBox createRenderObject(BuildContext context) { |
1760 | return RenderFittedBox( |
1761 | fit: fit, |
1762 | alignment: alignment, |
1763 | textDirection: Directionality.maybeOf(context), |
1764 | clipBehavior: clipBehavior, |
1765 | ); |
1766 | } |
1767 | |
1768 | @override |
1769 | void updateRenderObject(BuildContext context, RenderFittedBox renderObject) { |
1770 | renderObject |
1771 | ..fit = fit |
1772 | ..alignment = alignment |
1773 | ..textDirection = Directionality.maybeOf(context) |
1774 | ..clipBehavior = clipBehavior; |
1775 | } |
1776 | |
1777 | @override |
1778 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1779 | super.debugFillProperties(properties); |
1780 | properties.add(EnumProperty<BoxFit>('fit' , fit)); |
1781 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
1782 | } |
1783 | } |
1784 | |
1785 | /// Applies a translation transformation before painting its child. |
1786 | /// |
1787 | /// The translation is expressed as a [Offset] scaled to the child's size. For |
1788 | /// example, an [Offset] with a `dx` of 0.25 will result in a horizontal |
1789 | /// translation of one quarter the width of the child. |
1790 | /// |
1791 | /// Hit tests will only be detected inside the bounds of the |
1792 | /// [FractionalTranslation], even if the contents are offset such that |
1793 | /// they overflow. |
1794 | /// |
1795 | /// See also: |
1796 | /// |
1797 | /// * [Transform], which applies an arbitrary transform to its child widget at |
1798 | /// paint time. |
1799 | /// * [Transform.translate], which applies an absolute offset translation |
1800 | /// transformation instead of an offset scaled to the child. |
1801 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
1802 | class FractionalTranslation extends SingleChildRenderObjectWidget { |
1803 | /// Creates a widget that translates its child's painting. |
1804 | const FractionalTranslation({ |
1805 | super.key, |
1806 | required this.translation, |
1807 | this.transformHitTests = true, |
1808 | super.child, |
1809 | }); |
1810 | |
1811 | /// The translation to apply to the child, scaled to the child's size. |
1812 | /// |
1813 | /// For example, an [Offset] with a `dx` of 0.25 will result in a horizontal |
1814 | /// translation of one quarter the width of the child. |
1815 | final Offset translation; |
1816 | |
1817 | /// Whether to apply the translation when performing hit tests. |
1818 | final bool transformHitTests; |
1819 | |
1820 | @override |
1821 | RenderFractionalTranslation createRenderObject(BuildContext context) { |
1822 | return RenderFractionalTranslation( |
1823 | translation: translation, |
1824 | transformHitTests: transformHitTests, |
1825 | ); |
1826 | } |
1827 | |
1828 | @override |
1829 | void updateRenderObject(BuildContext context, RenderFractionalTranslation renderObject) { |
1830 | renderObject |
1831 | ..translation = translation |
1832 | ..transformHitTests = transformHitTests; |
1833 | } |
1834 | } |
1835 | |
1836 | /// A widget that rotates its child by a integral number of quarter turns. |
1837 | /// |
1838 | /// Unlike [Transform], which applies a transform just prior to painting, |
1839 | /// this object applies its rotation prior to layout, which means the entire |
1840 | /// rotated box consumes only as much space as required by the rotated child. |
1841 | /// |
1842 | /// {@youtube 560 315 https://www.youtube.com/watch?v=BFE6_UglLfQ} |
1843 | /// |
1844 | /// {@tool snippet} |
1845 | /// |
1846 | /// This snippet rotates the child (some [Text]) so that it renders from bottom |
1847 | /// to top, like an axis label on a graph: |
1848 | /// |
1849 | /// ```dart |
1850 | /// const RotatedBox( |
1851 | /// quarterTurns: 3, |
1852 | /// child: Text('Hello World!'), |
1853 | /// ) |
1854 | /// ``` |
1855 | /// {@end-tool} |
1856 | /// |
1857 | /// See also: |
1858 | /// |
1859 | /// * [Transform], which is a paint effect that allows you to apply an |
1860 | /// arbitrary transform to a child. |
1861 | /// * [Transform.rotate], which applies a rotation paint effect. |
1862 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
1863 | class RotatedBox extends SingleChildRenderObjectWidget { |
1864 | /// A widget that rotates its child. |
1865 | const RotatedBox({ |
1866 | super.key, |
1867 | required this.quarterTurns, |
1868 | super.child, |
1869 | }); |
1870 | |
1871 | /// The number of clockwise quarter turns the child should be rotated. |
1872 | final int quarterTurns; |
1873 | |
1874 | @override |
1875 | RenderRotatedBox createRenderObject(BuildContext context) => RenderRotatedBox(quarterTurns: quarterTurns); |
1876 | |
1877 | @override |
1878 | void updateRenderObject(BuildContext context, RenderRotatedBox renderObject) { |
1879 | renderObject.quarterTurns = quarterTurns; |
1880 | } |
1881 | } |
1882 | |
1883 | /// A widget that insets its child by the given padding. |
1884 | /// |
1885 | /// {@youtube 560 315 https://www.youtube.com/watch?v=oD5RtLhhubg} |
1886 | /// |
1887 | /// When passing layout constraints to its child, padding shrinks the |
1888 | /// constraints by the given padding, causing the child to layout at a smaller |
1889 | /// size. Padding then sizes itself to its child's size, inflated by the |
1890 | /// padding, effectively creating empty space around the child. |
1891 | /// |
1892 | /// {@tool snippet} |
1893 | /// |
1894 | /// This snippet creates "Hello World!" [Text] inside a [Card] that is indented |
1895 | /// by sixteen pixels in each direction. |
1896 | /// |
1897 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/padding.png) |
1898 | /// |
1899 | /// ```dart |
1900 | /// const Card( |
1901 | /// child: Padding( |
1902 | /// padding: EdgeInsets.all(16.0), |
1903 | /// child: Text('Hello World!'), |
1904 | /// ), |
1905 | /// ) |
1906 | /// ``` |
1907 | /// {@end-tool} |
1908 | /// |
1909 | /// ## Design discussion |
1910 | /// |
1911 | /// ### Why use a [Padding] widget rather than a [Container] with a [Container.padding] property? |
1912 | /// |
1913 | /// There isn't really any difference between the two. If you supply a |
1914 | /// [Container.padding] argument, [Container] builds a [Padding] widget |
1915 | /// for you. |
1916 | /// |
1917 | /// [Container] doesn't implement its properties directly. Instead, [Container] |
1918 | /// combines a number of simpler widgets together into a convenient package. For |
1919 | /// example, the [Container.padding] property causes the container to build a |
1920 | /// [Padding] widget and the [Container.decoration] property causes the |
1921 | /// container to build a [DecoratedBox] widget. If you find [Container] |
1922 | /// convenient, feel free to use it. If not, feel free to build these simpler |
1923 | /// widgets in whatever combination meets your needs. |
1924 | /// |
1925 | /// In fact, the majority of widgets in Flutter are combinations of other |
1926 | /// simpler widgets. Composition, rather than inheritance, is the primary |
1927 | /// mechanism for building up widgets. |
1928 | /// |
1929 | /// See also: |
1930 | /// |
1931 | /// * [EdgeInsets], the class that is used to describe the padding dimensions. |
1932 | /// * [AnimatedPadding], which animates changes in [padding] over a given |
1933 | /// duration. |
1934 | /// * [SliverPadding], the sliver equivalent of this widget. |
1935 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
1936 | class Padding extends SingleChildRenderObjectWidget { |
1937 | /// Creates a widget that insets its child. |
1938 | const Padding({ |
1939 | super.key, |
1940 | required this.padding, |
1941 | super.child, |
1942 | }); |
1943 | |
1944 | /// The amount of space by which to inset the child. |
1945 | final EdgeInsetsGeometry padding; |
1946 | |
1947 | @override |
1948 | RenderPadding createRenderObject(BuildContext context) { |
1949 | return RenderPadding( |
1950 | padding: padding, |
1951 | textDirection: Directionality.maybeOf(context), |
1952 | ); |
1953 | } |
1954 | |
1955 | @override |
1956 | void updateRenderObject(BuildContext context, RenderPadding renderObject) { |
1957 | renderObject |
1958 | ..padding = padding |
1959 | ..textDirection = Directionality.maybeOf(context); |
1960 | } |
1961 | |
1962 | @override |
1963 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1964 | super.debugFillProperties(properties); |
1965 | properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding' , padding)); |
1966 | } |
1967 | } |
1968 | |
1969 | /// A widget that aligns its child within itself and optionally sizes itself |
1970 | /// based on the child's size. |
1971 | /// |
1972 | /// For example, to align a box at the bottom right, you would pass this box a |
1973 | /// tight constraint that is bigger than the child's natural size, |
1974 | /// with an alignment of [Alignment.bottomRight]. |
1975 | /// |
1976 | /// {@youtube 560 315 https://www.youtube.com/watch?v=g2E7yl3MwMk} |
1977 | /// |
1978 | /// This widget will be as big as possible if its dimensions are constrained and |
1979 | /// [widthFactor] and [heightFactor] are null. If a dimension is unconstrained |
1980 | /// and the corresponding size factor is null then the widget will match its |
1981 | /// child's size in that dimension. If a size factor is non-null then the |
1982 | /// corresponding dimension of this widget will be the product of the child's |
1983 | /// dimension and the size factor. For example if widthFactor is 2.0 then |
1984 | /// the width of this widget will always be twice its child's width. |
1985 | /// |
1986 | /// {@tool snippet} |
1987 | /// The [Align] widget in this example uses one of the defined constants from |
1988 | /// [Alignment], [Alignment.topRight]. This places the [FlutterLogo] in the top |
1989 | /// right corner of the parent blue [Container]. |
1990 | /// |
1991 | /// ![A blue square container with the Flutter logo in the top right corner.](https://flutter.github.io/assets-for-api-docs/assets/widgets/align_constant.png) |
1992 | /// |
1993 | /// ```dart |
1994 | /// Center( |
1995 | /// child: Container( |
1996 | /// height: 120.0, |
1997 | /// width: 120.0, |
1998 | /// color: Colors.blue[50], |
1999 | /// child: const Align( |
2000 | /// alignment: Alignment.topRight, |
2001 | /// child: FlutterLogo( |
2002 | /// size: 60, |
2003 | /// ), |
2004 | /// ), |
2005 | /// ), |
2006 | /// ) |
2007 | /// ``` |
2008 | /// {@end-tool} |
2009 | /// |
2010 | /// ## How it works |
2011 | /// |
2012 | /// The [alignment] property describes a point in the `child`'s coordinate system |
2013 | /// and a different point in the coordinate system of this widget. The [Align] |
2014 | /// widget positions the `child` such that both points are lined up on top of |
2015 | /// each other. |
2016 | /// |
2017 | /// {@tool snippet} |
2018 | /// The [Alignment] used in the following example defines two points: |
2019 | /// |
2020 | /// * (0.2 * width of [FlutterLogo]/2 + width of [FlutterLogo]/2, 0.6 * height |
2021 | /// of [FlutterLogo]/2 + height of [FlutterLogo]/2) = (36.0, 48.0) in the |
2022 | /// coordinate system of the [FlutterLogo]. |
2023 | /// * (0.2 * width of [Align]/2 + width of [Align]/2, 0.6 * height |
2024 | /// of [Align]/2 + height of [Align]/2) = (72.0, 96.0) in the |
2025 | /// coordinate system of the [Align] widget (blue area). |
2026 | /// |
2027 | /// The [Align] widget positions the [FlutterLogo] such that the two points are on |
2028 | /// top of each other. In this example, the top left of the [FlutterLogo] will |
2029 | /// be placed at (72.0, 96.0) - (36.0, 48.0) = (36.0, 48.0) from the top left of |
2030 | /// the [Align] widget. |
2031 | /// |
2032 | /// ![A blue square container with the Flutter logo positioned according to the |
2033 | /// Alignment specified above. A point is marked at the center of the container |
2034 | /// for the origin of the Alignment coordinate system.](https://flutter.github.io/assets-for-api-docs/assets/widgets/align_alignment.png) |
2035 | /// |
2036 | /// ```dart |
2037 | /// Center( |
2038 | /// child: Container( |
2039 | /// height: 120.0, |
2040 | /// width: 120.0, |
2041 | /// color: Colors.blue[50], |
2042 | /// child: const Align( |
2043 | /// alignment: Alignment(0.2, 0.6), |
2044 | /// child: FlutterLogo( |
2045 | /// size: 60, |
2046 | /// ), |
2047 | /// ), |
2048 | /// ), |
2049 | /// ) |
2050 | /// ``` |
2051 | /// {@end-tool} |
2052 | /// |
2053 | /// {@tool snippet} |
2054 | /// The [FractionalOffset] used in the following example defines two points: |
2055 | /// |
2056 | /// * (0.2 * width of [FlutterLogo], 0.6 * height of [FlutterLogo]) = (12.0, 36.0) |
2057 | /// in the coordinate system of the [FlutterLogo]. |
2058 | /// * (0.2 * width of [Align], 0.6 * height of [Align]) = (24.0, 72.0) in the |
2059 | /// coordinate system of the [Align] widget (blue area). |
2060 | /// |
2061 | /// The [Align] widget positions the [FlutterLogo] such that the two points are on |
2062 | /// top of each other. In this example, the top left of the [FlutterLogo] will |
2063 | /// be placed at (24.0, 72.0) - (12.0, 36.0) = (12.0, 36.0) from the top left of |
2064 | /// the [Align] widget. |
2065 | /// |
2066 | /// The [FractionalOffset] class uses a coordinate system with an origin in the top-left |
2067 | /// corner of the [Container] in difference to the center-oriented system used in |
2068 | /// the example above with [Alignment]. |
2069 | /// |
2070 | /// ![A blue square container with the Flutter logo positioned according to the |
2071 | /// FractionalOffset specified above. A point is marked at the top left corner |
2072 | /// of the container for the origin of the FractionalOffset coordinate system.](https://flutter.github.io/assets-for-api-docs/assets/widgets/align_fractional_offset.png) |
2073 | /// |
2074 | /// ```dart |
2075 | /// Center( |
2076 | /// child: Container( |
2077 | /// height: 120.0, |
2078 | /// width: 120.0, |
2079 | /// color: Colors.blue[50], |
2080 | /// child: const Align( |
2081 | /// alignment: FractionalOffset(0.2, 0.6), |
2082 | /// child: FlutterLogo( |
2083 | /// size: 60, |
2084 | /// ), |
2085 | /// ), |
2086 | /// ), |
2087 | /// ) |
2088 | /// ``` |
2089 | /// {@end-tool} |
2090 | /// |
2091 | /// See also: |
2092 | /// |
2093 | /// * [AnimatedAlign], which animates changes in [alignment] smoothly over a |
2094 | /// given duration. |
2095 | /// * [CustomSingleChildLayout], which uses a delegate to control the layout of |
2096 | /// a single child. |
2097 | /// * [Center], which is the same as [Align] but with the [alignment] always |
2098 | /// set to [Alignment.center]. |
2099 | /// * [FractionallySizedBox], which sizes its child based on a fraction of its |
2100 | /// own size and positions the child according to an [Alignment] value. |
2101 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2102 | class Align extends SingleChildRenderObjectWidget { |
2103 | /// Creates an alignment widget. |
2104 | /// |
2105 | /// The alignment defaults to [Alignment.center]. |
2106 | const Align({ |
2107 | super.key, |
2108 | this.alignment = Alignment.center, |
2109 | this.widthFactor, |
2110 | this.heightFactor, |
2111 | super.child, |
2112 | }) : assert(widthFactor == null || widthFactor >= 0.0), |
2113 | assert(heightFactor == null || heightFactor >= 0.0); |
2114 | |
2115 | /// How to align the child. |
2116 | /// |
2117 | /// The x and y values of the [Alignment] control the horizontal and vertical |
2118 | /// alignment, respectively. An x value of -1.0 means that the left edge of |
2119 | /// the child is aligned with the left edge of the parent whereas an x value |
2120 | /// of 1.0 means that the right edge of the child is aligned with the right |
2121 | /// edge of the parent. Other values interpolate (and extrapolate) linearly. |
2122 | /// For example, a value of 0.0 means that the center of the child is aligned |
2123 | /// with the center of the parent. |
2124 | /// |
2125 | /// See also: |
2126 | /// |
2127 | /// * [Alignment], which has more details and some convenience constants for |
2128 | /// common positions. |
2129 | /// * [AlignmentDirectional], which has a horizontal coordinate orientation |
2130 | /// that depends on the [TextDirection]. |
2131 | final AlignmentGeometry alignment; |
2132 | |
2133 | /// If non-null, sets its width to the child's width multiplied by this factor. |
2134 | /// |
2135 | /// Can be both greater and less than 1.0 but must be non-negative. |
2136 | final double? widthFactor; |
2137 | |
2138 | /// If non-null, sets its height to the child's height multiplied by this factor. |
2139 | /// |
2140 | /// Can be both greater and less than 1.0 but must be non-negative. |
2141 | final double? heightFactor; |
2142 | |
2143 | @override |
2144 | RenderPositionedBox createRenderObject(BuildContext context) { |
2145 | return RenderPositionedBox( |
2146 | alignment: alignment, |
2147 | widthFactor: widthFactor, |
2148 | heightFactor: heightFactor, |
2149 | textDirection: Directionality.maybeOf(context), |
2150 | ); |
2151 | } |
2152 | |
2153 | @override |
2154 | void updateRenderObject(BuildContext context, RenderPositionedBox renderObject) { |
2155 | renderObject |
2156 | ..alignment = alignment |
2157 | ..widthFactor = widthFactor |
2158 | ..heightFactor = heightFactor |
2159 | ..textDirection = Directionality.maybeOf(context); |
2160 | } |
2161 | |
2162 | @override |
2163 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
2164 | super.debugFillProperties(properties); |
2165 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
2166 | properties.add(DoubleProperty('widthFactor' , widthFactor, defaultValue: null)); |
2167 | properties.add(DoubleProperty('heightFactor' , heightFactor, defaultValue: null)); |
2168 | } |
2169 | } |
2170 | |
2171 | /// A widget that centers its child within itself. |
2172 | /// |
2173 | /// This widget will be as big as possible if its dimensions are constrained and |
2174 | /// [widthFactor] and [heightFactor] are null. If a dimension is unconstrained |
2175 | /// and the corresponding size factor is null then the widget will match its |
2176 | /// child's size in that dimension. If a size factor is non-null then the |
2177 | /// corresponding dimension of this widget will be the product of the child's |
2178 | /// dimension and the size factor. For example if widthFactor is 2.0 then |
2179 | /// the width of this widget will always be twice its child's width. |
2180 | /// |
2181 | /// See also: |
2182 | /// |
2183 | /// * [Align], which lets you arbitrarily position a child within itself, |
2184 | /// rather than just centering it. |
2185 | /// * [Row], a widget that displays its children in a horizontal array. |
2186 | /// * [Column], a widget that displays its children in a vertical array. |
2187 | /// * [Container], a convenience widget that combines common painting, |
2188 | /// positioning, and sizing widgets. |
2189 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2190 | class Center extends Align { |
2191 | /// Creates a widget that centers its child. |
2192 | const Center({ super.key, super.widthFactor, super.heightFactor, super.child }); |
2193 | } |
2194 | |
2195 | /// A widget that defers the layout of its single child to a delegate. |
2196 | /// |
2197 | /// The delegate can determine the layout constraints for the child and can |
2198 | /// decide where to position the child. The delegate can also determine the size |
2199 | /// of the parent, but the size of the parent cannot depend on the size of the |
2200 | /// child. |
2201 | /// |
2202 | /// See also: |
2203 | /// |
2204 | /// * [SingleChildLayoutDelegate], which controls the layout of the child. |
2205 | /// * [Align], which sizes itself based on its child's size and positions |
2206 | /// the child according to an [Alignment] value. |
2207 | /// * [FractionallySizedBox], which sizes its child based on a fraction of its own |
2208 | /// size and positions the child according to an [Alignment] value. |
2209 | /// * [CustomMultiChildLayout], which uses a delegate to position multiple |
2210 | /// children. |
2211 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2212 | class CustomSingleChildLayout extends SingleChildRenderObjectWidget { |
2213 | /// Creates a custom single child layout. |
2214 | const CustomSingleChildLayout({ |
2215 | super.key, |
2216 | required this.delegate, |
2217 | super.child, |
2218 | }); |
2219 | |
2220 | /// The delegate that controls the layout of the child. |
2221 | final SingleChildLayoutDelegate delegate; |
2222 | |
2223 | @override |
2224 | RenderCustomSingleChildLayoutBox createRenderObject(BuildContext context) { |
2225 | return RenderCustomSingleChildLayoutBox(delegate: delegate); |
2226 | } |
2227 | |
2228 | @override |
2229 | void updateRenderObject(BuildContext context, RenderCustomSingleChildLayoutBox renderObject) { |
2230 | renderObject.delegate = delegate; |
2231 | } |
2232 | } |
2233 | |
2234 | /// Metadata for identifying children in a [CustomMultiChildLayout]. |
2235 | /// |
2236 | /// The [MultiChildLayoutDelegate.hasChild], |
2237 | /// [MultiChildLayoutDelegate.layoutChild], and |
2238 | /// [MultiChildLayoutDelegate.positionChild] methods use these identifiers. |
2239 | class LayoutId extends ParentDataWidget<MultiChildLayoutParentData> { |
2240 | /// Marks a child with a layout identifier. |
2241 | LayoutId({ |
2242 | Key? key, |
2243 | required this.id, |
2244 | required super.child, |
2245 | }) : super(key: key ?? ValueKey<Object>(id)); |
2246 | |
2247 | /// An object representing the identity of this child. |
2248 | /// |
2249 | /// The [id] needs to be unique among the children that the |
2250 | /// [CustomMultiChildLayout] manages. |
2251 | final Object id; |
2252 | |
2253 | @override |
2254 | void applyParentData(RenderObject renderObject) { |
2255 | assert(renderObject.parentData is MultiChildLayoutParentData); |
2256 | final MultiChildLayoutParentData parentData = renderObject.parentData! as MultiChildLayoutParentData; |
2257 | if (parentData.id != id) { |
2258 | parentData.id = id; |
2259 | final RenderObject? targetParent = renderObject.parent; |
2260 | if (targetParent is RenderObject) { |
2261 | targetParent.markNeedsLayout(); |
2262 | } |
2263 | } |
2264 | } |
2265 | |
2266 | @override |
2267 | Type get debugTypicalAncestorWidgetClass => CustomMultiChildLayout; |
2268 | |
2269 | @override |
2270 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
2271 | super.debugFillProperties(properties); |
2272 | properties.add(DiagnosticsProperty<Object>('id' , id)); |
2273 | } |
2274 | } |
2275 | |
2276 | /// A widget that uses a delegate to size and position multiple children. |
2277 | /// |
2278 | /// The delegate can determine the layout constraints for each child and can |
2279 | /// decide where to position each child. The delegate can also determine the |
2280 | /// size of the parent, but the size of the parent cannot depend on the sizes of |
2281 | /// the children. |
2282 | /// |
2283 | /// [CustomMultiChildLayout] is appropriate when there are complex relationships |
2284 | /// between the size and positioning of multiple widgets. To control the |
2285 | /// layout of a single child, [CustomSingleChildLayout] is more appropriate. For |
2286 | /// simple cases, such as aligning a widget to one or another edge, the [Stack] |
2287 | /// widget is more appropriate. |
2288 | /// |
2289 | /// Each child must be wrapped in a [LayoutId] widget to identify the widget for |
2290 | /// the delegate. |
2291 | /// |
2292 | /// {@tool dartpad} |
2293 | /// This example shows a [CustomMultiChildLayout] widget being used to lay out |
2294 | /// colored blocks from start to finish in a cascade that has some overlap. |
2295 | /// |
2296 | /// It responds to changes in [Directionality] by re-laying out its children. |
2297 | /// |
2298 | /// ** See code in examples/api/lib/widgets/basic/custom_multi_child_layout.0.dart ** |
2299 | /// {@end-tool} |
2300 | /// |
2301 | /// See also: |
2302 | /// |
2303 | /// * [MultiChildLayoutDelegate], for details about how to control the layout of |
2304 | /// the children. |
2305 | /// * [CustomSingleChildLayout], which uses a delegate to control the layout of |
2306 | /// a single child. |
2307 | /// * [Stack], which arranges children relative to the edges of the container. |
2308 | /// * [Flow], which provides paint-time control of its children using transform |
2309 | /// matrices. |
2310 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2311 | class CustomMultiChildLayout extends MultiChildRenderObjectWidget { |
2312 | /// Creates a custom multi-child layout. |
2313 | const CustomMultiChildLayout({ |
2314 | super.key, |
2315 | required this.delegate, |
2316 | super.children, |
2317 | }); |
2318 | |
2319 | /// The delegate that controls the layout of the children. |
2320 | final MultiChildLayoutDelegate delegate; |
2321 | |
2322 | @override |
2323 | RenderCustomMultiChildLayoutBox createRenderObject(BuildContext context) { |
2324 | return RenderCustomMultiChildLayoutBox(delegate: delegate); |
2325 | } |
2326 | |
2327 | @override |
2328 | void updateRenderObject(BuildContext context, RenderCustomMultiChildLayoutBox renderObject) { |
2329 | renderObject.delegate = delegate; |
2330 | } |
2331 | } |
2332 | |
2333 | /// A box with a specified size. |
2334 | /// |
2335 | /// If given a child, this widget forces it to have a specific width and/or height. |
2336 | /// These values will be ignored if this widget's parent does not permit them. |
2337 | /// For example, this happens if the parent is the screen (forces the child to |
2338 | /// be the same size as the parent), or another [SizedBox] (forces its child to |
2339 | /// have a specific width and/or height). This can be remedied by wrapping the |
2340 | /// child [SizedBox] in a widget that does permit it to be any size up to the |
2341 | /// size of the parent, such as [Center] or [Align]. |
2342 | /// |
2343 | /// If either the width or height is null, this widget will try to size itself to |
2344 | /// match the child's size in that dimension. If the child's size depends on the |
2345 | /// size of its parent, the height and width must be provided. |
2346 | /// |
2347 | /// If not given a child, [SizedBox] will try to size itself as close to the |
2348 | /// specified height and width as possible given the parent's constraints. If |
2349 | /// [height] or [width] is null or unspecified, it will be treated as zero. |
2350 | /// |
2351 | /// The [SizedBox.expand] constructor can be used to make a [SizedBox] that |
2352 | /// sizes itself to fit the parent. It is equivalent to setting [width] and |
2353 | /// [height] to [double.infinity]. |
2354 | /// |
2355 | /// {@youtube 560 315 https://www.youtube.com/watch?v=EHPu_DzRfqA} |
2356 | /// |
2357 | /// {@tool snippet} |
2358 | /// |
2359 | /// This snippet makes the child widget (a [Card] with some [Text]) have the |
2360 | /// exact size 200x300, parental constraints permitting: |
2361 | /// |
2362 | /// ```dart |
2363 | /// const SizedBox( |
2364 | /// width: 200.0, |
2365 | /// height: 300.0, |
2366 | /// child: Card(child: Text('Hello World!')), |
2367 | /// ) |
2368 | /// ``` |
2369 | /// {@end-tool} |
2370 | /// |
2371 | /// See also: |
2372 | /// |
2373 | /// * [ConstrainedBox], a more generic version of this class that takes |
2374 | /// arbitrary [BoxConstraints] instead of an explicit width and height. |
2375 | /// * [UnconstrainedBox], a container that tries to let its child draw without |
2376 | /// constraints. |
2377 | /// * [FractionallySizedBox], a widget that sizes its child to a fraction of |
2378 | /// the total available space. |
2379 | /// * [AspectRatio], a widget that attempts to fit within the parent's |
2380 | /// constraints while also sizing its child to match a given aspect ratio. |
2381 | /// * [FittedBox], which sizes and positions its child widget to fit the parent |
2382 | /// according to a given [BoxFit] discipline. |
2383 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2384 | /// * [Understanding constraints](https://flutter.dev/docs/development/ui/layout/constraints), |
2385 | /// an in-depth article about layout in Flutter. |
2386 | class SizedBox extends SingleChildRenderObjectWidget { |
2387 | /// Creates a fixed size box. The [width] and [height] parameters can be null |
2388 | /// to indicate that the size of the box should not be constrained in |
2389 | /// the corresponding dimension. |
2390 | const SizedBox({ super.key, this.width, this.height, super.child }); |
2391 | |
2392 | /// Creates a box that will become as large as its parent allows. |
2393 | const SizedBox.expand({ super.key, super.child }) |
2394 | : width = double.infinity, |
2395 | height = double.infinity; |
2396 | |
2397 | /// Creates a box that will become as small as its parent allows. |
2398 | const SizedBox.shrink({ super.key, super.child }) |
2399 | : width = 0.0, |
2400 | height = 0.0; |
2401 | |
2402 | /// Creates a box with the specified size. |
2403 | SizedBox.fromSize({ super.key, super.child, Size? size }) |
2404 | : width = size?.width, |
2405 | height = size?.height; |
2406 | |
2407 | /// Creates a box whose [width] and [height] are equal. |
2408 | const SizedBox.square({super.key, super.child, double? dimension}) |
2409 | : width = dimension, |
2410 | height = dimension; |
2411 | |
2412 | /// If non-null, requires the child to have exactly this width. |
2413 | final double? width; |
2414 | |
2415 | /// If non-null, requires the child to have exactly this height. |
2416 | final double? height; |
2417 | |
2418 | @override |
2419 | RenderConstrainedBox createRenderObject(BuildContext context) { |
2420 | return RenderConstrainedBox( |
2421 | additionalConstraints: _additionalConstraints, |
2422 | ); |
2423 | } |
2424 | |
2425 | BoxConstraints get _additionalConstraints { |
2426 | return BoxConstraints.tightFor(width: width, height: height); |
2427 | } |
2428 | |
2429 | @override |
2430 | void updateRenderObject(BuildContext context, RenderConstrainedBox renderObject) { |
2431 | renderObject.additionalConstraints = _additionalConstraints; |
2432 | } |
2433 | |
2434 | @override |
2435 | String toStringShort() { |
2436 | final String type; |
2437 | if (width == double.infinity && height == double.infinity) { |
2438 | type = ' ${objectRuntimeType(this, 'SizedBox' )}.expand' ; |
2439 | } else if (width == 0.0 && height == 0.0) { |
2440 | type = ' ${objectRuntimeType(this, 'SizedBox' )}.shrink' ; |
2441 | } else { |
2442 | type = objectRuntimeType(this, 'SizedBox' ); |
2443 | } |
2444 | return key == null ? type : ' $type- $key' ; |
2445 | } |
2446 | |
2447 | @override |
2448 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
2449 | super.debugFillProperties(properties); |
2450 | final DiagnosticLevel level; |
2451 | if ((width == double.infinity && height == double.infinity) || |
2452 | (width == 0.0 && height == 0.0)) { |
2453 | level = DiagnosticLevel.hidden; |
2454 | } else { |
2455 | level = DiagnosticLevel.info; |
2456 | } |
2457 | properties.add(DoubleProperty('width' , width, defaultValue: null, level: level)); |
2458 | properties.add(DoubleProperty('height' , height, defaultValue: null, level: level)); |
2459 | } |
2460 | } |
2461 | |
2462 | /// A widget that imposes additional constraints on its child. |
2463 | /// |
2464 | /// For example, if you wanted [child] to have a minimum height of 50.0 logical |
2465 | /// pixels, you could use `const BoxConstraints(minHeight: 50.0)` as the |
2466 | /// [constraints]. |
2467 | /// |
2468 | /// {@youtube 560 315 https://www.youtube.com/watch?v=o2KveVr7adg} |
2469 | /// |
2470 | /// {@tool snippet} |
2471 | /// |
2472 | /// This snippet makes the child widget (a [Card] with some [Text]) fill the |
2473 | /// parent, by applying [BoxConstraints.expand] constraints: |
2474 | /// |
2475 | /// ```dart |
2476 | /// ConstrainedBox( |
2477 | /// constraints: const BoxConstraints.expand(), |
2478 | /// child: const Card(child: Text('Hello World!')), |
2479 | /// ) |
2480 | /// ``` |
2481 | /// {@end-tool} |
2482 | /// |
2483 | /// The same behavior can be obtained using the [SizedBox.expand] widget. |
2484 | /// |
2485 | /// See also: |
2486 | /// |
2487 | /// * [BoxConstraints], the class that describes constraints. |
2488 | /// * [UnconstrainedBox], a container that tries to let its child draw without |
2489 | /// constraints. |
2490 | /// * [SizedBox], which lets you specify tight constraints by explicitly |
2491 | /// specifying the height or width. |
2492 | /// * [FractionallySizedBox], which sizes its child based on a fraction of its |
2493 | /// own size and positions the child according to an [Alignment] value. |
2494 | /// * [AspectRatio], a widget that attempts to fit within the parent's |
2495 | /// constraints while also sizing its child to match a given aspect ratio. |
2496 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2497 | class ConstrainedBox extends SingleChildRenderObjectWidget { |
2498 | /// Creates a widget that imposes additional constraints on its child. |
2499 | ConstrainedBox({ |
2500 | super.key, |
2501 | required this.constraints, |
2502 | super.child, |
2503 | }) : assert(constraints.debugAssertIsValid()); |
2504 | |
2505 | /// The additional constraints to impose on the child. |
2506 | final BoxConstraints constraints; |
2507 | |
2508 | @override |
2509 | RenderConstrainedBox createRenderObject(BuildContext context) { |
2510 | return RenderConstrainedBox(additionalConstraints: constraints); |
2511 | } |
2512 | |
2513 | @override |
2514 | void updateRenderObject(BuildContext context, RenderConstrainedBox renderObject) { |
2515 | renderObject.additionalConstraints = constraints; |
2516 | } |
2517 | |
2518 | @override |
2519 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
2520 | super.debugFillProperties(properties); |
2521 | properties.add(DiagnosticsProperty<BoxConstraints>('constraints' , constraints, showName: false)); |
2522 | } |
2523 | } |
2524 | |
2525 | /// A container widget that applies an arbitrary transform to its constraints, |
2526 | /// and sizes its child using the resulting [BoxConstraints], optionally |
2527 | /// clipping, or treating the overflow as an error. |
2528 | /// |
2529 | /// This container sizes its child using a [BoxConstraints] created by applying |
2530 | /// [constraintsTransform] to its own constraints. This container will then |
2531 | /// attempt to adopt the same size, within the limits of its own constraints. If |
2532 | /// it ends up with a different size, it will align the child based on |
2533 | /// [alignment]. If the container cannot expand enough to accommodate the entire |
2534 | /// child, the child will be clipped if [clipBehavior] is not [Clip.none]. |
2535 | /// |
2536 | /// In debug mode, if [clipBehavior] is [Clip.none] and the child overflows the |
2537 | /// container, a warning will be printed on the console, and black and yellow |
2538 | /// striped areas will appear where the overflow occurs. |
2539 | /// |
2540 | /// When [child] is null, this widget becomes as small as possible and never |
2541 | /// overflows. |
2542 | /// |
2543 | /// This widget can be used to ensure some of [child]'s natural dimensions are |
2544 | /// honored, and get an early warning otherwise during development. For |
2545 | /// instance, if [child] requires a minimum height to fully display its content, |
2546 | /// [constraintsTransform] can be set to [maxHeightUnconstrained], so that if |
2547 | /// the parent [RenderObject] fails to provide enough vertical space, a warning |
2548 | /// will be displayed in debug mode, while still allowing [child] to grow |
2549 | /// vertically: |
2550 | /// |
2551 | /// {@tool snippet} |
2552 | /// In the following snippet, the [Card] is guaranteed to be at least as tall as |
2553 | /// its "natural" height. Unlike [UnconstrainedBox], it will become taller if |
2554 | /// its "natural" height is smaller than 40 px. If the [Container] isn't high |
2555 | /// enough to show the full content of the [Card], in debug mode a warning will |
2556 | /// be given. |
2557 | /// |
2558 | /// ```dart |
2559 | /// Container( |
2560 | /// constraints: const BoxConstraints(minHeight: 40, maxHeight: 100), |
2561 | /// alignment: Alignment.center, |
2562 | /// child: const ConstraintsTransformBox( |
2563 | /// constraintsTransform: ConstraintsTransformBox.maxHeightUnconstrained, |
2564 | /// child: Card(child: Text('Hello World!')), |
2565 | /// ) |
2566 | /// ) |
2567 | /// ``` |
2568 | /// {@end-tool} |
2569 | /// |
2570 | /// See also: |
2571 | /// |
2572 | /// * [ConstrainedBox], which renders a box which imposes constraints |
2573 | /// on its child. |
2574 | /// * [OverflowBox], a widget that imposes additional constraints on its child, |
2575 | /// and allows the child to overflow itself. |
2576 | /// * [UnconstrainedBox] which allows its children to render themselves |
2577 | /// unconstrained and expands to fit them. |
2578 | class ConstraintsTransformBox extends SingleChildRenderObjectWidget { |
2579 | /// Creates a widget that uses a function to transform the constraints it |
2580 | /// passes to its child. If the child overflows the parent's constraints, a |
2581 | /// warning will be given in debug mode. |
2582 | /// |
2583 | /// The `debugTransformType` argument adds a debug label to this widget. |
2584 | /// |
2585 | /// The `alignment`, `clipBehavior` and `constraintsTransform` arguments must |
2586 | /// not be null. |
2587 | const ConstraintsTransformBox({ |
2588 | super.key, |
2589 | super.child, |
2590 | this.textDirection, |
2591 | this.alignment = Alignment.center, |
2592 | required this.constraintsTransform, |
2593 | this.clipBehavior = Clip.none, |
2594 | String debugTransformType = '' , |
2595 | }) : _debugTransformLabel = debugTransformType; |
2596 | |
2597 | /// A [BoxConstraintsTransform] that always returns its argument as-is (i.e., |
2598 | /// it is an identity function). |
2599 | /// |
2600 | /// The [ConstraintsTransformBox] becomes a proxy widget that has no effect on |
2601 | /// layout if [constraintsTransform] is set to this. |
2602 | static BoxConstraints unmodified(BoxConstraints constraints) => constraints; |
2603 | |
2604 | /// A [BoxConstraintsTransform] that always returns a [BoxConstraints] that |
2605 | /// imposes no constraints on either dimension (i.e. `const BoxConstraints()`). |
2606 | /// |
2607 | /// Setting [constraintsTransform] to this allows [child] to render at its |
2608 | /// "natural" size (equivalent to an [UnconstrainedBox] with `constrainedAxis` |
2609 | /// set to null). |
2610 | static BoxConstraints unconstrained(BoxConstraints constraints) => const BoxConstraints(); |
2611 | |
2612 | /// A [BoxConstraintsTransform] that removes the width constraints from the |
2613 | /// input. |
2614 | /// |
2615 | /// Setting [constraintsTransform] to this allows [child] to render at its |
2616 | /// "natural" width (equivalent to an [UnconstrainedBox] with |
2617 | /// `constrainedAxis` set to [Axis.horizontal]). |
2618 | static BoxConstraints widthUnconstrained(BoxConstraints constraints) => constraints.heightConstraints(); |
2619 | |
2620 | /// A [BoxConstraintsTransform] that removes the height constraints from the |
2621 | /// input. |
2622 | /// |
2623 | /// Setting [constraintsTransform] to this allows [child] to render at its |
2624 | /// "natural" height (equivalent to an [UnconstrainedBox] with |
2625 | /// `constrainedAxis` set to [Axis.vertical]). |
2626 | static BoxConstraints heightUnconstrained(BoxConstraints constraints) => constraints.widthConstraints(); |
2627 | |
2628 | /// A [BoxConstraintsTransform] that removes the `maxHeight` constraint from |
2629 | /// the input. |
2630 | /// |
2631 | /// Setting [constraintsTransform] to this allows [child] to render at its |
2632 | /// "natural" height or the `minHeight` of the incoming [BoxConstraints], |
2633 | /// whichever is larger. |
2634 | static BoxConstraints maxHeightUnconstrained(BoxConstraints constraints) => constraints.copyWith(maxHeight: double.infinity); |
2635 | |
2636 | /// A [BoxConstraintsTransform] that removes the `maxWidth` constraint from |
2637 | /// the input. |
2638 | /// |
2639 | /// Setting [constraintsTransform] to this allows [child] to render at its |
2640 | /// "natural" width or the `minWidth` of the incoming [BoxConstraints], |
2641 | /// whichever is larger. |
2642 | static BoxConstraints maxWidthUnconstrained(BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity); |
2643 | |
2644 | /// A [BoxConstraintsTransform] that removes both the `maxWidth` and the |
2645 | /// `maxHeight` constraints from the input. |
2646 | /// |
2647 | /// Setting [constraintsTransform] to this allows [child] to render at least |
2648 | /// its "natural" size, and grow along an axis if the incoming |
2649 | /// [BoxConstraints] has a larger minimum constraint on that axis. |
2650 | static BoxConstraints maxUnconstrained(BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity, maxHeight: double.infinity); |
2651 | |
2652 | static final Map<BoxConstraintsTransform, String> _debugKnownTransforms = <BoxConstraintsTransform, String>{ |
2653 | unmodified: 'unmodified' , |
2654 | unconstrained: 'unconstrained' , |
2655 | widthUnconstrained: 'width constraints removed' , |
2656 | heightUnconstrained: 'height constraints removed' , |
2657 | maxWidthUnconstrained: 'maxWidth constraint removed' , |
2658 | maxHeightUnconstrained: 'maxHeight constraint removed' , |
2659 | maxUnconstrained: 'maxWidth & maxHeight constraints removed' , |
2660 | }; |
2661 | |
2662 | /// The text direction to use when interpreting the [alignment] if it is an |
2663 | /// [AlignmentDirectional]. |
2664 | /// |
2665 | /// Defaults to null, in which case [Directionality.maybeOf] is used to determine |
2666 | /// the text direction. |
2667 | final TextDirection? textDirection; |
2668 | |
2669 | /// The alignment to use when laying out the child, if it has a different size |
2670 | /// than this widget. |
2671 | /// |
2672 | /// If this is an [AlignmentDirectional], then [textDirection] must not be |
2673 | /// null. |
2674 | /// |
2675 | /// See also: |
2676 | /// |
2677 | /// * [Alignment] for non-[Directionality]-aware alignments. |
2678 | /// * [AlignmentDirectional] for [Directionality]-aware alignments. |
2679 | final AlignmentGeometry alignment; |
2680 | |
2681 | /// {@template flutter.widgets.constraintsTransform} |
2682 | /// The function used to transform the incoming [BoxConstraints], to size |
2683 | /// [child]. |
2684 | /// |
2685 | /// The function must return a [BoxConstraints] that is |
2686 | /// [BoxConstraints.isNormalized]. |
2687 | /// |
2688 | /// See [ConstraintsTransformBox] for predefined common |
2689 | /// [BoxConstraintsTransform]s. |
2690 | /// {@endtemplate} |
2691 | final BoxConstraintsTransform constraintsTransform; |
2692 | |
2693 | /// {@macro flutter.material.Material.clipBehavior} |
2694 | /// |
2695 | /// {@template flutter.widgets.ConstraintsTransformBox.clipBehavior} |
2696 | /// In debug mode, if [clipBehavior] is [Clip.none], and the child overflows |
2697 | /// its constraints, a warning will be printed on the console, and black and |
2698 | /// yellow striped areas will appear where the overflow occurs. For other |
2699 | /// values of [clipBehavior], the contents are clipped accordingly. |
2700 | /// {@endtemplate} |
2701 | /// |
2702 | /// Defaults to [Clip.none]. |
2703 | final Clip clipBehavior; |
2704 | |
2705 | final String _debugTransformLabel; |
2706 | |
2707 | @override |
2708 | RenderConstraintsTransformBox createRenderObject(BuildContext context) { |
2709 | return RenderConstraintsTransformBox( |
2710 | textDirection: textDirection ?? Directionality.maybeOf(context), |
2711 | alignment: alignment, |
2712 | constraintsTransform: constraintsTransform, |
2713 | clipBehavior: clipBehavior, |
2714 | ); |
2715 | } |
2716 | |
2717 | @override |
2718 | void updateRenderObject(BuildContext context, covariant RenderConstraintsTransformBox renderObject) { |
2719 | renderObject |
2720 | ..textDirection = textDirection ?? Directionality.maybeOf(context) |
2721 | ..constraintsTransform = constraintsTransform |
2722 | ..alignment = alignment |
2723 | ..clipBehavior = clipBehavior; |
2724 | } |
2725 | |
2726 | @override |
2727 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
2728 | super.debugFillProperties(properties); |
2729 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
2730 | properties.add(EnumProperty<TextDirection>('textDirection' , textDirection, defaultValue: null)); |
2731 | |
2732 | final String? debugTransformLabel = _debugTransformLabel.isNotEmpty |
2733 | ? _debugTransformLabel |
2734 | : _debugKnownTransforms[constraintsTransform]; |
2735 | |
2736 | if (debugTransformLabel != null) { |
2737 | properties.add(DiagnosticsProperty<String>('constraints transform' , debugTransformLabel)); |
2738 | } |
2739 | } |
2740 | } |
2741 | |
2742 | /// A widget that imposes no constraints on its child, allowing it to render |
2743 | /// at its "natural" size. |
2744 | /// |
2745 | /// This allows a child to render at the size it would render if it were alone |
2746 | /// on an infinite canvas with no constraints. This container will then attempt |
2747 | /// to adopt the same size, within the limits of its own constraints. If it ends |
2748 | /// up with a different size, it will align the child based on [alignment]. |
2749 | /// If the box cannot expand enough to accommodate the entire child, the |
2750 | /// child will be clipped. |
2751 | /// |
2752 | /// In debug mode, if the child overflows the container, a warning will be |
2753 | /// printed on the console, and black and yellow striped areas will appear where |
2754 | /// the overflow occurs. |
2755 | /// |
2756 | /// See also: |
2757 | /// |
2758 | /// * [ConstrainedBox], for a box which imposes constraints on its child. |
2759 | /// * [Align], which loosens the constraints given to the child rather than |
2760 | /// removing them entirely. |
2761 | /// * [Container], a convenience widget that combines common painting, |
2762 | /// positioning, and sizing widgets. |
2763 | /// * [OverflowBox], a widget that imposes different constraints on its child |
2764 | /// than it gets from its parent, possibly allowing the child to overflow |
2765 | /// the parent. |
2766 | /// * [ConstraintsTransformBox], a widget that sizes its child using a |
2767 | /// transformed [BoxConstraints], and shows a warning if the child overflows |
2768 | /// in debug mode. |
2769 | class UnconstrainedBox extends StatelessWidget { |
2770 | /// Creates a widget that imposes no constraints on its child, allowing it to |
2771 | /// render at its "natural" size. If the child overflows the parents |
2772 | /// constraints, a warning will be given in debug mode. |
2773 | const UnconstrainedBox({ |
2774 | super.key, |
2775 | this.child, |
2776 | this.textDirection, |
2777 | this.alignment = Alignment.center, |
2778 | this.constrainedAxis, |
2779 | this.clipBehavior = Clip.none, |
2780 | }); |
2781 | |
2782 | /// The text direction to use when interpreting the [alignment] if it is an |
2783 | /// [AlignmentDirectional]. |
2784 | final TextDirection? textDirection; |
2785 | |
2786 | /// The alignment to use when laying out the child. |
2787 | /// |
2788 | /// If this is an [AlignmentDirectional], then [textDirection] must not be |
2789 | /// null. |
2790 | /// |
2791 | /// See also: |
2792 | /// |
2793 | /// * [Alignment] for non-[Directionality]-aware alignments. |
2794 | /// * [AlignmentDirectional] for [Directionality]-aware alignments. |
2795 | final AlignmentGeometry alignment; |
2796 | |
2797 | /// The axis to retain constraints on, if any. |
2798 | /// |
2799 | /// If not set, or set to null (the default), neither axis will retain its |
2800 | /// constraints. If set to [Axis.vertical], then vertical constraints will |
2801 | /// be retained, and if set to [Axis.horizontal], then horizontal constraints |
2802 | /// will be retained. |
2803 | final Axis? constrainedAxis; |
2804 | |
2805 | /// {@macro flutter.material.Material.clipBehavior} |
2806 | /// |
2807 | /// Defaults to [Clip.none]. |
2808 | final Clip clipBehavior; |
2809 | |
2810 | /// The widget below this widget in the tree. |
2811 | /// |
2812 | /// {@macro flutter.widgets.ProxyWidget.child} |
2813 | final Widget? child; |
2814 | |
2815 | BoxConstraintsTransform _axisToTransform(Axis? constrainedAxis) { |
2816 | if (constrainedAxis != null) { |
2817 | switch (constrainedAxis) { |
2818 | case Axis.horizontal: |
2819 | return ConstraintsTransformBox.heightUnconstrained; |
2820 | case Axis.vertical: |
2821 | return ConstraintsTransformBox.widthUnconstrained; |
2822 | } |
2823 | } else { |
2824 | return ConstraintsTransformBox.unconstrained; |
2825 | } |
2826 | } |
2827 | |
2828 | @override |
2829 | Widget build(BuildContext context) { |
2830 | return ConstraintsTransformBox( |
2831 | textDirection: textDirection, |
2832 | alignment: alignment, |
2833 | clipBehavior: clipBehavior, |
2834 | constraintsTransform: _axisToTransform(constrainedAxis), |
2835 | child: child, |
2836 | ); |
2837 | } |
2838 | |
2839 | @override |
2840 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
2841 | super.debugFillProperties(properties); |
2842 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
2843 | properties.add(EnumProperty<Axis>('constrainedAxis' , constrainedAxis, defaultValue: null)); |
2844 | properties.add(EnumProperty<TextDirection>('textDirection' , textDirection, defaultValue: null)); |
2845 | } |
2846 | } |
2847 | |
2848 | /// A widget that sizes its child to a fraction of the total available space. |
2849 | /// For more details about the layout algorithm, see |
2850 | /// [RenderFractionallySizedOverflowBox]. |
2851 | /// |
2852 | /// {@youtube 560 315 https://www.youtube.com/watch?v=PEsY654EGZ0} |
2853 | /// |
2854 | /// {@tool dartpad} |
2855 | /// This sample shows a [FractionallySizedBox] whose one child is 50% of |
2856 | /// the box's size per the width and height factor parameters, and centered |
2857 | /// within that box by the alignment parameter. |
2858 | /// |
2859 | /// ** See code in examples/api/lib/widgets/basic/fractionally_sized_box.0.dart ** |
2860 | /// {@end-tool} |
2861 | /// |
2862 | /// See also: |
2863 | /// |
2864 | /// * [Align], which sizes itself based on its child's size and positions |
2865 | /// the child according to an [Alignment] value. |
2866 | /// * [OverflowBox], a widget that imposes different constraints on its child |
2867 | /// than it gets from its parent, possibly allowing the child to overflow the |
2868 | /// parent. |
2869 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2870 | class FractionallySizedBox extends SingleChildRenderObjectWidget { |
2871 | /// Creates a widget that sizes its child to a fraction of the total available space. |
2872 | /// |
2873 | /// If non-null, the [widthFactor] and [heightFactor] arguments must be |
2874 | /// non-negative. |
2875 | const FractionallySizedBox({ |
2876 | super.key, |
2877 | this.alignment = Alignment.center, |
2878 | this.widthFactor, |
2879 | this.heightFactor, |
2880 | super.child, |
2881 | }) : assert(widthFactor == null || widthFactor >= 0.0), |
2882 | assert(heightFactor == null || heightFactor >= 0.0); |
2883 | |
2884 | /// {@template flutter.widgets.basic.fractionallySizedBox.widthFactor} |
2885 | /// If non-null, the fraction of the incoming width given to the child. |
2886 | /// |
2887 | /// If non-null, the child is given a tight width constraint that is the max |
2888 | /// incoming width constraint multiplied by this factor. |
2889 | /// |
2890 | /// If null, the incoming width constraints are passed to the child |
2891 | /// unmodified. |
2892 | /// {@endtemplate} |
2893 | final double? widthFactor; |
2894 | |
2895 | /// {@template flutter.widgets.basic.fractionallySizedBox.heightFactor} |
2896 | /// If non-null, the fraction of the incoming height given to the child. |
2897 | /// |
2898 | /// If non-null, the child is given a tight height constraint that is the max |
2899 | /// incoming height constraint multiplied by this factor. |
2900 | /// |
2901 | /// If null, the incoming height constraints are passed to the child |
2902 | /// unmodified. |
2903 | /// {@endtemplate} |
2904 | final double? heightFactor; |
2905 | |
2906 | /// {@template flutter.widgets.basic.fractionallySizedBox.alignment} |
2907 | /// How to align the child. |
2908 | /// |
2909 | /// The x and y values of the alignment control the horizontal and vertical |
2910 | /// alignment, respectively. An x value of -1.0 means that the left edge of |
2911 | /// the child is aligned with the left edge of the parent whereas an x value |
2912 | /// of 1.0 means that the right edge of the child is aligned with the right |
2913 | /// edge of the parent. Other values interpolate (and extrapolate) linearly. |
2914 | /// For example, a value of 0.0 means that the center of the child is aligned |
2915 | /// with the center of the parent. |
2916 | /// |
2917 | /// Defaults to [Alignment.center]. |
2918 | /// |
2919 | /// See also: |
2920 | /// |
2921 | /// * [Alignment], a class with convenient constants typically used to |
2922 | /// specify an [AlignmentGeometry]. |
2923 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments |
2924 | /// relative to text direction. |
2925 | /// {@endtemplate} |
2926 | final AlignmentGeometry alignment; |
2927 | |
2928 | @override |
2929 | RenderFractionallySizedOverflowBox createRenderObject(BuildContext context) { |
2930 | return RenderFractionallySizedOverflowBox( |
2931 | alignment: alignment, |
2932 | widthFactor: widthFactor, |
2933 | heightFactor: heightFactor, |
2934 | textDirection: Directionality.maybeOf(context), |
2935 | ); |
2936 | } |
2937 | |
2938 | @override |
2939 | void updateRenderObject(BuildContext context, RenderFractionallySizedOverflowBox renderObject) { |
2940 | renderObject |
2941 | ..alignment = alignment |
2942 | ..widthFactor = widthFactor |
2943 | ..heightFactor = heightFactor |
2944 | ..textDirection = Directionality.maybeOf(context); |
2945 | } |
2946 | |
2947 | @override |
2948 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
2949 | super.debugFillProperties(properties); |
2950 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
2951 | properties.add(DoubleProperty('widthFactor' , widthFactor, defaultValue: null)); |
2952 | properties.add(DoubleProperty('heightFactor' , heightFactor, defaultValue: null)); |
2953 | } |
2954 | } |
2955 | |
2956 | /// A box that limits its size only when it's unconstrained. |
2957 | /// |
2958 | /// If this widget's maximum width is unconstrained then its child's width is |
2959 | /// limited to [maxWidth]. Similarly, if this widget's maximum height is |
2960 | /// unconstrained then its child's height is limited to [maxHeight]. |
2961 | /// |
2962 | /// This has the effect of giving the child a natural dimension in unbounded |
2963 | /// environments. For example, by providing a [maxHeight] to a widget that |
2964 | /// normally tries to be as big as possible, the widget will normally size |
2965 | /// itself to fit its parent, but when placed in a vertical list, it will take |
2966 | /// on the given height. |
2967 | /// |
2968 | /// This is useful when composing widgets that normally try to match their |
2969 | /// parents' size, so that they behave reasonably in lists (which are |
2970 | /// unbounded). |
2971 | /// |
2972 | /// {@youtube 560 315 https://www.youtube.com/watch?v=uVki2CIzBTs} |
2973 | /// |
2974 | /// See also: |
2975 | /// |
2976 | /// * [ConstrainedBox], which applies its constraints in all cases, not just |
2977 | /// when the incoming constraints are unbounded. |
2978 | /// * [SizedBox], which lets you specify tight constraints by explicitly |
2979 | /// specifying the height or width. |
2980 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
2981 | class LimitedBox extends SingleChildRenderObjectWidget { |
2982 | /// Creates a box that limits its size only when it's unconstrained. |
2983 | /// |
2984 | /// The [maxWidth] and [maxHeight] arguments must not be negative. |
2985 | const LimitedBox({ |
2986 | super.key, |
2987 | this.maxWidth = double.infinity, |
2988 | this.maxHeight = double.infinity, |
2989 | super.child, |
2990 | }) : assert(maxWidth >= 0.0), |
2991 | assert(maxHeight >= 0.0); |
2992 | |
2993 | /// The maximum width limit to apply in the absence of a |
2994 | /// [BoxConstraints.maxWidth] constraint. |
2995 | final double maxWidth; |
2996 | |
2997 | /// The maximum height limit to apply in the absence of a |
2998 | /// [BoxConstraints.maxHeight] constraint. |
2999 | final double maxHeight; |
3000 | |
3001 | @override |
3002 | RenderLimitedBox createRenderObject(BuildContext context) { |
3003 | return RenderLimitedBox( |
3004 | maxWidth: maxWidth, |
3005 | maxHeight: maxHeight, |
3006 | ); |
3007 | } |
3008 | |
3009 | @override |
3010 | void updateRenderObject(BuildContext context, RenderLimitedBox renderObject) { |
3011 | renderObject |
3012 | ..maxWidth = maxWidth |
3013 | ..maxHeight = maxHeight; |
3014 | } |
3015 | |
3016 | @override |
3017 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3018 | super.debugFillProperties(properties); |
3019 | properties.add(DoubleProperty('maxWidth' , maxWidth, defaultValue: double.infinity)); |
3020 | properties.add(DoubleProperty('maxHeight' , maxHeight, defaultValue: double.infinity)); |
3021 | } |
3022 | } |
3023 | |
3024 | /// A widget that imposes different constraints on its child than it gets |
3025 | /// from its parent, possibly allowing the child to overflow the parent. |
3026 | /// |
3027 | /// {@tool dartpad} |
3028 | /// This example shows how an [OverflowBox] is used, and what its effect is. |
3029 | /// |
3030 | /// ** See code in examples/api/lib/widgets/basic/overflowbox.0.dart ** |
3031 | /// {@end-tool} |
3032 | /// |
3033 | /// See also: |
3034 | /// |
3035 | /// * [RenderConstrainedOverflowBox] for details about how [OverflowBox] is |
3036 | /// rendered. |
3037 | /// * [SizedOverflowBox], a widget that is a specific size but passes its |
3038 | /// original constraints through to its child, which may then overflow. |
3039 | /// * [ConstrainedBox], a widget that imposes additional constraints on its |
3040 | /// child. |
3041 | /// * [UnconstrainedBox], a container that tries to let its child draw without |
3042 | /// constraints. |
3043 | /// * [SizedBox], a box with a specified size. |
3044 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3045 | class OverflowBox extends SingleChildRenderObjectWidget { |
3046 | /// Creates a widget that lets its child overflow itself. |
3047 | const OverflowBox({ |
3048 | super.key, |
3049 | this.alignment = Alignment.center, |
3050 | this.minWidth, |
3051 | this.maxWidth, |
3052 | this.minHeight, |
3053 | this.maxHeight, |
3054 | this.fit = OverflowBoxFit.max, |
3055 | super.child, |
3056 | }); |
3057 | |
3058 | /// How to align the child. |
3059 | /// |
3060 | /// The x and y values of the alignment control the horizontal and vertical |
3061 | /// alignment, respectively. An x value of -1.0 means that the left edge of |
3062 | /// the child is aligned with the left edge of the parent whereas an x value |
3063 | /// of 1.0 means that the right edge of the child is aligned with the right |
3064 | /// edge of the parent. Other values interpolate (and extrapolate) linearly. |
3065 | /// For example, a value of 0.0 means that the center of the child is aligned |
3066 | /// with the center of the parent. |
3067 | /// |
3068 | /// Defaults to [Alignment.center]. |
3069 | /// |
3070 | /// See also: |
3071 | /// |
3072 | /// * [Alignment], a class with convenient constants typically used to |
3073 | /// specify an [AlignmentGeometry]. |
3074 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments |
3075 | /// relative to text direction. |
3076 | final AlignmentGeometry alignment; |
3077 | |
3078 | /// The minimum width constraint to give the child. Set this to null (the |
3079 | /// default) to use the constraint from the parent instead. |
3080 | final double? minWidth; |
3081 | |
3082 | /// The maximum width constraint to give the child. Set this to null (the |
3083 | /// default) to use the constraint from the parent instead. |
3084 | final double? maxWidth; |
3085 | |
3086 | /// The minimum height constraint to give the child. Set this to null (the |
3087 | /// default) to use the constraint from the parent instead. |
3088 | final double? minHeight; |
3089 | |
3090 | /// The maximum height constraint to give the child. Set this to null (the |
3091 | /// default) to use the constraint from the parent instead. |
3092 | final double? maxHeight; |
3093 | |
3094 | /// The way to size the render object. |
3095 | /// |
3096 | /// This only affects scenario when the child does not indeed overflow. |
3097 | /// If set to [OverflowBoxFit.deferToChild], the render object will size itself to |
3098 | /// match the size of its child within the constraints of its parent or be |
3099 | /// as small as the parent allows if no child is set. If set to |
3100 | /// [OverflowBoxFit.max] (the default), the render object will size itself |
3101 | /// to be as large as the parent allows. |
3102 | final OverflowBoxFit fit; |
3103 | |
3104 | @override |
3105 | RenderConstrainedOverflowBox createRenderObject(BuildContext context) { |
3106 | return RenderConstrainedOverflowBox( |
3107 | alignment: alignment, |
3108 | minWidth: minWidth, |
3109 | maxWidth: maxWidth, |
3110 | minHeight: minHeight, |
3111 | maxHeight: maxHeight, |
3112 | fit: fit, |
3113 | textDirection: Directionality.maybeOf(context), |
3114 | ); |
3115 | } |
3116 | |
3117 | @override |
3118 | void updateRenderObject(BuildContext context, RenderConstrainedOverflowBox renderObject) { |
3119 | renderObject |
3120 | ..alignment = alignment |
3121 | ..minWidth = minWidth |
3122 | ..maxWidth = maxWidth |
3123 | ..minHeight = minHeight |
3124 | ..maxHeight = maxHeight |
3125 | ..fit = fit |
3126 | ..textDirection = Directionality.maybeOf(context); |
3127 | } |
3128 | |
3129 | @override |
3130 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3131 | super.debugFillProperties(properties); |
3132 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
3133 | properties.add(DoubleProperty('minWidth' , minWidth, defaultValue: null)); |
3134 | properties.add(DoubleProperty('maxWidth' , maxWidth, defaultValue: null)); |
3135 | properties.add(DoubleProperty('minHeight' , minHeight, defaultValue: null)); |
3136 | properties.add(DoubleProperty('maxHeight' , maxHeight, defaultValue: null)); |
3137 | properties.add(EnumProperty<OverflowBoxFit>('fit' , fit)); |
3138 | } |
3139 | } |
3140 | |
3141 | /// A widget that is a specific size but passes its original constraints |
3142 | /// through to its child, which may then overflow. |
3143 | /// |
3144 | /// See also: |
3145 | /// |
3146 | /// * [OverflowBox], A widget that imposes different constraints on its child |
3147 | /// than it gets from its parent, possibly allowing the child to overflow the |
3148 | /// parent. |
3149 | /// * [ConstrainedBox], a widget that imposes additional constraints on its |
3150 | /// child. |
3151 | /// * [UnconstrainedBox], a container that tries to let its child draw without |
3152 | /// constraints. |
3153 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3154 | class SizedOverflowBox extends SingleChildRenderObjectWidget { |
3155 | /// Creates a widget of a given size that lets its child overflow. |
3156 | const SizedOverflowBox({ |
3157 | super.key, |
3158 | required this.size, |
3159 | this.alignment = Alignment.center, |
3160 | super.child, |
3161 | }); |
3162 | |
3163 | /// How to align the child. |
3164 | /// |
3165 | /// The x and y values of the alignment control the horizontal and vertical |
3166 | /// alignment, respectively. An x value of -1.0 means that the left edge of |
3167 | /// the child is aligned with the left edge of the parent whereas an x value |
3168 | /// of 1.0 means that the right edge of the child is aligned with the right |
3169 | /// edge of the parent. Other values interpolate (and extrapolate) linearly. |
3170 | /// For example, a value of 0.0 means that the center of the child is aligned |
3171 | /// with the center of the parent. |
3172 | /// |
3173 | /// Defaults to [Alignment.center]. |
3174 | /// |
3175 | /// See also: |
3176 | /// |
3177 | /// * [Alignment], a class with convenient constants typically used to |
3178 | /// specify an [AlignmentGeometry]. |
3179 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments |
3180 | /// relative to text direction. |
3181 | final AlignmentGeometry alignment; |
3182 | |
3183 | /// The size this widget should attempt to be. |
3184 | final Size size; |
3185 | |
3186 | @override |
3187 | RenderSizedOverflowBox createRenderObject(BuildContext context) { |
3188 | return RenderSizedOverflowBox( |
3189 | alignment: alignment, |
3190 | requestedSize: size, |
3191 | textDirection: Directionality.of(context), |
3192 | ); |
3193 | } |
3194 | |
3195 | @override |
3196 | void updateRenderObject(BuildContext context, RenderSizedOverflowBox renderObject) { |
3197 | renderObject |
3198 | ..alignment = alignment |
3199 | ..requestedSize = size |
3200 | ..textDirection = Directionality.of(context); |
3201 | } |
3202 | |
3203 | @override |
3204 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3205 | super.debugFillProperties(properties); |
3206 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
3207 | properties.add(DiagnosticsProperty<Size>('size' , size, defaultValue: null)); |
3208 | } |
3209 | } |
3210 | |
3211 | /// A widget that lays the child out as if it was in the tree, but without |
3212 | /// painting anything, without making the child available for hit testing, and |
3213 | /// without taking any room in the parent. |
3214 | /// |
3215 | /// Offstage children are still active: they can receive focus and have keyboard |
3216 | /// input directed to them. |
3217 | /// |
3218 | /// Animations continue to run in offstage children, and therefore use battery |
3219 | /// and CPU time, regardless of whether the animations end up being visible. |
3220 | /// |
3221 | /// [Offstage] can be used to measure the dimensions of a widget without |
3222 | /// bringing it on screen (yet). To hide a widget from view while it is not |
3223 | /// needed, prefer removing the widget from the tree entirely rather than |
3224 | /// keeping it alive in an [Offstage] subtree. |
3225 | /// |
3226 | /// {@tool dartpad} |
3227 | /// This example shows a [FlutterLogo] widget when the `_offstage` member field |
3228 | /// is false, and hides it without any room in the parent when it is true. When |
3229 | /// offstage, this app displays a button to get the logo size, which will be |
3230 | /// displayed in a [SnackBar]. |
3231 | /// |
3232 | /// ** See code in examples/api/lib/widgets/basic/offstage.0.dart ** |
3233 | /// {@end-tool} |
3234 | /// |
3235 | /// See also: |
3236 | /// |
3237 | /// * [Visibility], which can hide a child more efficiently (albeit less |
3238 | /// subtly). |
3239 | /// * [TickerMode], which can be used to disable animations in a subtree. |
3240 | /// * [SliverOffstage], the sliver version of this widget. |
3241 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3242 | class Offstage extends SingleChildRenderObjectWidget { |
3243 | /// Creates a widget that visually hides its child. |
3244 | const Offstage({ super.key, this.offstage = true, super.child }); |
3245 | |
3246 | /// Whether the child is hidden from the rest of the tree. |
3247 | /// |
3248 | /// If true, the child is laid out as if it was in the tree, but without |
3249 | /// painting anything, without making the child available for hit testing, and |
3250 | /// without taking any room in the parent. |
3251 | /// |
3252 | /// Offstage children are still active: they can receive focus and have keyboard |
3253 | /// input directed to them. |
3254 | /// |
3255 | /// Animations continue to run in offstage children, and therefore use battery |
3256 | /// and CPU time, regardless of whether the animations end up being visible. |
3257 | /// |
3258 | /// If false, the child is included in the tree as normal. |
3259 | final bool offstage; |
3260 | |
3261 | @override |
3262 | RenderOffstage createRenderObject(BuildContext context) => RenderOffstage(offstage: offstage); |
3263 | |
3264 | @override |
3265 | void updateRenderObject(BuildContext context, RenderOffstage renderObject) { |
3266 | renderObject.offstage = offstage; |
3267 | } |
3268 | |
3269 | @override |
3270 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3271 | super.debugFillProperties(properties); |
3272 | properties.add(DiagnosticsProperty<bool>('offstage' , offstage)); |
3273 | } |
3274 | |
3275 | @override |
3276 | SingleChildRenderObjectElement createElement() => _OffstageElement(this); |
3277 | } |
3278 | |
3279 | class _OffstageElement extends SingleChildRenderObjectElement { |
3280 | _OffstageElement(Offstage super.widget); |
3281 | |
3282 | @override |
3283 | void debugVisitOnstageChildren(ElementVisitor visitor) { |
3284 | if (!(widget as Offstage).offstage) { |
3285 | super.debugVisitOnstageChildren(visitor); |
3286 | } |
3287 | } |
3288 | } |
3289 | |
3290 | /// A widget that attempts to size the child to a specific aspect ratio. |
3291 | /// |
3292 | /// The aspect ratio is expressed as a ratio of width to height. For example, a |
3293 | /// 16:9 width:height aspect ratio would have a value of 16.0/9.0. |
3294 | /// |
3295 | /// {@youtube 560 315 https://www.youtube.com/watch?v=XcnP3_mO_Ms} |
3296 | /// |
3297 | /// The [AspectRatio] widget uses a finite iterative process to compute the |
3298 | /// appropriate constraints for the child, and then lays the child out a single |
3299 | /// time with those constraints. This iterative process is efficient and does |
3300 | /// not require multiple layout passes. |
3301 | /// |
3302 | /// The widget first tries the largest width permitted by the layout |
3303 | /// constraints, and determines the height of the widget by applying the given |
3304 | /// aspect ratio to the width, expressed as a ratio of width to height. |
3305 | /// |
3306 | /// If the maximum width is infinite, the initial width is determined |
3307 | /// by applying the aspect ratio to the maximum height instead. |
3308 | /// |
3309 | /// The widget then examines if the computed dimensions are compatible with the |
3310 | /// parent's constraints; if not, the dimensions are recomputed a second time, |
3311 | /// taking those constraints into account. |
3312 | /// |
3313 | /// If the widget does not find a feasible size after consulting each |
3314 | /// constraint, the widget will eventually select a size for the child that |
3315 | /// meets the layout constraints but fails to meet the aspect ratio constraints. |
3316 | /// |
3317 | /// {@tool dartpad} |
3318 | /// This examples shows how [AspectRatio] sets the width when its parent's width |
3319 | /// constraint is infinite. Since the parent's allowed height is a fixed value, |
3320 | /// the actual width is determined via the given [aspectRatio]. |
3321 | /// |
3322 | /// In this example, the height is fixed at 100.0 and the aspect ratio is set to |
3323 | /// 16 / 9, making the width 100.0 / 9 * 16. |
3324 | /// |
3325 | /// ** See code in examples/api/lib/widgets/basic/aspect_ratio.0.dart ** |
3326 | /// {@end-tool} |
3327 | /// |
3328 | /// {@tool dartpad} |
3329 | /// This second example uses an aspect ratio of 2.0, and layout constraints that |
3330 | /// require the width to be between 0.0 and 100.0, and the height to be between |
3331 | /// 0.0 and 100.0. The widget selects a width of 100.0 (the biggest allowed) and |
3332 | /// a height of 50.0 (to match the aspect ratio). |
3333 | /// |
3334 | /// ** See code in examples/api/lib/widgets/basic/aspect_ratio.1.dart ** |
3335 | /// {@end-tool} |
3336 | /// |
3337 | /// {@tool dartpad} |
3338 | /// This third example is similar to the second, but with the aspect ratio set |
3339 | /// to 0.5. The widget still selects a width of 100.0 (the biggest allowed), and |
3340 | /// attempts to use a height of 200.0. Unfortunately, that violates the |
3341 | /// constraints because the child can be at most 100.0 pixels tall. The widget |
3342 | /// will then take that value and apply the aspect ratio again to obtain a width |
3343 | /// of 50.0. That width is permitted by the constraints and the child receives a |
3344 | /// width of 50.0 and a height of 100.0. If the width were not permitted, the |
3345 | /// widget would continue iterating through the constraints. |
3346 | /// |
3347 | /// ** See code in examples/api/lib/widgets/basic/aspect_ratio.2.dart ** |
3348 | /// {@end-tool} |
3349 | /// |
3350 | /// ## Setting the aspect ratio in unconstrained situations |
3351 | /// |
3352 | /// When using a widget such as [FittedBox], the constraints are unbounded. This |
3353 | /// results in [AspectRatio] being unable to find a suitable set of constraints |
3354 | /// to apply. In that situation, consider explicitly setting a size using |
3355 | /// [SizedBox] instead of setting the aspect ratio using [AspectRatio]. The size |
3356 | /// is then scaled appropriately by the [FittedBox]. |
3357 | /// |
3358 | /// See also: |
3359 | /// |
3360 | /// * [Align], a widget that aligns its child within itself and optionally |
3361 | /// sizes itself based on the child's size. |
3362 | /// * [ConstrainedBox], a widget that imposes additional constraints on its |
3363 | /// child. |
3364 | /// * [UnconstrainedBox], a container that tries to let its child draw without |
3365 | /// constraints. |
3366 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3367 | class AspectRatio extends SingleChildRenderObjectWidget { |
3368 | /// Creates a widget with a specific aspect ratio. |
3369 | /// |
3370 | /// The [aspectRatio] argument must be a finite number greater than zero. |
3371 | const AspectRatio({ |
3372 | super.key, |
3373 | required this.aspectRatio, |
3374 | super.child, |
3375 | }) : assert(aspectRatio > 0.0); |
3376 | |
3377 | /// The aspect ratio to attempt to use. |
3378 | /// |
3379 | /// The aspect ratio is expressed as a ratio of width to height. For example, |
3380 | /// a 16:9 width:height aspect ratio would have a value of 16.0/9.0. |
3381 | final double aspectRatio; |
3382 | |
3383 | @override |
3384 | RenderAspectRatio createRenderObject(BuildContext context) => RenderAspectRatio(aspectRatio: aspectRatio); |
3385 | |
3386 | @override |
3387 | void updateRenderObject(BuildContext context, RenderAspectRatio renderObject) { |
3388 | renderObject.aspectRatio = aspectRatio; |
3389 | } |
3390 | |
3391 | @override |
3392 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3393 | super.debugFillProperties(properties); |
3394 | properties.add(DoubleProperty('aspectRatio' , aspectRatio)); |
3395 | } |
3396 | } |
3397 | |
3398 | /// A widget that sizes its child to the child's maximum intrinsic width. |
3399 | /// |
3400 | /// This class is useful, for example, when unlimited width is available and |
3401 | /// you would like a child that would otherwise attempt to expand infinitely to |
3402 | /// instead size itself to a more reasonable width. |
3403 | /// |
3404 | /// The constraints that this widget passes to its child will adhere to the |
3405 | /// parent's constraints, so if the constraints are not large enough to satisfy |
3406 | /// the child's maximum intrinsic width, then the child will get less width |
3407 | /// than it otherwise would. Likewise, if the minimum width constraint is |
3408 | /// larger than the child's maximum intrinsic width, the child will be given |
3409 | /// more width than it otherwise would. |
3410 | /// |
3411 | /// If [stepWidth] is non-null, the child's width will be snapped to a multiple |
3412 | /// of the [stepWidth]. Similarly, if [stepHeight] is non-null, the child's |
3413 | /// height will be snapped to a multiple of the [stepHeight]. |
3414 | /// |
3415 | /// This class is relatively expensive, because it adds a speculative layout |
3416 | /// pass before the final layout phase. Avoid using it where possible. In the |
3417 | /// worst case, this widget can result in a layout that is O(N²) in the depth of |
3418 | /// the tree. |
3419 | /// |
3420 | /// See also: |
3421 | /// |
3422 | /// * [Align], a widget that aligns its child within itself. This can be used |
3423 | /// to loosen the constraints passed to the [RenderIntrinsicWidth], |
3424 | /// allowing the [RenderIntrinsicWidth]'s child to be smaller than that of |
3425 | /// its parent. |
3426 | /// * [Row], which when used with [CrossAxisAlignment.stretch] can be used |
3427 | /// to loosen just the width constraints that are passed to the |
3428 | /// [RenderIntrinsicWidth], allowing the [RenderIntrinsicWidth]'s child's |
3429 | /// width to be smaller than that of its parent. |
3430 | /// * [The catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3431 | class IntrinsicWidth extends SingleChildRenderObjectWidget { |
3432 | /// Creates a widget that sizes its child to the child's intrinsic width. |
3433 | /// |
3434 | /// This class is relatively expensive. Avoid using it where possible. |
3435 | const IntrinsicWidth({ super.key, this.stepWidth, this.stepHeight, super.child }) |
3436 | : assert(stepWidth == null || stepWidth >= 0.0), |
3437 | assert(stepHeight == null || stepHeight >= 0.0); |
3438 | |
3439 | /// If non-null, force the child's width to be a multiple of this value. |
3440 | /// |
3441 | /// If null or 0.0 the child's width will be the same as its maximum |
3442 | /// intrinsic width. |
3443 | /// |
3444 | /// This value must not be negative. |
3445 | /// |
3446 | /// See also: |
3447 | /// |
3448 | /// * [RenderBox.getMaxIntrinsicWidth], which defines a widget's max |
3449 | /// intrinsic width in general. |
3450 | final double? stepWidth; |
3451 | |
3452 | /// If non-null, force the child's height to be a multiple of this value. |
3453 | /// |
3454 | /// If null or 0.0 the child's height will not be constrained. |
3455 | /// |
3456 | /// This value must not be negative. |
3457 | final double? stepHeight; |
3458 | |
3459 | double? get _stepWidth => stepWidth == 0.0 ? null : stepWidth; |
3460 | double? get _stepHeight => stepHeight == 0.0 ? null : stepHeight; |
3461 | |
3462 | @override |
3463 | RenderIntrinsicWidth createRenderObject(BuildContext context) { |
3464 | return RenderIntrinsicWidth(stepWidth: _stepWidth, stepHeight: _stepHeight); |
3465 | } |
3466 | |
3467 | @override |
3468 | void updateRenderObject(BuildContext context, RenderIntrinsicWidth renderObject) { |
3469 | renderObject |
3470 | ..stepWidth = _stepWidth |
3471 | ..stepHeight = _stepHeight; |
3472 | } |
3473 | } |
3474 | |
3475 | /// A widget that sizes its child to the child's intrinsic height. |
3476 | /// |
3477 | /// This class is useful, for example, when unlimited height is available and |
3478 | /// you would like a child that would otherwise attempt to expand infinitely to |
3479 | /// instead size itself to a more reasonable height. |
3480 | /// |
3481 | /// The constraints that this widget passes to its child will adhere to the |
3482 | /// parent's constraints, so if the constraints are not large enough to satisfy |
3483 | /// the child's maximum intrinsic height, then the child will get less height |
3484 | /// than it otherwise would. Likewise, if the minimum height constraint is |
3485 | /// larger than the child's maximum intrinsic height, the child will be given |
3486 | /// more height than it otherwise would. |
3487 | /// |
3488 | /// This class is relatively expensive, because it adds a speculative layout |
3489 | /// pass before the final layout phase. Avoid using it where possible. In the |
3490 | /// worst case, this widget can result in a layout that is O(N²) in the depth of |
3491 | /// the tree. |
3492 | /// |
3493 | /// See also: |
3494 | /// |
3495 | /// * [Align], a widget that aligns its child within itself. This can be used |
3496 | /// to loosen the constraints passed to the [RenderIntrinsicHeight], |
3497 | /// allowing the [RenderIntrinsicHeight]'s child to be smaller than that of |
3498 | /// its parent. |
3499 | /// * [Column], which when used with [CrossAxisAlignment.stretch] can be used |
3500 | /// to loosen just the height constraints that are passed to the |
3501 | /// [RenderIntrinsicHeight], allowing the [RenderIntrinsicHeight]'s child's |
3502 | /// height to be smaller than that of its parent. |
3503 | /// * [The catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3504 | class IntrinsicHeight extends SingleChildRenderObjectWidget { |
3505 | /// Creates a widget that sizes its child to the child's intrinsic height. |
3506 | /// |
3507 | /// This class is relatively expensive. Avoid using it where possible. |
3508 | const IntrinsicHeight({ super.key, super.child }); |
3509 | |
3510 | @override |
3511 | RenderIntrinsicHeight createRenderObject(BuildContext context) => RenderIntrinsicHeight(); |
3512 | } |
3513 | |
3514 | /// A widget that positions its child according to the child's baseline. |
3515 | /// |
3516 | /// This widget shifts the child down such that the child's baseline (or the |
3517 | /// bottom of the child, if the child has no baseline) is [baseline] |
3518 | /// logical pixels below the top of this box, then sizes this box to |
3519 | /// contain the child. If [baseline] is less than the distance from |
3520 | /// the top of the child to the baseline of the child, then the child |
3521 | /// is top-aligned instead. |
3522 | /// |
3523 | /// {@youtube 560 315 https://www.youtube.com/watch?v=8ZaFk0yvNlI} |
3524 | /// |
3525 | /// See also: |
3526 | /// |
3527 | /// * [Align], a widget that aligns its child within itself and optionally |
3528 | /// sizes itself based on the child's size. |
3529 | /// * [Center], a widget that centers its child within itself. |
3530 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3531 | class Baseline extends SingleChildRenderObjectWidget { |
3532 | /// Creates a widget that positions its child according to the child's baseline. |
3533 | const Baseline({ |
3534 | super.key, |
3535 | required this.baseline, |
3536 | required this.baselineType, |
3537 | super.child, |
3538 | }); |
3539 | |
3540 | /// The number of logical pixels from the top of this box at which to position |
3541 | /// the child's baseline. |
3542 | final double baseline; |
3543 | |
3544 | /// The type of baseline to use for positioning the child. |
3545 | final TextBaseline baselineType; |
3546 | |
3547 | @override |
3548 | RenderBaseline createRenderObject(BuildContext context) { |
3549 | return RenderBaseline(baseline: baseline, baselineType: baselineType); |
3550 | } |
3551 | |
3552 | @override |
3553 | void updateRenderObject(BuildContext context, RenderBaseline renderObject) { |
3554 | renderObject |
3555 | ..baseline = baseline |
3556 | ..baselineType = baselineType; |
3557 | } |
3558 | } |
3559 | |
3560 | /// A widget that causes the parent to ignore the [child] for the purposes |
3561 | /// of baseline alignment. |
3562 | /// |
3563 | /// See also: |
3564 | /// |
3565 | /// * [Baseline], a widget that positions a child relative to a baseline. |
3566 | class IgnoreBaseline extends SingleChildRenderObjectWidget { |
3567 | /// Creates a widget that ignores the child for baseline alignment purposes. |
3568 | const IgnoreBaseline({ |
3569 | super.key, |
3570 | super.child, |
3571 | }); |
3572 | |
3573 | @override |
3574 | RenderIgnoreBaseline createRenderObject(BuildContext context) { |
3575 | return RenderIgnoreBaseline(); |
3576 | } |
3577 | } |
3578 | |
3579 | |
3580 | // SLIVERS |
3581 | |
3582 | /// A sliver that contains a single box widget. |
3583 | /// |
3584 | /// Slivers are special-purpose widgets that can be combined using a |
3585 | /// [CustomScrollView] to create custom scroll effects. A [SliverToBoxAdapter] |
3586 | /// is a basic sliver that creates a bridge back to one of the usual box-based |
3587 | /// widgets. |
3588 | /// |
3589 | /// _To learn more about slivers, see [CustomScrollView.slivers]._ |
3590 | /// |
3591 | /// Rather than using multiple [SliverToBoxAdapter] widgets to display multiple |
3592 | /// box widgets in a [CustomScrollView], consider using [SliverList], |
3593 | /// [SliverFixedExtentList], [SliverPrototypeExtentList], or [SliverGrid], |
3594 | /// which are more efficient because they instantiate only those children that |
3595 | /// are actually visible through the scroll view's viewport. |
3596 | /// |
3597 | /// See also: |
3598 | /// |
3599 | /// * [CustomScrollView], which displays a scrollable list of slivers. |
3600 | /// * [SliverList], which displays multiple box widgets in a linear array. |
3601 | /// * [SliverFixedExtentList], which displays multiple box widgets with the |
3602 | /// same main-axis extent in a linear array. |
3603 | /// * [SliverPrototypeExtentList], which displays multiple box widgets with the |
3604 | /// same main-axis extent as a prototype item, in a linear array. |
3605 | /// * [SliverGrid], which displays multiple box widgets in arbitrary positions. |
3606 | class SliverToBoxAdapter extends SingleChildRenderObjectWidget { |
3607 | /// Creates a sliver that contains a single box widget. |
3608 | const SliverToBoxAdapter({ |
3609 | super.key, |
3610 | super.child, |
3611 | }); |
3612 | |
3613 | @override |
3614 | RenderSliverToBoxAdapter createRenderObject(BuildContext context) => RenderSliverToBoxAdapter(); |
3615 | } |
3616 | |
3617 | /// A sliver that applies padding on each side of another sliver. |
3618 | /// |
3619 | /// Slivers are special-purpose widgets that can be combined using a |
3620 | /// [CustomScrollView] to create custom scroll effects. A [SliverPadding] |
3621 | /// is a basic sliver that insets another sliver by applying padding on each |
3622 | /// side. |
3623 | /// |
3624 | /// {@macro flutter.rendering.RenderSliverEdgeInsetsPadding} |
3625 | /// |
3626 | /// See also: |
3627 | /// |
3628 | /// * [CustomScrollView], which displays a scrollable list of slivers. |
3629 | /// * [Padding], the box version of this widget. |
3630 | class SliverPadding extends SingleChildRenderObjectWidget { |
3631 | /// Creates a sliver that applies padding on each side of another sliver. |
3632 | const SliverPadding({ |
3633 | super.key, |
3634 | required this.padding, |
3635 | Widget? sliver, |
3636 | }) : super(child: sliver); |
3637 | |
3638 | /// The amount of space by which to inset the child sliver. |
3639 | final EdgeInsetsGeometry padding; |
3640 | |
3641 | @override |
3642 | RenderSliverPadding createRenderObject(BuildContext context) { |
3643 | return RenderSliverPadding( |
3644 | padding: padding, |
3645 | textDirection: Directionality.of(context), |
3646 | ); |
3647 | } |
3648 | |
3649 | @override |
3650 | void updateRenderObject(BuildContext context, RenderSliverPadding renderObject) { |
3651 | renderObject |
3652 | ..padding = padding |
3653 | ..textDirection = Directionality.of(context); |
3654 | } |
3655 | |
3656 | @override |
3657 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3658 | super.debugFillProperties(properties); |
3659 | properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding' , padding)); |
3660 | } |
3661 | } |
3662 | |
3663 | |
3664 | // LAYOUT NODES |
3665 | |
3666 | /// Returns the [AxisDirection] in the given [Axis] in the current |
3667 | /// [Directionality] (or the reverse if `reverse` is true). |
3668 | /// |
3669 | /// If `axis` is [Axis.vertical], this function returns [AxisDirection.down] |
3670 | /// unless `reverse` is true, in which case this function returns |
3671 | /// [AxisDirection.up]. |
3672 | /// |
3673 | /// If `axis` is [Axis.horizontal], this function checks the current |
3674 | /// [Directionality]. If the current [Directionality] is right-to-left, then |
3675 | /// this function returns [AxisDirection.left] (unless `reverse` is true, in |
3676 | /// which case it returns [AxisDirection.right]). Similarly, if the current |
3677 | /// [Directionality] is left-to-right, then this function returns |
3678 | /// [AxisDirection.right] (unless `reverse` is true, in which case it returns |
3679 | /// [AxisDirection.left]). |
3680 | /// |
3681 | /// This function is used by a number of scrolling widgets (e.g., [ListView], |
3682 | /// [GridView], [PageView], and [SingleChildScrollView]) as well as [ListBody] |
3683 | /// to translate their [Axis] and `reverse` properties into a concrete |
3684 | /// [AxisDirection]. |
3685 | AxisDirection getAxisDirectionFromAxisReverseAndDirectionality( |
3686 | BuildContext context, |
3687 | Axis axis, |
3688 | bool reverse, |
3689 | ) { |
3690 | switch (axis) { |
3691 | case Axis.horizontal: |
3692 | assert(debugCheckHasDirectionality(context)); |
3693 | final TextDirection textDirection = Directionality.of(context); |
3694 | final AxisDirection axisDirection = textDirectionToAxisDirection(textDirection); |
3695 | return reverse ? flipAxisDirection(axisDirection) : axisDirection; |
3696 | case Axis.vertical: |
3697 | return reverse ? AxisDirection.up : AxisDirection.down; |
3698 | } |
3699 | } |
3700 | |
3701 | /// A widget that arranges its children sequentially along a given axis, forcing |
3702 | /// them to the dimension of the parent in the other axis. |
3703 | /// |
3704 | /// This widget is rarely used directly. Instead, consider using [ListView], |
3705 | /// which combines a similar layout algorithm with scrolling behavior, or |
3706 | /// [Column], which gives you more flexible control over the layout of a |
3707 | /// vertical set of boxes. |
3708 | /// |
3709 | /// See also: |
3710 | /// |
3711 | /// * [RenderListBody], which implements this layout algorithm and the |
3712 | /// documentation for which describes some of its subtleties. |
3713 | /// * [SingleChildScrollView], which is sometimes used with [ListBody] to |
3714 | /// make the contents scrollable. |
3715 | /// * [Column] and [Row], which implement a more elaborate version of |
3716 | /// this layout algorithm (at the cost of being slightly less efficient). |
3717 | /// * [ListView], which implements an efficient scrolling version of this |
3718 | /// layout algorithm. |
3719 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3720 | class ListBody extends MultiChildRenderObjectWidget { |
3721 | /// Creates a layout widget that arranges its children sequentially along a |
3722 | /// given axis. |
3723 | /// |
3724 | /// By default, the [mainAxis] is [Axis.vertical]. |
3725 | const ListBody({ |
3726 | super.key, |
3727 | this.mainAxis = Axis.vertical, |
3728 | this.reverse = false, |
3729 | super.children, |
3730 | }); |
3731 | |
3732 | /// The direction to use as the main axis. |
3733 | final Axis mainAxis; |
3734 | |
3735 | /// Whether the list body positions children in the reading direction. |
3736 | /// |
3737 | /// For example, if the reading direction is left-to-right and |
3738 | /// [mainAxis] is [Axis.horizontal], then the list body positions children |
3739 | /// from left to right when [reverse] is false and from right to left when |
3740 | /// [reverse] is true. |
3741 | /// |
3742 | /// Similarly, if [mainAxis] is [Axis.vertical], then the list body positions |
3743 | /// from top to bottom when [reverse] is false and from bottom to top when |
3744 | /// [reverse] is true. |
3745 | /// |
3746 | /// Defaults to false. |
3747 | final bool reverse; |
3748 | |
3749 | AxisDirection _getDirection(BuildContext context) { |
3750 | return getAxisDirectionFromAxisReverseAndDirectionality(context, mainAxis, reverse); |
3751 | } |
3752 | |
3753 | @override |
3754 | RenderListBody createRenderObject(BuildContext context) { |
3755 | return RenderListBody(axisDirection: _getDirection(context)); |
3756 | } |
3757 | |
3758 | @override |
3759 | void updateRenderObject(BuildContext context, RenderListBody renderObject) { |
3760 | renderObject.axisDirection = _getDirection(context); |
3761 | } |
3762 | } |
3763 | |
3764 | /// A widget that positions its children relative to the edges of its box. |
3765 | /// |
3766 | /// This class is useful if you want to overlap several children in a simple |
3767 | /// way, for example having some text and an image, overlaid with a gradient and |
3768 | /// a button attached to the bottom. |
3769 | /// |
3770 | /// {@youtube 560 315 https://www.youtube.com/watch?v=liEGSeD3Zt8} |
3771 | /// |
3772 | /// Each child of a [Stack] widget is either _positioned_ or _non-positioned_. |
3773 | /// Positioned children are those wrapped in a [Positioned] widget that has at |
3774 | /// least one non-null property. The stack sizes itself to contain all the |
3775 | /// non-positioned children, which are positioned according to [alignment] |
3776 | /// (which defaults to the top-left corner in left-to-right environments and the |
3777 | /// top-right corner in right-to-left environments). The positioned children are |
3778 | /// then placed relative to the stack according to their top, right, bottom, and |
3779 | /// left properties. |
3780 | /// |
3781 | /// The stack paints its children in order with the first child being at the |
3782 | /// bottom. If you want to change the order in which the children paint, you |
3783 | /// can rebuild the stack with the children in the new order. If you reorder |
3784 | /// the children in this way, consider giving the children non-null keys. |
3785 | /// These keys will cause the framework to move the underlying objects for |
3786 | /// the children to their new locations rather than recreate them at their |
3787 | /// new location. |
3788 | /// |
3789 | /// For more details about the stack layout algorithm, see [RenderStack]. |
3790 | /// |
3791 | /// If you want to lay a number of children out in a particular pattern, or if |
3792 | /// you want to make a custom layout manager, you probably want to use |
3793 | /// [CustomMultiChildLayout] instead. In particular, when using a [Stack] you |
3794 | /// can't position children relative to their size or the stack's own size. |
3795 | /// |
3796 | /// {@tool snippet} |
3797 | /// |
3798 | /// Using a [Stack] you can position widgets over one another. |
3799 | /// |
3800 | /// ![The sample creates a blue box that overlaps a larger green box, which itself overlaps an even larger red box.](https://flutter.github.io/assets-for-api-docs/assets/widgets/stack.png) |
3801 | /// |
3802 | /// ```dart |
3803 | /// Stack( |
3804 | /// children: <Widget>[ |
3805 | /// Container( |
3806 | /// width: 100, |
3807 | /// height: 100, |
3808 | /// color: Colors.red, |
3809 | /// ), |
3810 | /// Container( |
3811 | /// width: 90, |
3812 | /// height: 90, |
3813 | /// color: Colors.green, |
3814 | /// ), |
3815 | /// Container( |
3816 | /// width: 80, |
3817 | /// height: 80, |
3818 | /// color: Colors.blue, |
3819 | /// ), |
3820 | /// ], |
3821 | /// ) |
3822 | /// ``` |
3823 | /// {@end-tool} |
3824 | /// |
3825 | /// {@tool snippet} |
3826 | /// |
3827 | /// This example shows how [Stack] can be used to enhance text visibility |
3828 | /// by adding gradient backdrops. |
3829 | /// |
3830 | /// ![The gradient fades from transparent to dark grey at the bottom, with white text on top of the darker portion.](https://flutter.github.io/assets-for-api-docs/assets/widgets/stack_with_gradient.png) |
3831 | /// |
3832 | /// ```dart |
3833 | /// SizedBox( |
3834 | /// width: 250, |
3835 | /// height: 250, |
3836 | /// child: Stack( |
3837 | /// children: <Widget>[ |
3838 | /// Container( |
3839 | /// width: 250, |
3840 | /// height: 250, |
3841 | /// color: Colors.white, |
3842 | /// ), |
3843 | /// Container( |
3844 | /// padding: const EdgeInsets.all(5.0), |
3845 | /// alignment: Alignment.bottomCenter, |
3846 | /// decoration: BoxDecoration( |
3847 | /// gradient: LinearGradient( |
3848 | /// begin: Alignment.topCenter, |
3849 | /// end: Alignment.bottomCenter, |
3850 | /// colors: <Color>[ |
3851 | /// Colors.black.withAlpha(0), |
3852 | /// Colors.black12, |
3853 | /// Colors.black45 |
3854 | /// ], |
3855 | /// ), |
3856 | /// ), |
3857 | /// child: const Text( |
3858 | /// 'Foreground Text', |
3859 | /// style: TextStyle(color: Colors.white, fontSize: 20.0), |
3860 | /// ), |
3861 | /// ), |
3862 | /// ], |
3863 | /// ), |
3864 | /// ) |
3865 | /// ``` |
3866 | /// {@end-tool} |
3867 | /// |
3868 | /// See also: |
3869 | /// |
3870 | /// * [Align], which sizes itself based on its child's size and positions |
3871 | /// the child according to an [Alignment] value. |
3872 | /// * [CustomSingleChildLayout], which uses a delegate to control the layout of |
3873 | /// a single child. |
3874 | /// * [CustomMultiChildLayout], which uses a delegate to position multiple |
3875 | /// children. |
3876 | /// * [Flow], which provides paint-time control of its children using transform |
3877 | /// matrices. |
3878 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
3879 | class Stack extends MultiChildRenderObjectWidget { |
3880 | /// Creates a stack layout widget. |
3881 | /// |
3882 | /// By default, the non-positioned children of the stack are aligned by their |
3883 | /// top left corners. |
3884 | const Stack({ |
3885 | super.key, |
3886 | this.alignment = AlignmentDirectional.topStart, |
3887 | this.textDirection, |
3888 | this.fit = StackFit.loose, |
3889 | this.clipBehavior = Clip.hardEdge, |
3890 | super.children, |
3891 | }); |
3892 | |
3893 | /// How to align the non-positioned and partially-positioned children in the |
3894 | /// stack. |
3895 | /// |
3896 | /// The non-positioned children are placed relative to each other such that |
3897 | /// the points determined by [alignment] are co-located. For example, if the |
3898 | /// [alignment] is [Alignment.topLeft], then the top left corner of |
3899 | /// each non-positioned child will be located at the same global coordinate. |
3900 | /// |
3901 | /// Partially-positioned children, those that do not specify an alignment in a |
3902 | /// particular axis (e.g. that have neither `top` nor `bottom` set), use the |
3903 | /// alignment to determine how they should be positioned in that |
3904 | /// under-specified axis. |
3905 | /// |
3906 | /// Defaults to [AlignmentDirectional.topStart]. |
3907 | /// |
3908 | /// See also: |
3909 | /// |
3910 | /// * [Alignment], a class with convenient constants typically used to |
3911 | /// specify an [AlignmentGeometry]. |
3912 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments |
3913 | /// relative to text direction. |
3914 | final AlignmentGeometry alignment; |
3915 | |
3916 | /// The text direction with which to resolve [alignment]. |
3917 | /// |
3918 | /// Defaults to the ambient [Directionality]. |
3919 | final TextDirection? textDirection; |
3920 | |
3921 | /// How to size the non-positioned children in the stack. |
3922 | /// |
3923 | /// The constraints passed into the [Stack] from its parent are either |
3924 | /// loosened ([StackFit.loose]) or tightened to their biggest size |
3925 | /// ([StackFit.expand]). |
3926 | final StackFit fit; |
3927 | |
3928 | /// {@macro flutter.material.Material.clipBehavior} |
3929 | /// |
3930 | /// Stacks only clip children whose _geometry_ overflows the stack. A child |
3931 | /// that paints outside its bounds (e.g. a box with a shadow) will not be |
3932 | /// clipped, regardless of the value of this property. Similarly, a child that |
3933 | /// itself has a descendant that overflows the stack will not be clipped, as |
3934 | /// only the geometry of the stack's direct children are considered. |
3935 | /// [Transform] is an example of a widget that can cause its children to paint |
3936 | /// outside its geometry. |
3937 | /// |
3938 | /// To clip children whose geometry does not overflow the stack, consider |
3939 | /// using a [ClipRect] widget. |
3940 | /// |
3941 | /// Defaults to [Clip.hardEdge]. |
3942 | final Clip clipBehavior; |
3943 | |
3944 | bool _debugCheckHasDirectionality(BuildContext context) { |
3945 | if (alignment is AlignmentDirectional && textDirection == null) { |
3946 | assert(debugCheckHasDirectionality( |
3947 | context, |
3948 | why: "to resolve the 'alignment' argument" , |
3949 | hint: alignment == AlignmentDirectional.topStart ? "The default value for 'alignment' is AlignmentDirectional.topStart, which requires a text direction." : null, |
3950 | alternative: "Instead of providing a Directionality widget, another solution would be passing a non-directional 'alignment', or an explicit 'textDirection', to the $runtimeType." , |
3951 | )); |
3952 | } |
3953 | return true; |
3954 | } |
3955 | |
3956 | @override |
3957 | RenderStack createRenderObject(BuildContext context) { |
3958 | assert(_debugCheckHasDirectionality(context)); |
3959 | return RenderStack( |
3960 | alignment: alignment, |
3961 | textDirection: textDirection ?? Directionality.maybeOf(context), |
3962 | fit: fit, |
3963 | clipBehavior: clipBehavior, |
3964 | ); |
3965 | } |
3966 | |
3967 | @override |
3968 | void updateRenderObject(BuildContext context, RenderStack renderObject) { |
3969 | assert(_debugCheckHasDirectionality(context)); |
3970 | renderObject |
3971 | ..alignment = alignment |
3972 | ..textDirection = textDirection ?? Directionality.maybeOf(context) |
3973 | ..fit = fit |
3974 | ..clipBehavior = clipBehavior; |
3975 | } |
3976 | |
3977 | @override |
3978 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
3979 | super.debugFillProperties(properties); |
3980 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment)); |
3981 | properties.add(EnumProperty<TextDirection>('textDirection' , textDirection, defaultValue: null)); |
3982 | properties.add(EnumProperty<StackFit>('fit' , fit)); |
3983 | properties.add(EnumProperty<Clip>('clipBehavior' , clipBehavior, defaultValue: Clip.hardEdge)); |
3984 | } |
3985 | } |
3986 | |
3987 | /// A [Stack] that shows a single child from a list of children. |
3988 | /// |
3989 | /// The displayed child is the one with the given [index]. The stack is |
3990 | /// always as big as the largest child. |
3991 | /// |
3992 | /// If value is null, then nothing is displayed. |
3993 | /// |
3994 | /// {@youtube 560 315 https://www.youtube.com/watch?v=_O0PPD1Xfbk} |
3995 | /// |
3996 | /// {@tool dartpad} |
3997 | /// This example shows a [IndexedStack] widget being used to lay out one card |
3998 | /// at a time from a series of cards, each keeping their respective states. |
3999 | /// |
4000 | /// ** See code in examples/api/lib/widgets/basic/indexed_stack.0.dart ** |
4001 | /// {@end-tool} |
4002 | /// |
4003 | /// See also: |
4004 | /// |
4005 | /// * [Stack], for more details about stacks. |
4006 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
4007 | class IndexedStack extends StatelessWidget { |
4008 | /// Creates a [Stack] widget that paints a single child. |
4009 | const IndexedStack({ |
4010 | super.key, |
4011 | this.alignment = AlignmentDirectional.topStart, |
4012 | this.textDirection, |
4013 | this.clipBehavior = Clip.hardEdge, |
4014 | this.sizing = StackFit.loose, |
4015 | this.index = 0, |
4016 | this.children = const <Widget>[], |
4017 | }); |
4018 | |
4019 | /// How to align the non-positioned and partially-positioned children in the |
4020 | /// stack. |
4021 | /// |
4022 | /// Defaults to [AlignmentDirectional.topStart]. |
4023 | /// |
4024 | /// See [Stack.alignment] for more information. |
4025 | final AlignmentGeometry alignment; |
4026 | |
4027 | /// The text direction with which to resolve [alignment]. |
4028 | /// |
4029 | /// Defaults to the ambient [Directionality]. |
4030 | final TextDirection? textDirection; |
4031 | |
4032 | /// {@macro flutter.material.Material.clipBehavior} |
4033 | /// |
4034 | /// Defaults to [Clip.hardEdge]. |
4035 | final Clip clipBehavior; |
4036 | |
4037 | /// How to size the non-positioned children in the stack. |
4038 | /// |
4039 | /// Defaults to [StackFit.loose]. |
4040 | /// |
4041 | /// See [Stack.fit] for more information. |
4042 | final StackFit sizing; |
4043 | |
4044 | /// The index of the child to show. |
4045 | /// |
4046 | /// If this is null, none of the children will be shown. |
4047 | final int? index; |
4048 | |
4049 | /// The child widgets of the stack. |
4050 | /// |
4051 | /// Only the child at index [index] will be shown. |
4052 | /// |
4053 | /// See [Stack.children] for more information. |
4054 | final List<Widget> children; |
4055 | |
4056 | @override |
4057 | Widget build(BuildContext context) { |
4058 | final List<Widget> wrappedChildren = List<Widget>.generate(children.length, (int i) { |
4059 | return Visibility.maintain( |
4060 | visible: i == index, |
4061 | child: children[i], |
4062 | ); |
4063 | }); |
4064 | return _RawIndexedStack( |
4065 | alignment: alignment, |
4066 | textDirection: textDirection, |
4067 | clipBehavior: clipBehavior, |
4068 | sizing: sizing, |
4069 | index: index, |
4070 | children: wrappedChildren, |
4071 | ); |
4072 | } |
4073 | } |
4074 | |
4075 | /// The render object widget that backs [IndexedStack]. |
4076 | class _RawIndexedStack extends Stack { |
4077 | /// Creates a [Stack] widget that paints a single child. |
4078 | const _RawIndexedStack({ |
4079 | super.alignment, |
4080 | super.textDirection, |
4081 | super.clipBehavior, |
4082 | StackFit sizing = StackFit.loose, |
4083 | this.index = 0, |
4084 | super.children, |
4085 | }) : super(fit: sizing); |
4086 | |
4087 | /// The index of the child to show. |
4088 | final int? index; |
4089 | |
4090 | @override |
4091 | RenderIndexedStack createRenderObject(BuildContext context) { |
4092 | assert(_debugCheckHasDirectionality(context)); |
4093 | return RenderIndexedStack( |
4094 | index: index, |
4095 | fit: fit, |
4096 | clipBehavior: clipBehavior, |
4097 | alignment: alignment, |
4098 | textDirection: textDirection ?? Directionality.maybeOf(context), |
4099 | ); |
4100 | } |
4101 | |
4102 | @override |
4103 | void updateRenderObject(BuildContext context, RenderIndexedStack renderObject) { |
4104 | assert(_debugCheckHasDirectionality(context)); |
4105 | renderObject |
4106 | ..index = index |
4107 | ..fit = fit |
4108 | ..clipBehavior = clipBehavior |
4109 | ..alignment = alignment |
4110 | ..textDirection = textDirection ?? Directionality.maybeOf(context); |
4111 | } |
4112 | |
4113 | @override |
4114 | MultiChildRenderObjectElement createElement() { |
4115 | return _IndexedStackElement(this); |
4116 | } |
4117 | } |
4118 | |
4119 | class _IndexedStackElement extends MultiChildRenderObjectElement { |
4120 | _IndexedStackElement(_RawIndexedStack super.widget); |
4121 | |
4122 | @override |
4123 | _RawIndexedStack get widget => super.widget as _RawIndexedStack; |
4124 | |
4125 | @override |
4126 | void debugVisitOnstageChildren(ElementVisitor visitor) { |
4127 | final int? index = widget.index; |
4128 | // If the index is null, no child is onstage. Otherwise, only the child at |
4129 | // the selected index is. |
4130 | if (index != null && children.isNotEmpty) { |
4131 | visitor(children.elementAt(index)); |
4132 | } |
4133 | } |
4134 | } |
4135 | |
4136 | /// A widget that controls where a child of a [Stack] is positioned. |
4137 | /// |
4138 | /// A [Positioned] widget must be a descendant of a [Stack], and the path from |
4139 | /// the [Positioned] widget to its enclosing [Stack] must contain only |
4140 | /// [StatelessWidget]s or [StatefulWidget]s (not other kinds of widgets, like |
4141 | /// [RenderObjectWidget]s). |
4142 | /// |
4143 | /// {@youtube 560 315 https://www.youtube.com/watch?v=EgtPleVwxBQ} |
4144 | /// |
4145 | /// If a widget is wrapped in a [Positioned], then it is a _positioned_ widget |
4146 | /// in its [Stack]. If the [top] property is non-null, the top edge of this child |
4147 | /// will be positioned [top] layout units from the top of the stack widget. The |
4148 | /// [right], [bottom], and [left] properties work analogously. |
4149 | /// |
4150 | /// If both the [top] and [bottom] properties are non-null, then the child will |
4151 | /// be forced to have exactly the height required to satisfy both constraints. |
4152 | /// Similarly, setting the [right] and [left] properties to non-null values will |
4153 | /// force the child to have a particular width. Alternatively the [width] and |
4154 | /// [height] properties can be used to give the dimensions, with one |
4155 | /// corresponding position property (e.g. [top] and [height]). |
4156 | /// |
4157 | /// If all three values on a particular axis are null, then the |
4158 | /// [Stack.alignment] property is used to position the child. |
4159 | /// |
4160 | /// If all six values are null, the child is a non-positioned child. The [Stack] |
4161 | /// uses only the non-positioned children to size itself. |
4162 | /// |
4163 | /// See also: |
4164 | /// |
4165 | /// * [AnimatedPositioned], which automatically transitions the child's |
4166 | /// position over a given duration whenever the given position changes. |
4167 | /// * [PositionedTransition], which takes a provided [Animation] to transition |
4168 | /// changes in the child's position over a given duration. |
4169 | /// * [PositionedDirectional], which adapts to the ambient [Directionality]. |
4170 | class Positioned extends ParentDataWidget<StackParentData> { |
4171 | /// Creates a widget that controls where a child of a [Stack] is positioned. |
4172 | /// |
4173 | /// Only two out of the three horizontal values ([left], [right], |
4174 | /// [width]), and only two out of the three vertical values ([top], |
4175 | /// [bottom], [height]), can be set. In each case, at least one of |
4176 | /// the three must be null. |
4177 | /// |
4178 | /// See also: |
4179 | /// |
4180 | /// * [Positioned.directional], which specifies the widget's horizontal |
4181 | /// position using `start` and `end` rather than `left` and `right`. |
4182 | /// * [PositionedDirectional], which is similar to [Positioned.directional] |
4183 | /// but adapts to the ambient [Directionality]. |
4184 | const Positioned({ |
4185 | super.key, |
4186 | this.left, |
4187 | this.top, |
4188 | this.right, |
4189 | this.bottom, |
4190 | this.width, |
4191 | this.height, |
4192 | required super.child, |
4193 | }) : assert(left == null || right == null || width == null), |
4194 | assert(top == null || bottom == null || height == null); |
4195 | |
4196 | /// Creates a Positioned object with the values from the given [Rect]. |
4197 | /// |
4198 | /// This sets the [left], [top], [width], and [height] properties |
4199 | /// from the given [Rect]. The [right] and [bottom] properties are |
4200 | /// set to null. |
4201 | Positioned.fromRect({ |
4202 | super.key, |
4203 | required Rect rect, |
4204 | required super.child, |
4205 | }) : left = rect.left, |
4206 | top = rect.top, |
4207 | width = rect.width, |
4208 | height = rect.height, |
4209 | right = null, |
4210 | bottom = null; |
4211 | |
4212 | /// Creates a Positioned object with the values from the given [RelativeRect]. |
4213 | /// |
4214 | /// This sets the [left], [top], [right], and [bottom] properties from the |
4215 | /// given [RelativeRect]. The [height] and [width] properties are set to null. |
4216 | Positioned.fromRelativeRect({ |
4217 | super.key, |
4218 | required RelativeRect rect, |
4219 | required super.child, |
4220 | }) : left = rect.left, |
4221 | top = rect.top, |
4222 | right = rect.right, |
4223 | bottom = rect.bottom, |
4224 | width = null, |
4225 | height = null; |
4226 | |
4227 | /// Creates a Positioned object with [left], [top], [right], and [bottom] set |
4228 | /// to 0.0 unless a value for them is passed. |
4229 | const Positioned.fill({ |
4230 | super.key, |
4231 | this.left = 0.0, |
4232 | this.top = 0.0, |
4233 | this.right = 0.0, |
4234 | this.bottom = 0.0, |
4235 | required super.child, |
4236 | }) : width = null, |
4237 | height = null; |
4238 | |
4239 | /// Creates a widget that controls where a child of a [Stack] is positioned. |
4240 | /// |
4241 | /// Only two out of the three horizontal values (`start`, `end`, |
4242 | /// [width]), and only two out of the three vertical values ([top], |
4243 | /// [bottom], [height]), can be set. In each case, at least one of |
4244 | /// the three must be null. |
4245 | /// |
4246 | /// If `textDirection` is [TextDirection.rtl], then the `start` argument is |
4247 | /// used for the [right] property and the `end` argument is used for the |
4248 | /// [left] property. Otherwise, if `textDirection` is [TextDirection.ltr], |
4249 | /// then the `start` argument is used for the [left] property and the `end` |
4250 | /// argument is used for the [right] property. |
4251 | /// |
4252 | /// See also: |
4253 | /// |
4254 | /// * [PositionedDirectional], which adapts to the ambient [Directionality]. |
4255 | factory Positioned.directional({ |
4256 | Key? key, |
4257 | required TextDirection textDirection, |
4258 | double? start, |
4259 | double? top, |
4260 | double? end, |
4261 | double? bottom, |
4262 | double? width, |
4263 | double? height, |
4264 | required Widget child, |
4265 | }) { |
4266 | double? left; |
4267 | double? right; |
4268 | switch (textDirection) { |
4269 | case TextDirection.rtl: |
4270 | left = end; |
4271 | right = start; |
4272 | case TextDirection.ltr: |
4273 | left = start; |
4274 | right = end; |
4275 | } |
4276 | return Positioned( |
4277 | key: key, |
4278 | left: left, |
4279 | top: top, |
4280 | right: right, |
4281 | bottom: bottom, |
4282 | width: width, |
4283 | height: height, |
4284 | child: child, |
4285 | ); |
4286 | } |
4287 | |
4288 | /// The distance that the child's left edge is inset from the left of the stack. |
4289 | /// |
4290 | /// Only two out of the three horizontal values ([left], [right], [width]) can be |
4291 | /// set. The third must be null. |
4292 | /// |
4293 | /// If all three are null, the [Stack.alignment] is used to position the child |
4294 | /// horizontally. |
4295 | final double? left; |
4296 | |
4297 | /// The distance that the child's top edge is inset from the top of the stack. |
4298 | /// |
4299 | /// Only two out of the three vertical values ([top], [bottom], [height]) can be |
4300 | /// set. The third must be null. |
4301 | /// |
4302 | /// If all three are null, the [Stack.alignment] is used to position the child |
4303 | /// vertically. |
4304 | final double? top; |
4305 | |
4306 | /// The distance that the child's right edge is inset from the right of the stack. |
4307 | /// |
4308 | /// Only two out of the three horizontal values ([left], [right], [width]) can be |
4309 | /// set. The third must be null. |
4310 | /// |
4311 | /// If all three are null, the [Stack.alignment] is used to position the child |
4312 | /// horizontally. |
4313 | final double? right; |
4314 | |
4315 | /// The distance that the child's bottom edge is inset from the bottom of the stack. |
4316 | /// |
4317 | /// Only two out of the three vertical values ([top], [bottom], [height]) can be |
4318 | /// set. The third must be null. |
4319 | /// |
4320 | /// If all three are null, the [Stack.alignment] is used to position the child |
4321 | /// vertically. |
4322 | final double? bottom; |
4323 | |
4324 | /// The child's width. |
4325 | /// |
4326 | /// Only two out of the three horizontal values ([left], [right], [width]) can be |
4327 | /// set. The third must be null. |
4328 | /// |
4329 | /// If all three are null, the [Stack.alignment] is used to position the child |
4330 | /// horizontally. |
4331 | final double? width; |
4332 | |
4333 | /// The child's height. |
4334 | /// |
4335 | /// Only two out of the three vertical values ([top], [bottom], [height]) can be |
4336 | /// set. The third must be null. |
4337 | /// |
4338 | /// If all three are null, the [Stack.alignment] is used to position the child |
4339 | /// vertically. |
4340 | final double? height; |
4341 | |
4342 | @override |
4343 | void applyParentData(RenderObject renderObject) { |
4344 | assert(renderObject.parentData is StackParentData); |
4345 | final StackParentData parentData = renderObject.parentData! as StackParentData; |
4346 | bool needsLayout = false; |
4347 | |
4348 | if (parentData.left != left) { |
4349 | parentData.left = left; |
4350 | needsLayout = true; |
4351 | } |
4352 | |
4353 | if (parentData.top != top) { |
4354 | parentData.top = top; |
4355 | needsLayout = true; |
4356 | } |
4357 | |
4358 | if (parentData.right != right) { |
4359 | parentData.right = right; |
4360 | needsLayout = true; |
4361 | } |
4362 | |
4363 | if (parentData.bottom != bottom) { |
4364 | parentData.bottom = bottom; |
4365 | needsLayout = true; |
4366 | } |
4367 | |
4368 | if (parentData.width != width) { |
4369 | parentData.width = width; |
4370 | needsLayout = true; |
4371 | } |
4372 | |
4373 | if (parentData.height != height) { |
4374 | parentData.height = height; |
4375 | needsLayout = true; |
4376 | } |
4377 | |
4378 | if (needsLayout) { |
4379 | final RenderObject? targetParent = renderObject.parent; |
4380 | if (targetParent is RenderObject) { |
4381 | targetParent.markNeedsLayout(); |
4382 | } |
4383 | } |
4384 | } |
4385 | |
4386 | @override |
4387 | Type get debugTypicalAncestorWidgetClass => Stack; |
4388 | |
4389 | @override |
4390 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
4391 | super.debugFillProperties(properties); |
4392 | properties.add(DoubleProperty('left' , left, defaultValue: null)); |
4393 | properties.add(DoubleProperty('top' , top, defaultValue: null)); |
4394 | properties.add(DoubleProperty('right' , right, defaultValue: null)); |
4395 | properties.add(DoubleProperty('bottom' , bottom, defaultValue: null)); |
4396 | properties.add(DoubleProperty('width' , width, defaultValue: null)); |
4397 | properties.add(DoubleProperty('height' , height, defaultValue: null)); |
4398 | } |
4399 | } |
4400 | |
4401 | /// A widget that controls where a child of a [Stack] is positioned without |
4402 | /// committing to a specific [TextDirection]. |
4403 | /// |
4404 | /// The ambient [Directionality] is used to determine whether [start] is to the |
4405 | /// left or to the right. |
4406 | /// |
4407 | /// A [PositionedDirectional] widget must be a descendant of a [Stack], and the |
4408 | /// path from the [PositionedDirectional] widget to its enclosing [Stack] must |
4409 | /// contain only [StatelessWidget]s or [StatefulWidget]s (not other kinds of |
4410 | /// widgets, like [RenderObjectWidget]s). |
4411 | /// |
4412 | /// If a widget is wrapped in a [PositionedDirectional], then it is a |
4413 | /// _positioned_ widget in its [Stack]. If the [top] property is non-null, the |
4414 | /// top edge of this child/ will be positioned [top] layout units from the top |
4415 | /// of the stack widget. The [start], [bottom], and [end] properties work |
4416 | /// analogously. |
4417 | /// |
4418 | /// If both the [top] and [bottom] properties are non-null, then the child will |
4419 | /// be forced to have exactly the height required to satisfy both constraints. |
4420 | /// Similarly, setting the [start] and [end] properties to non-null values will |
4421 | /// force the child to have a particular width. Alternatively the [width] and |
4422 | /// [height] properties can be used to give the dimensions, with one |
4423 | /// corresponding position property (e.g. [top] and [height]). |
4424 | /// |
4425 | /// See also: |
4426 | /// |
4427 | /// * [Positioned], which specifies the widget's position visually. |
4428 | /// * [Positioned.directional], which also specifies the widget's horizontal |
4429 | /// position using [start] and [end] but has an explicit [TextDirection]. |
4430 | /// * [AnimatedPositionedDirectional], which automatically transitions |
4431 | /// the child's position over a given duration whenever the given position |
4432 | /// changes. |
4433 | class PositionedDirectional extends StatelessWidget { |
4434 | /// Creates a widget that controls where a child of a [Stack] is positioned. |
4435 | /// |
4436 | /// Only two out of the three horizontal values (`start`, `end`, |
4437 | /// [width]), and only two out of the three vertical values ([top], |
4438 | /// [bottom], [height]), can be set. In each case, at least one of |
4439 | /// the three must be null. |
4440 | /// |
4441 | /// See also: |
4442 | /// |
4443 | /// * [Positioned.directional], which also specifies the widget's horizontal |
4444 | /// position using [start] and [end] but has an explicit [TextDirection]. |
4445 | const PositionedDirectional({ |
4446 | super.key, |
4447 | this.start, |
4448 | this.top, |
4449 | this.end, |
4450 | this.bottom, |
4451 | this.width, |
4452 | this.height, |
4453 | required this.child, |
4454 | }); |
4455 | |
4456 | /// The distance that the child's leading edge is inset from the leading edge |
4457 | /// of the stack. |
4458 | /// |
4459 | /// Only two out of the three horizontal values ([start], [end], [width]) can be |
4460 | /// set. The third must be null. |
4461 | final double? start; |
4462 | |
4463 | /// The distance that the child's top edge is inset from the top of the stack. |
4464 | /// |
4465 | /// Only two out of the three vertical values ([top], [bottom], [height]) can be |
4466 | /// set. The third must be null. |
4467 | final double? top; |
4468 | |
4469 | /// The distance that the child's trailing edge is inset from the trailing |
4470 | /// edge of the stack. |
4471 | /// |
4472 | /// Only two out of the three horizontal values ([start], [end], [width]) can be |
4473 | /// set. The third must be null. |
4474 | final double? end; |
4475 | |
4476 | /// The distance that the child's bottom edge is inset from the bottom of the stack. |
4477 | /// |
4478 | /// Only two out of the three vertical values ([top], [bottom], [height]) can be |
4479 | /// set. The third must be null. |
4480 | final double? bottom; |
4481 | |
4482 | /// The child's width. |
4483 | /// |
4484 | /// Only two out of the three horizontal values ([start], [end], [width]) can be |
4485 | /// set. The third must be null. |
4486 | final double? width; |
4487 | |
4488 | /// The child's height. |
4489 | /// |
4490 | /// Only two out of the three vertical values ([top], [bottom], [height]) can be |
4491 | /// set. The third must be null. |
4492 | final double? height; |
4493 | |
4494 | /// The widget below this widget in the tree. |
4495 | /// |
4496 | /// {@macro flutter.widgets.ProxyWidget.child} |
4497 | final Widget child; |
4498 | |
4499 | @override |
4500 | Widget build(BuildContext context) { |
4501 | return Positioned.directional( |
4502 | textDirection: Directionality.of(context), |
4503 | start: start, |
4504 | top: top, |
4505 | end: end, |
4506 | bottom: bottom, |
4507 | width: width, |
4508 | height: height, |
4509 | child: child, |
4510 | ); |
4511 | } |
4512 | } |
4513 | |
4514 | /// A widget that displays its children in a one-dimensional array. |
4515 | /// |
4516 | /// The [Flex] widget allows you to control the axis along which the children are |
4517 | /// placed (horizontal or vertical). This is referred to as the _main axis_. If |
4518 | /// you know the main axis in advance, then consider using a [Row] (if it's |
4519 | /// horizontal) or [Column] (if it's vertical) instead, because that will be less |
4520 | /// verbose. |
4521 | /// |
4522 | /// To cause a child to expand to fill the available space in the [direction] |
4523 | /// of this widget's main axis, wrap the child in an [Expanded] widget. |
4524 | /// |
4525 | /// The [Flex] widget does not scroll (and in general it is considered an error |
4526 | /// to have more children in a [Flex] than will fit in the available room). If |
4527 | /// you have some widgets and want them to be able to scroll if there is |
4528 | /// insufficient room, consider using a [ListView]. |
4529 | /// |
4530 | /// The [Flex] widget does not allow its children to wrap across multiple |
4531 | /// horizontal or vertical runs. For a widget that allows its children to wrap, |
4532 | /// consider using the [Wrap] widget instead of [Flex]. |
4533 | /// |
4534 | /// If you only have one child, then rather than using [Flex], [Row], or |
4535 | /// [Column], consider using [Align] or [Center] to position the child. |
4536 | /// |
4537 | /// ## Layout algorithm |
4538 | /// |
4539 | /// _This section describes how a [Flex] is rendered by the framework._ |
4540 | /// _See [BoxConstraints] for an introduction to box layout models._ |
4541 | /// |
4542 | /// Layout for a [Flex] proceeds in six steps: |
4543 | /// |
4544 | /// 1. Layout each child with a null or zero flex factor (e.g., those that are |
4545 | /// not [Expanded]) with unbounded main axis constraints and the incoming |
4546 | /// cross axis constraints. If the [crossAxisAlignment] is |
4547 | /// [CrossAxisAlignment.stretch], instead use tight cross axis constraints |
4548 | /// that match the incoming max extent in the cross axis. |
4549 | /// 2. Divide the remaining main axis space among the children with non-zero |
4550 | /// flex factors (e.g., those that are [Expanded]) according to their flex |
4551 | /// factor. For example, a child with a flex factor of 2.0 will receive twice |
4552 | /// the amount of main axis space as a child with a flex factor of 1.0. |
4553 | /// 3. Layout each of the remaining children with the same cross axis |
4554 | /// constraints as in step 1, but instead of using unbounded main axis |
4555 | /// constraints, use max axis constraints based on the amount of space |
4556 | /// allocated in step 2. Children with [Flexible.fit] properties that are |
4557 | /// [FlexFit.tight] are given tight constraints (i.e., forced to fill the |
4558 | /// allocated space), and children with [Flexible.fit] properties that are |
4559 | /// [FlexFit.loose] are given loose constraints (i.e., not forced to fill the |
4560 | /// allocated space). |
4561 | /// 4. The cross axis extent of the [Flex] is the maximum cross axis extent of |
4562 | /// the children (which will always satisfy the incoming constraints). |
4563 | /// 5. The main axis extent of the [Flex] is determined by the [mainAxisSize] |
4564 | /// property. If the [mainAxisSize] property is [MainAxisSize.max], then the |
4565 | /// main axis extent of the [Flex] is the max extent of the incoming main |
4566 | /// axis constraints. If the [mainAxisSize] property is [MainAxisSize.min], |
4567 | /// then the main axis extent of the [Flex] is the sum of the main axis |
4568 | /// extents of the children (subject to the incoming constraints). |
4569 | /// 6. Determine the position for each child according to the |
4570 | /// [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the |
4571 | /// [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any main axis |
4572 | /// space that has not been allocated to children is divided evenly and |
4573 | /// placed between the children. |
4574 | /// |
4575 | /// See also: |
4576 | /// |
4577 | /// * [Row], for a version of this widget that is always horizontal. |
4578 | /// * [Column], for a version of this widget that is always vertical. |
4579 | /// * [Expanded], to indicate children that should take all the remaining room. |
4580 | /// * [Flexible], to indicate children that should share the remaining room. |
4581 | /// * [Spacer], a widget that takes up space proportional to its flex value. |
4582 | /// that may be sized smaller (leaving some remaining room unused). |
4583 | /// * [Wrap], for a widget that allows its children to wrap over multiple _runs_. |
4584 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
4585 | class Flex extends MultiChildRenderObjectWidget { |
4586 | /// Creates a flex layout. |
4587 | /// |
4588 | /// The [direction] is required. |
4589 | /// |
4590 | /// If [crossAxisAlignment] is [CrossAxisAlignment.baseline], then |
4591 | /// [textBaseline] must not be null. |
4592 | /// |
4593 | /// The [textDirection] argument defaults to the ambient [Directionality], if |
4594 | /// any. If there is no ambient directionality, and a text direction is going |
4595 | /// to be necessary to decide which direction to lay the children in or to |
4596 | /// disambiguate `start` or `end` values for the main or cross axis |
4597 | /// directions, the [textDirection] must not be null. |
4598 | const Flex({ |
4599 | super.key, |
4600 | required this.direction, |
4601 | this.mainAxisAlignment = MainAxisAlignment.start, |
4602 | this.mainAxisSize = MainAxisSize.max, |
4603 | this.crossAxisAlignment = CrossAxisAlignment.center, |
4604 | this.textDirection, |
4605 | this.verticalDirection = VerticalDirection.down, |
4606 | this.textBaseline, // NO DEFAULT: we don't know what the text's baseline should be |
4607 | this.clipBehavior = Clip.none, |
4608 | super.children, |
4609 | }) : assert(!identical(crossAxisAlignment, CrossAxisAlignment.baseline) || textBaseline != null, 'textBaseline is required if you specify the crossAxisAlignment with CrossAxisAlignment.baseline' ); |
4610 | // Cannot use == in the assert above instead of identical because of https://github.com/dart-lang/language/issues/1811. |
4611 | |
4612 | /// The direction to use as the main axis. |
4613 | /// |
4614 | /// If you know the axis in advance, then consider using a [Row] (if it's |
4615 | /// horizontal) or [Column] (if it's vertical) instead of a [Flex], since that |
4616 | /// will be less verbose. (For [Row] and [Column] this property is fixed to |
4617 | /// the appropriate axis.) |
4618 | final Axis direction; |
4619 | |
4620 | /// How the children should be placed along the main axis. |
4621 | /// |
4622 | /// For example, [MainAxisAlignment.start], the default, places the children |
4623 | /// at the start (i.e., the left for a [Row] or the top for a [Column]) of the |
4624 | /// main axis. |
4625 | final MainAxisAlignment mainAxisAlignment; |
4626 | |
4627 | /// How much space should be occupied in the main axis. |
4628 | /// |
4629 | /// After allocating space to children, there might be some remaining free |
4630 | /// space. This value controls whether to maximize or minimize the amount of |
4631 | /// free space, subject to the incoming layout constraints. |
4632 | /// |
4633 | /// If some children have a non-zero flex factors (and none have a fit of |
4634 | /// [FlexFit.loose]), they will expand to consume all the available space and |
4635 | /// there will be no remaining free space to maximize or minimize, making this |
4636 | /// value irrelevant to the final layout. |
4637 | final MainAxisSize mainAxisSize; |
4638 | |
4639 | /// How the children should be placed along the cross axis. |
4640 | /// |
4641 | /// For example, [CrossAxisAlignment.center], the default, centers the |
4642 | /// children in the cross axis (e.g., horizontally for a [Column]). |
4643 | final CrossAxisAlignment crossAxisAlignment; |
4644 | |
4645 | /// Determines the order to lay children out horizontally and how to interpret |
4646 | /// `start` and `end` in the horizontal direction. |
4647 | /// |
4648 | /// Defaults to the ambient [Directionality]. |
4649 | /// |
4650 | /// If [textDirection] is [TextDirection.rtl], then the direction in which |
4651 | /// text flows starts from right to left. Otherwise, if [textDirection] is |
4652 | /// [TextDirection.ltr], then the direction in which text flows starts from |
4653 | /// left to right. |
4654 | /// |
4655 | /// If the [direction] is [Axis.horizontal], this controls the order in which |
4656 | /// the children are positioned (left-to-right or right-to-left), and the |
4657 | /// meaning of the [mainAxisAlignment] property's [MainAxisAlignment.start] and |
4658 | /// [MainAxisAlignment.end] values. |
4659 | /// |
4660 | /// If the [direction] is [Axis.horizontal], and either the |
4661 | /// [mainAxisAlignment] is either [MainAxisAlignment.start] or |
4662 | /// [MainAxisAlignment.end], or there's more than one child, then the |
4663 | /// [textDirection] (or the ambient [Directionality]) must not be null. |
4664 | /// |
4665 | /// If the [direction] is [Axis.vertical], this controls the meaning of the |
4666 | /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and |
4667 | /// [CrossAxisAlignment.end] values. |
4668 | /// |
4669 | /// If the [direction] is [Axis.vertical], and the [crossAxisAlignment] is |
4670 | /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the |
4671 | /// [textDirection] (or the ambient [Directionality]) must not be null. |
4672 | final TextDirection? textDirection; |
4673 | |
4674 | /// Determines the order to lay children out vertically and how to interpret |
4675 | /// `start` and `end` in the vertical direction. |
4676 | /// |
4677 | /// Defaults to [VerticalDirection.down]. |
4678 | /// |
4679 | /// If the [direction] is [Axis.vertical], this controls which order children |
4680 | /// are painted in (down or up), the meaning of the [mainAxisAlignment] |
4681 | /// property's [MainAxisAlignment.start] and [MainAxisAlignment.end] values. |
4682 | /// |
4683 | /// If the [direction] is [Axis.vertical], and either the [mainAxisAlignment] |
4684 | /// is either [MainAxisAlignment.start] or [MainAxisAlignment.end], or there's |
4685 | /// more than one child, then the [verticalDirection] must not be null. |
4686 | /// |
4687 | /// If the [direction] is [Axis.horizontal], this controls the meaning of the |
4688 | /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and |
4689 | /// [CrossAxisAlignment.end] values. |
4690 | /// |
4691 | /// If the [direction] is [Axis.horizontal], and the [crossAxisAlignment] is |
4692 | /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the |
4693 | /// [verticalDirection] must not be null. |
4694 | final VerticalDirection verticalDirection; |
4695 | |
4696 | /// If aligning items according to their baseline, which baseline to use. |
4697 | /// |
4698 | /// This must be set if using baseline alignment. There is no default because there is no |
4699 | /// way for the framework to know the correct baseline _a priori_. |
4700 | final TextBaseline? textBaseline; |
4701 | |
4702 | /// {@macro flutter.material.Material.clipBehavior} |
4703 | /// |
4704 | /// Defaults to [Clip.none]. |
4705 | final Clip clipBehavior; |
4706 | |
4707 | bool get _needTextDirection { |
4708 | switch (direction) { |
4709 | case Axis.horizontal: |
4710 | return true; // because it affects the layout order. |
4711 | case Axis.vertical: |
4712 | return crossAxisAlignment == CrossAxisAlignment.start |
4713 | || crossAxisAlignment == CrossAxisAlignment.end; |
4714 | } |
4715 | } |
4716 | |
4717 | /// The value to pass to [RenderFlex.textDirection]. |
4718 | /// |
4719 | /// This value is derived from the [textDirection] property and the ambient |
4720 | /// [Directionality]. The value is null if there is no need to specify the |
4721 | /// text direction. In practice there's always a need to specify the direction |
4722 | /// except for vertical flexes (e.g. [Column]s) whose [crossAxisAlignment] is |
4723 | /// not dependent on the text direction (not `start` or `end`). In particular, |
4724 | /// a [Row] always needs a text direction because the text direction controls |
4725 | /// its layout order. (For [Column]s, the layout order is controlled by |
4726 | /// [verticalDirection], which is always specified as it does not depend on an |
4727 | /// inherited widget and defaults to [VerticalDirection.down].) |
4728 | /// |
4729 | /// This method exists so that subclasses of [Flex] that create their own |
4730 | /// render objects that are derived from [RenderFlex] can do so and still use |
4731 | /// the logic for providing a text direction only when it is necessary. |
4732 | @protected |
4733 | TextDirection? getEffectiveTextDirection(BuildContext context) { |
4734 | return textDirection ?? (_needTextDirection ? Directionality.maybeOf(context) : null); |
4735 | } |
4736 | |
4737 | @override |
4738 | RenderFlex createRenderObject(BuildContext context) { |
4739 | return RenderFlex( |
4740 | direction: direction, |
4741 | mainAxisAlignment: mainAxisAlignment, |
4742 | mainAxisSize: mainAxisSize, |
4743 | crossAxisAlignment: crossAxisAlignment, |
4744 | textDirection: getEffectiveTextDirection(context), |
4745 | verticalDirection: verticalDirection, |
4746 | textBaseline: textBaseline, |
4747 | clipBehavior: clipBehavior, |
4748 | ); |
4749 | } |
4750 | |
4751 | @override |
4752 | void updateRenderObject(BuildContext context, covariant RenderFlex renderObject) { |
4753 | renderObject |
4754 | ..direction = direction |
4755 | ..mainAxisAlignment = mainAxisAlignment |
4756 | ..mainAxisSize = mainAxisSize |
4757 | ..crossAxisAlignment = crossAxisAlignment |
4758 | ..textDirection = getEffectiveTextDirection(context) |
4759 | ..verticalDirection = verticalDirection |
4760 | ..textBaseline = textBaseline |
4761 | ..clipBehavior = clipBehavior; |
4762 | } |
4763 | |
4764 | @override |
4765 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
4766 | super.debugFillProperties(properties); |
4767 | properties.add(EnumProperty<Axis>('direction' , direction)); |
4768 | properties.add(EnumProperty<MainAxisAlignment>('mainAxisAlignment' , mainAxisAlignment)); |
4769 | properties.add(EnumProperty<MainAxisSize>('mainAxisSize' , mainAxisSize, defaultValue: MainAxisSize.max)); |
4770 | properties.add(EnumProperty<CrossAxisAlignment>('crossAxisAlignment' , crossAxisAlignment)); |
4771 | properties.add(EnumProperty<TextDirection>('textDirection' , textDirection, defaultValue: null)); |
4772 | properties.add(EnumProperty<VerticalDirection>('verticalDirection' , verticalDirection, defaultValue: VerticalDirection.down)); |
4773 | properties.add(EnumProperty<TextBaseline>('textBaseline' , textBaseline, defaultValue: null)); |
4774 | } |
4775 | } |
4776 | |
4777 | /// A widget that displays its children in a horizontal array. |
4778 | /// |
4779 | /// To cause a child to expand to fill the available horizontal space, wrap the |
4780 | /// child in an [Expanded] widget. |
4781 | /// |
4782 | /// The [Row] widget does not scroll (and in general it is considered an error |
4783 | /// to have more children in a [Row] than will fit in the available room). If |
4784 | /// you have a line of widgets and want them to be able to scroll if there is |
4785 | /// insufficient room, consider using a [ListView]. |
4786 | /// |
4787 | /// For a vertical variant, see [Column]. |
4788 | /// |
4789 | /// If you only have one child, then consider using [Align] or [Center] to |
4790 | /// position the child. |
4791 | /// |
4792 | /// {@tool snippet} |
4793 | /// |
4794 | /// This example divides the available space into three (horizontally), and |
4795 | /// places text centered in the first two cells and the Flutter logo centered in |
4796 | /// the third: |
4797 | /// |
4798 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/row.png) |
4799 | /// |
4800 | /// ```dart |
4801 | /// const Row( |
4802 | /// children: <Widget>[ |
4803 | /// Expanded( |
4804 | /// child: Text('Deliver features faster', textAlign: TextAlign.center), |
4805 | /// ), |
4806 | /// Expanded( |
4807 | /// child: Text('Craft beautiful UIs', textAlign: TextAlign.center), |
4808 | /// ), |
4809 | /// Expanded( |
4810 | /// child: FittedBox( |
4811 | /// child: FlutterLogo(), |
4812 | /// ), |
4813 | /// ), |
4814 | /// ], |
4815 | /// ) |
4816 | /// ``` |
4817 | /// {@end-tool} |
4818 | /// |
4819 | /// ## Troubleshooting |
4820 | /// |
4821 | /// ### Why does my row have a yellow and black warning stripe? |
4822 | /// |
4823 | /// If the non-flexible contents of the row (those that are not wrapped in |
4824 | /// [Expanded] or [Flexible] widgets) are together wider than the row itself, |
4825 | /// then the row is said to have overflowed. When a row overflows, the row does |
4826 | /// not have any remaining space to share between its [Expanded] and [Flexible] |
4827 | /// children. The row reports this by drawing a yellow and black striped |
4828 | /// warning box on the edge that is overflowing. If there is room on the outside |
4829 | /// of the row, the amount of overflow is printed in red lettering. |
4830 | /// |
4831 | /// #### Story time |
4832 | /// |
4833 | /// Suppose, for instance, that you had this code: |
4834 | /// |
4835 | /// ```dart |
4836 | /// const Row( |
4837 | /// children: <Widget>[ |
4838 | /// FlutterLogo(), |
4839 | /// Text("Flutter's hot reload helps you quickly and easily experiment, build UIs, add features, and fix bug faster. Experience sub-second reload times, without losing state, on emulators, simulators, and hardware for iOS and Android."), |
4840 | /// Icon(Icons.sentiment_very_satisfied), |
4841 | /// ], |
4842 | /// ) |
4843 | /// ``` |
4844 | /// |
4845 | /// The row first asks its first child, the [FlutterLogo], to lay out, at |
4846 | /// whatever size the logo would like. The logo is friendly and happily decides |
4847 | /// to be 24 pixels to a side. This leaves lots of room for the next child. The |
4848 | /// row then asks that next child, the text, to lay out, at whatever size it |
4849 | /// thinks is best. |
4850 | /// |
4851 | /// At this point, the text, not knowing how wide is too wide, says "Ok, I will |
4852 | /// be thiiiiiiiiiiiiiiiiiiiis wide.", and goes well beyond the space that the |
4853 | /// row has available, not wrapping. The row responds, "That's not fair, now I |
4854 | /// have no more room available for my other children!", and gets angry and |
4855 | /// sprouts a yellow and black strip. |
4856 | /// |
4857 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/row_error.png) |
4858 | /// |
4859 | /// The fix is to wrap the second child in an [Expanded] widget, which tells the |
4860 | /// row that the child should be given the remaining room: |
4861 | /// |
4862 | /// ```dart |
4863 | /// const Row( |
4864 | /// children: <Widget>[ |
4865 | /// FlutterLogo(), |
4866 | /// Expanded( |
4867 | /// child: Text("Flutter's hot reload helps you quickly and easily experiment, build UIs, add features, and fix bug faster. Experience sub-second reload times, without losing state, on emulators, simulators, and hardware for iOS and Android."), |
4868 | /// ), |
4869 | /// Icon(Icons.sentiment_very_satisfied), |
4870 | /// ], |
4871 | /// ) |
4872 | /// ``` |
4873 | /// |
4874 | /// Now, the row first asks the logo to lay out, and then asks the _icon_ to lay |
4875 | /// out. The [Icon], like the logo, is happy to take on a reasonable size (also |
4876 | /// 24 pixels, not coincidentally, since both [FlutterLogo] and [Icon] honor the |
4877 | /// ambient [IconTheme]). This leaves some room left over, and now the row tells |
4878 | /// the text exactly how wide to be: the exact width of the remaining space. The |
4879 | /// text, now happy to comply to a reasonable request, wraps the text within |
4880 | /// that width, and you end up with a paragraph split over several lines. |
4881 | /// |
4882 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/row_fixed.png) |
4883 | /// |
4884 | /// The [textDirection] property controls the direction that children are rendered in. |
4885 | /// [TextDirection.ltr] is the default [textDirection] of [Row] children, so the first |
4886 | /// child is rendered at the `start` of the [Row], to the left, with subsequent children |
4887 | /// following to the right. If you want to order children in the opposite |
4888 | /// direction (right to left), then [textDirection] can be set to |
4889 | /// [TextDirection.rtl]. This is shown in the example below |
4890 | /// |
4891 | /// ```dart |
4892 | /// const Row( |
4893 | /// textDirection: TextDirection.rtl, |
4894 | /// children: <Widget>[ |
4895 | /// FlutterLogo(), |
4896 | /// Expanded( |
4897 | /// child: Text("Flutter's hot reload helps you quickly and easily experiment, build UIs, add features, and fix bug faster. Experience sub-second reload times, without losing state, on emulators, simulators, and hardware for iOS and Android."), |
4898 | /// ), |
4899 | /// Icon(Icons.sentiment_very_satisfied), |
4900 | /// ], |
4901 | /// ) |
4902 | /// ``` |
4903 | /// |
4904 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/row_textDirection.png) |
4905 | /// |
4906 | /// ## Layout algorithm |
4907 | /// |
4908 | /// _This section describes how a [Row] is rendered by the framework._ |
4909 | /// _See [BoxConstraints] for an introduction to box layout models._ |
4910 | /// |
4911 | /// Layout for a [Row] proceeds in six steps: |
4912 | /// |
4913 | /// 1. Layout each child with a null or zero flex factor (e.g., those that are |
4914 | /// not [Expanded]) with unbounded horizontal constraints and the incoming |
4915 | /// vertical constraints. If the [crossAxisAlignment] is |
4916 | /// [CrossAxisAlignment.stretch], instead use tight vertical constraints that |
4917 | /// match the incoming max height. |
4918 | /// 2. Divide the remaining horizontal space among the children with non-zero |
4919 | /// flex factors (e.g., those that are [Expanded]) according to their flex |
4920 | /// factor. For example, a child with a flex factor of 2.0 will receive twice |
4921 | /// the amount of horizontal space as a child with a flex factor of 1.0. |
4922 | /// 3. Layout each of the remaining children with the same vertical constraints |
4923 | /// as in step 1, but instead of using unbounded horizontal constraints, use |
4924 | /// horizontal constraints based on the amount of space allocated in step 2. |
4925 | /// Children with [Flexible.fit] properties that are [FlexFit.tight] are |
4926 | /// given tight constraints (i.e., forced to fill the allocated space), and |
4927 | /// children with [Flexible.fit] properties that are [FlexFit.loose] are |
4928 | /// given loose constraints (i.e., not forced to fill the allocated space). |
4929 | /// 4. The height of the [Row] is the maximum height of the children (which will |
4930 | /// always satisfy the incoming vertical constraints). |
4931 | /// 5. The width of the [Row] is determined by the [mainAxisSize] property. If |
4932 | /// the [mainAxisSize] property is [MainAxisSize.max], then the width of the |
4933 | /// [Row] is the max width of the incoming constraints. If the [mainAxisSize] |
4934 | /// property is [MainAxisSize.min], then the width of the [Row] is the sum |
4935 | /// of widths of the children (subject to the incoming constraints). |
4936 | /// 6. Determine the position for each child according to the |
4937 | /// [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the |
4938 | /// [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any horizontal |
4939 | /// space that has not been allocated to children is divided evenly and |
4940 | /// placed between the children. |
4941 | /// |
4942 | /// See also: |
4943 | /// |
4944 | /// * [Column], for a vertical equivalent. |
4945 | /// * [Flex], if you don't know in advance if you want a horizontal or vertical |
4946 | /// arrangement. |
4947 | /// * [Expanded], to indicate children that should take all the remaining room. |
4948 | /// * [Flexible], to indicate children that should share the remaining room but |
4949 | /// that may by sized smaller (leaving some remaining room unused). |
4950 | /// * [Spacer], a widget that takes up space proportional to its flex value. |
4951 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
4952 | class Row extends Flex { |
4953 | /// Creates a horizontal array of children. |
4954 | /// |
4955 | /// If [crossAxisAlignment] is [CrossAxisAlignment.baseline], then |
4956 | /// [textBaseline] must not be null. |
4957 | /// |
4958 | /// The [textDirection] argument defaults to the ambient [Directionality], if |
4959 | /// any. If there is no ambient directionality, and a text direction is going |
4960 | /// to be necessary to determine the layout order (which is always the case |
4961 | /// unless the row has no children or only one child) or to disambiguate |
4962 | /// `start` or `end` values for the [mainAxisAlignment], the [textDirection] |
4963 | /// must not be null. |
4964 | const Row({ |
4965 | super.key, |
4966 | super.mainAxisAlignment, |
4967 | super.mainAxisSize, |
4968 | super.crossAxisAlignment, |
4969 | super.textDirection, |
4970 | super.verticalDirection, |
4971 | super.textBaseline, // NO DEFAULT: we don't know what the text's baseline should be |
4972 | super.children, |
4973 | }) : super( |
4974 | direction: Axis.horizontal, |
4975 | ); |
4976 | } |
4977 | |
4978 | /// A widget that displays its children in a vertical array. |
4979 | /// |
4980 | /// To cause a child to expand to fill the available vertical space, wrap the |
4981 | /// child in an [Expanded] widget. |
4982 | /// |
4983 | /// The [Column] widget does not scroll (and in general it is considered an error |
4984 | /// to have more children in a [Column] than will fit in the available room). If |
4985 | /// you have a line of widgets and want them to be able to scroll if there is |
4986 | /// insufficient room, consider using a [ListView]. |
4987 | /// |
4988 | /// For a horizontal variant, see [Row]. |
4989 | /// |
4990 | /// If you only have one child, then consider using [Align] or [Center] to |
4991 | /// position the child. |
4992 | /// |
4993 | /// {@tool snippet} |
4994 | /// |
4995 | /// This example uses a [Column] to arrange three widgets vertically, the last |
4996 | /// being made to fill all the remaining space. |
4997 | /// |
4998 | /// ![Using the Column in this way creates two short lines of text with a large Flutter underneath.](https://flutter.github.io/assets-for-api-docs/assets/widgets/column.png) |
4999 | /// |
5000 | /// ```dart |
5001 | /// const Column( |
5002 | /// children: <Widget>[ |
5003 | /// Text('Deliver features faster'), |
5004 | /// Text('Craft beautiful UIs'), |
5005 | /// Expanded( |
5006 | /// child: FittedBox( |
5007 | /// child: FlutterLogo(), |
5008 | /// ), |
5009 | /// ), |
5010 | /// ], |
5011 | /// ) |
5012 | /// ``` |
5013 | /// {@end-tool} |
5014 | /// {@tool snippet} |
5015 | /// |
5016 | /// In the sample above, the text and the logo are centered on each line. In the |
5017 | /// following example, the [crossAxisAlignment] is set to |
5018 | /// [CrossAxisAlignment.start], so that the children are left-aligned. The |
5019 | /// [mainAxisSize] is set to [MainAxisSize.min], so that the column shrinks to |
5020 | /// fit the children. |
5021 | /// |
5022 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/column_properties.png) |
5023 | /// |
5024 | /// ```dart |
5025 | /// Column( |
5026 | /// crossAxisAlignment: CrossAxisAlignment.start, |
5027 | /// mainAxisSize: MainAxisSize.min, |
5028 | /// children: <Widget>[ |
5029 | /// const Text('We move under cover and we move as one'), |
5030 | /// const Text('Through the night, we have one shot to live another day'), |
5031 | /// const Text('We cannot let a stray gunshot give us away'), |
5032 | /// const Text('We will fight up close, seize the moment and stay in it'), |
5033 | /// const Text('It’s either that or meet the business end of a bayonet'), |
5034 | /// const Text('The code word is ‘Rochambeau,’ dig me?'), |
5035 | /// Text('Rochambeau!', style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 2.0)), |
5036 | /// ], |
5037 | /// ) |
5038 | /// ``` |
5039 | /// {@end-tool} |
5040 | /// |
5041 | /// ## Troubleshooting |
5042 | /// |
5043 | /// ### When the incoming vertical constraints are unbounded |
5044 | /// |
5045 | /// When a [Column] has one or more [Expanded] or [Flexible] children, and is |
5046 | /// placed in another [Column], or in a [ListView], or in some other context |
5047 | /// that does not provide a maximum height constraint for the [Column], you will |
5048 | /// get an exception at runtime saying that there are children with non-zero |
5049 | /// flex but the vertical constraints are unbounded. |
5050 | /// |
5051 | /// The problem, as described in the details that accompany that exception, is |
5052 | /// that using [Flexible] or [Expanded] means that the remaining space after |
5053 | /// laying out all the other children must be shared equally, but if the |
5054 | /// incoming vertical constraints are unbounded, there is infinite remaining |
5055 | /// space. |
5056 | /// |
5057 | /// The key to solving this problem is usually to determine why the [Column] is |
5058 | /// receiving unbounded vertical constraints. |
5059 | /// |
5060 | /// One common reason for this to happen is that the [Column] has been placed in |
5061 | /// another [Column] (without using [Expanded] or [Flexible] around the inner |
5062 | /// nested [Column]). When a [Column] lays out its non-flex children (those that |
5063 | /// have neither [Expanded] or [Flexible] around them), it gives them unbounded |
5064 | /// constraints so that they can determine their own dimensions (passing |
5065 | /// unbounded constraints usually signals to the child that it should |
5066 | /// shrink-wrap its contents). The solution in this case is typically to just |
5067 | /// wrap the inner column in an [Expanded] to indicate that it should take the |
5068 | /// remaining space of the outer column, rather than being allowed to take any |
5069 | /// amount of room it desires. |
5070 | /// |
5071 | /// Another reason for this message to be displayed is nesting a [Column] inside |
5072 | /// a [ListView] or other vertical scrollable. In that scenario, there really is |
5073 | /// infinite vertical space (the whole point of a vertical scrolling list is to |
5074 | /// allow infinite space vertically). In such scenarios, it is usually worth |
5075 | /// examining why the inner [Column] should have an [Expanded] or [Flexible] |
5076 | /// child: what size should the inner children really be? The solution in this |
5077 | /// case is typically to remove the [Expanded] or [Flexible] widgets from around |
5078 | /// the inner children. |
5079 | /// |
5080 | /// {@youtube 560 315 https://www.youtube.com/watch?v=jckqXR5CrPI} |
5081 | /// |
5082 | /// For more discussion about constraints, see [BoxConstraints]. |
5083 | /// |
5084 | /// ### The yellow and black striped banner |
5085 | /// |
5086 | /// When the contents of a [Column] exceed the amount of space available, the |
5087 | /// [Column] overflows, and the contents are clipped. In debug mode, a yellow |
5088 | /// and black striped bar is rendered at the overflowing edge to indicate the |
5089 | /// problem, and a message is printed below the [Column] saying how much |
5090 | /// overflow was detected. |
5091 | /// |
5092 | /// The usual solution is to use a [ListView] rather than a [Column], to enable |
5093 | /// the contents to scroll when vertical space is limited. |
5094 | /// |
5095 | /// ## Layout algorithm |
5096 | /// |
5097 | /// _This section describes how a [Column] is rendered by the framework._ |
5098 | /// _See [BoxConstraints] for an introduction to box layout models._ |
5099 | /// |
5100 | /// Layout for a [Column] proceeds in six steps: |
5101 | /// |
5102 | /// 1. Layout each child with a null or zero flex factor (e.g., those that are |
5103 | /// not [Expanded]) with unbounded vertical constraints and the incoming |
5104 | /// horizontal constraints. If the [crossAxisAlignment] is |
5105 | /// [CrossAxisAlignment.stretch], instead use tight horizontal constraints |
5106 | /// that match the incoming max width. |
5107 | /// 2. Divide the remaining vertical space among the children with non-zero |
5108 | /// flex factors (e.g., those that are [Expanded]) according to their flex |
5109 | /// factor. For example, a child with a flex factor of 2.0 will receive twice |
5110 | /// the amount of vertical space as a child with a flex factor of 1.0. |
5111 | /// 3. Layout each of the remaining children with the same horizontal |
5112 | /// constraints as in step 1, but instead of using unbounded vertical |
5113 | /// constraints, use vertical constraints based on the amount of space |
5114 | /// allocated in step 2. Children with [Flexible.fit] properties that are |
5115 | /// [FlexFit.tight] are given tight constraints (i.e., forced to fill the |
5116 | /// allocated space), and children with [Flexible.fit] properties that are |
5117 | /// [FlexFit.loose] are given loose constraints (i.e., not forced to fill the |
5118 | /// allocated space). |
5119 | /// 4. The width of the [Column] is the maximum width of the children (which |
5120 | /// will always satisfy the incoming horizontal constraints). |
5121 | /// 5. The height of the [Column] is determined by the [mainAxisSize] property. |
5122 | /// If the [mainAxisSize] property is [MainAxisSize.max], then the height of |
5123 | /// the [Column] is the max height of the incoming constraints. If the |
5124 | /// [mainAxisSize] property is [MainAxisSize.min], then the height of the |
5125 | /// [Column] is the sum of heights of the children (subject to the incoming |
5126 | /// constraints). |
5127 | /// 6. Determine the position for each child according to the |
5128 | /// [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the |
5129 | /// [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any vertical |
5130 | /// space that has not been allocated to children is divided evenly and |
5131 | /// placed between the children. |
5132 | /// |
5133 | /// See also: |
5134 | /// |
5135 | /// * [Row], for a horizontal equivalent. |
5136 | /// * [Flex], if you don't know in advance if you want a horizontal or vertical |
5137 | /// arrangement. |
5138 | /// * [Expanded], to indicate children that should take all the remaining room. |
5139 | /// * [Flexible], to indicate children that should share the remaining room but |
5140 | /// that may size smaller (leaving some remaining room unused). |
5141 | /// * [SingleChildScrollView], whose documentation discusses some ways to |
5142 | /// use a [Column] inside a scrolling container. |
5143 | /// * [Spacer], a widget that takes up space proportional to its flex value. |
5144 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
5145 | class Column extends Flex { |
5146 | /// Creates a vertical array of children. |
5147 | /// |
5148 | /// If [crossAxisAlignment] is [CrossAxisAlignment.baseline], then |
5149 | /// [textBaseline] must not be null. |
5150 | /// |
5151 | /// The [textDirection] argument defaults to the ambient [Directionality], if |
5152 | /// any. If there is no ambient directionality, and a text direction is going |
5153 | /// to be necessary to disambiguate `start` or `end` values for the |
5154 | /// [crossAxisAlignment], the [textDirection] must not be null. |
5155 | const Column({ |
5156 | super.key, |
5157 | super.mainAxisAlignment, |
5158 | super.mainAxisSize, |
5159 | super.crossAxisAlignment, |
5160 | super.textDirection, |
5161 | super.verticalDirection, |
5162 | super.textBaseline, |
5163 | super.children, |
5164 | }) : super( |
5165 | direction: Axis.vertical, |
5166 | ); |
5167 | } |
5168 | |
5169 | /// A widget that controls how a child of a [Row], [Column], or [Flex] flexes. |
5170 | /// |
5171 | /// Using a [Flexible] widget gives a child of a [Row], [Column], or [Flex] |
5172 | /// the flexibility to expand to fill the available space in the main axis |
5173 | /// (e.g., horizontally for a [Row] or vertically for a [Column]), but, unlike |
5174 | /// [Expanded], [Flexible] does not require the child to fill the available |
5175 | /// space. |
5176 | /// |
5177 | /// A [Flexible] widget must be a descendant of a [Row], [Column], or [Flex], |
5178 | /// and the path from the [Flexible] widget to its enclosing [Row], [Column], or |
5179 | /// [Flex] must contain only [StatelessWidget]s or [StatefulWidget]s (not other |
5180 | /// kinds of widgets, like [RenderObjectWidget]s). |
5181 | /// |
5182 | /// {@youtube 560 315 https://www.youtube.com/watch?v=CI7x0mAZiY0} |
5183 | /// |
5184 | /// See also: |
5185 | /// |
5186 | /// * [Expanded], which forces the child to expand to fill the available space. |
5187 | /// * [Spacer], a widget that takes up space proportional to its flex value. |
5188 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
5189 | class Flexible extends ParentDataWidget<FlexParentData> { |
5190 | /// Creates a widget that controls how a child of a [Row], [Column], or [Flex] |
5191 | /// flexes. |
5192 | const Flexible({ |
5193 | super.key, |
5194 | this.flex = 1, |
5195 | this.fit = FlexFit.loose, |
5196 | required super.child, |
5197 | }); |
5198 | |
5199 | /// The flex factor to use for this child. |
5200 | /// |
5201 | /// If null or zero, the child is inflexible and determines its own size. If |
5202 | /// non-zero, the amount of space the child's can occupy in the main axis is |
5203 | /// determined by dividing the free space (after placing the inflexible |
5204 | /// children) according to the flex factors of the flexible children. |
5205 | final int flex; |
5206 | |
5207 | /// How a flexible child is inscribed into the available space. |
5208 | /// |
5209 | /// If [flex] is non-zero, the [fit] determines whether the child fills the |
5210 | /// space the parent makes available during layout. If the fit is |
5211 | /// [FlexFit.tight], the child is required to fill the available space. If the |
5212 | /// fit is [FlexFit.loose], the child can be at most as large as the available |
5213 | /// space (but is allowed to be smaller). |
5214 | final FlexFit fit; |
5215 | |
5216 | @override |
5217 | void applyParentData(RenderObject renderObject) { |
5218 | assert(renderObject.parentData is FlexParentData); |
5219 | final FlexParentData parentData = renderObject.parentData! as FlexParentData; |
5220 | bool needsLayout = false; |
5221 | |
5222 | if (parentData.flex != flex) { |
5223 | parentData.flex = flex; |
5224 | needsLayout = true; |
5225 | } |
5226 | |
5227 | if (parentData.fit != fit) { |
5228 | parentData.fit = fit; |
5229 | needsLayout = true; |
5230 | } |
5231 | |
5232 | if (needsLayout) { |
5233 | final RenderObject? targetParent = renderObject.parent; |
5234 | if (targetParent is RenderObject) { |
5235 | targetParent.markNeedsLayout(); |
5236 | } |
5237 | } |
5238 | } |
5239 | |
5240 | @override |
5241 | Type get debugTypicalAncestorWidgetClass => Flex; |
5242 | |
5243 | @override |
5244 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
5245 | super.debugFillProperties(properties); |
5246 | properties.add(IntProperty('flex' , flex)); |
5247 | } |
5248 | } |
5249 | |
5250 | /// A widget that expands a child of a [Row], [Column], or [Flex] |
5251 | /// so that the child fills the available space. |
5252 | /// |
5253 | /// Using an [Expanded] widget makes a child of a [Row], [Column], or [Flex] |
5254 | /// expand to fill the available space along the main axis (e.g., horizontally for |
5255 | /// a [Row] or vertically for a [Column]). If multiple children are expanded, |
5256 | /// the available space is divided among them according to the [flex] factor. |
5257 | /// |
5258 | /// An [Expanded] widget must be a descendant of a [Row], [Column], or [Flex], |
5259 | /// and the path from the [Expanded] widget to its enclosing [Row], [Column], or |
5260 | /// [Flex] must contain only [StatelessWidget]s or [StatefulWidget]s (not other |
5261 | /// kinds of widgets, like [RenderObjectWidget]s). |
5262 | /// |
5263 | /// {@youtube 560 315 https://www.youtube.com/watch?v=_rnZaagadyo} |
5264 | /// |
5265 | /// {@tool dartpad} |
5266 | /// This example shows how to use an [Expanded] widget in a [Column] so that |
5267 | /// its middle child, a [Container] here, expands to fill the space. |
5268 | /// |
5269 | /// ![This results in two thin blue boxes with a larger amber box in between.](https://flutter.github.io/assets-for-api-docs/assets/widgets/expanded_column.png) |
5270 | /// |
5271 | /// ** See code in examples/api/lib/widgets/basic/expanded.0.dart ** |
5272 | /// {@end-tool} |
5273 | /// |
5274 | /// {@tool dartpad} |
5275 | /// This example shows how to use an [Expanded] widget in a [Row] with multiple |
5276 | /// children expanded, utilizing the [flex] factor to prioritize available space. |
5277 | /// |
5278 | /// ![This results in a wide amber box, followed by a thin blue box, with a medium width amber box at the end.](https://flutter.github.io/assets-for-api-docs/assets/widgets/expanded_row.png) |
5279 | /// |
5280 | /// ** See code in examples/api/lib/widgets/basic/expanded.1.dart ** |
5281 | /// {@end-tool} |
5282 | /// |
5283 | /// See also: |
5284 | /// |
5285 | /// * [Flexible], which does not force the child to fill the available space. |
5286 | /// * [Spacer], a widget that takes up space proportional to its flex value. |
5287 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
5288 | class Expanded extends Flexible { |
5289 | /// Creates a widget that expands a child of a [Row], [Column], or [Flex] |
5290 | /// so that the child fills the available space along the flex widget's |
5291 | /// main axis. |
5292 | const Expanded({ |
5293 | super.key, |
5294 | super.flex, |
5295 | required super.child, |
5296 | }) : super(fit: FlexFit.tight); |
5297 | } |
5298 | |
5299 | /// A widget that displays its children in multiple horizontal or vertical runs. |
5300 | /// |
5301 | /// A [Wrap] lays out each child and attempts to place the child adjacent to the |
5302 | /// previous child in the main axis, given by [direction], leaving [spacing] |
5303 | /// space in between. If there is not enough space to fit the child, [Wrap] |
5304 | /// creates a new _run_ adjacent to the existing children in the cross axis. |
5305 | /// |
5306 | /// After all the children have been allocated to runs, the children within the |
5307 | /// runs are positioned according to the [alignment] in the main axis and |
5308 | /// according to the [crossAxisAlignment] in the cross axis. |
5309 | /// |
5310 | /// The runs themselves are then positioned in the cross axis according to the |
5311 | /// [runSpacing] and [runAlignment]. |
5312 | /// |
5313 | /// {@youtube 560 315 https://www.youtube.com/watch?v=z5iw2SeFx2M} |
5314 | /// |
5315 | /// {@tool snippet} |
5316 | /// |
5317 | /// This example renders some [Chip]s representing four contacts in a [Wrap] so |
5318 | /// that they flow across lines as necessary. |
5319 | /// |
5320 | /// ```dart |
5321 | /// Wrap( |
5322 | /// spacing: 8.0, // gap between adjacent chips |
5323 | /// runSpacing: 4.0, // gap between lines |
5324 | /// children: <Widget>[ |
5325 | /// Chip( |
5326 | /// avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('AH')), |
5327 | /// label: const Text('Hamilton'), |
5328 | /// ), |
5329 | /// Chip( |
5330 | /// avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('ML')), |
5331 | /// label: const Text('Lafayette'), |
5332 | /// ), |
5333 | /// Chip( |
5334 | /// avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('HM')), |
5335 | /// label: const Text('Mulligan'), |
5336 | /// ), |
5337 | /// Chip( |
5338 | /// avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('JL')), |
5339 | /// label: const Text('Laurens'), |
5340 | /// ), |
5341 | /// ], |
5342 | /// ) |
5343 | /// ``` |
5344 | /// {@end-tool} |
5345 | /// |
5346 | /// See also: |
5347 | /// |
5348 | /// * [Row], which places children in one line, and gives control over their |
5349 | /// alignment and spacing. |
5350 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
5351 | class Wrap extends MultiChildRenderObjectWidget { |
5352 | /// Creates a wrap layout. |
5353 | /// |
5354 | /// By default, the wrap layout is horizontal and both the children and the |
5355 | /// runs are aligned to the start. |
5356 | /// |
5357 | /// The [textDirection] argument defaults to the ambient [Directionality], if |
5358 | /// any. If there is no ambient directionality, and a text direction is going |
5359 | /// to be necessary to decide which direction to lay the children in or to |
5360 | /// disambiguate `start` or `end` values for the main or cross axis |
5361 | /// directions, the [textDirection] must not be null. |
5362 | const Wrap({ |
5363 | super.key, |
5364 | this.direction = Axis.horizontal, |
5365 | this.alignment = WrapAlignment.start, |
5366 | this.spacing = 0.0, |
5367 | this.runAlignment = WrapAlignment.start, |
5368 | this.runSpacing = 0.0, |
5369 | this.crossAxisAlignment = WrapCrossAlignment.start, |
5370 | this.textDirection, |
5371 | this.verticalDirection = VerticalDirection.down, |
5372 | this.clipBehavior = Clip.none, |
5373 | super.children, |
5374 | }); |
5375 | |
5376 | /// The direction to use as the main axis. |
5377 | /// |
5378 | /// For example, if [direction] is [Axis.horizontal], the default, the |
5379 | /// children are placed adjacent to one another in a horizontal run until the |
5380 | /// available horizontal space is consumed, at which point a subsequent |
5381 | /// children are placed in a new run vertically adjacent to the previous run. |
5382 | final Axis direction; |
5383 | |
5384 | /// How the children within a run should be placed in the main axis. |
5385 | /// |
5386 | /// For example, if [alignment] is [WrapAlignment.center], the children in |
5387 | /// each run are grouped together in the center of their run in the main axis. |
5388 | /// |
5389 | /// Defaults to [WrapAlignment.start]. |
5390 | /// |
5391 | /// See also: |
5392 | /// |
5393 | /// * [runAlignment], which controls how the runs are placed relative to each |
5394 | /// other in the cross axis. |
5395 | /// * [crossAxisAlignment], which controls how the children within each run |
5396 | /// are placed relative to each other in the cross axis. |
5397 | final WrapAlignment alignment; |
5398 | |
5399 | /// How much space to place between children in a run in the main axis. |
5400 | /// |
5401 | /// For example, if [spacing] is 10.0, the children will be spaced at least |
5402 | /// 10.0 logical pixels apart in the main axis. |
5403 | /// |
5404 | /// If there is additional free space in a run (e.g., because the wrap has a |
5405 | /// minimum size that is not filled or because some runs are longer than |
5406 | /// others), the additional free space will be allocated according to the |
5407 | /// [alignment]. |
5408 | /// |
5409 | /// Defaults to 0.0. |
5410 | final double spacing; |
5411 | |
5412 | /// How the runs themselves should be placed in the cross axis. |
5413 | /// |
5414 | /// For example, if [runAlignment] is [WrapAlignment.center], the runs are |
5415 | /// grouped together in the center of the overall [Wrap] in the cross axis. |
5416 | /// |
5417 | /// Defaults to [WrapAlignment.start]. |
5418 | /// |
5419 | /// See also: |
5420 | /// |
5421 | /// * [alignment], which controls how the children within each run are placed |
5422 | /// relative to each other in the main axis. |
5423 | /// * [crossAxisAlignment], which controls how the children within each run |
5424 | /// are placed relative to each other in the cross axis. |
5425 | final WrapAlignment runAlignment; |
5426 | |
5427 | /// How much space to place between the runs themselves in the cross axis. |
5428 | /// |
5429 | /// For example, if [runSpacing] is 10.0, the runs will be spaced at least |
5430 | /// 10.0 logical pixels apart in the cross axis. |
5431 | /// |
5432 | /// If there is additional free space in the overall [Wrap] (e.g., because |
5433 | /// the wrap has a minimum size that is not filled), the additional free space |
5434 | /// will be allocated according to the [runAlignment]. |
5435 | /// |
5436 | /// Defaults to 0.0. |
5437 | final double runSpacing; |
5438 | |
5439 | /// How the children within a run should be aligned relative to each other in |
5440 | /// the cross axis. |
5441 | /// |
5442 | /// For example, if this is set to [WrapCrossAlignment.end], and the |
5443 | /// [direction] is [Axis.horizontal], then the children within each |
5444 | /// run will have their bottom edges aligned to the bottom edge of the run. |
5445 | /// |
5446 | /// Defaults to [WrapCrossAlignment.start]. |
5447 | /// |
5448 | /// See also: |
5449 | /// |
5450 | /// * [alignment], which controls how the children within each run are placed |
5451 | /// relative to each other in the main axis. |
5452 | /// * [runAlignment], which controls how the runs are placed relative to each |
5453 | /// other in the cross axis. |
5454 | final WrapCrossAlignment crossAxisAlignment; |
5455 | |
5456 | /// Determines the order to lay children out horizontally and how to interpret |
5457 | /// `start` and `end` in the horizontal direction. |
5458 | /// |
5459 | /// Defaults to the ambient [Directionality]. |
5460 | /// |
5461 | /// If the [direction] is [Axis.horizontal], this controls order in which the |
5462 | /// children are positioned (left-to-right or right-to-left), and the meaning |
5463 | /// of the [alignment] property's [WrapAlignment.start] and |
5464 | /// [WrapAlignment.end] values. |
5465 | /// |
5466 | /// If the [direction] is [Axis.horizontal], and either the |
5467 | /// [alignment] is either [WrapAlignment.start] or [WrapAlignment.end], or |
5468 | /// there's more than one child, then the [textDirection] (or the ambient |
5469 | /// [Directionality]) must not be null. |
5470 | /// |
5471 | /// If the [direction] is [Axis.vertical], this controls the order in which |
5472 | /// runs are positioned, the meaning of the [runAlignment] property's |
5473 | /// [WrapAlignment.start] and [WrapAlignment.end] values, as well as the |
5474 | /// [crossAxisAlignment] property's [WrapCrossAlignment.start] and |
5475 | /// [WrapCrossAlignment.end] values. |
5476 | /// |
5477 | /// If the [direction] is [Axis.vertical], and either the |
5478 | /// [runAlignment] is either [WrapAlignment.start] or [WrapAlignment.end], the |
5479 | /// [crossAxisAlignment] is either [WrapCrossAlignment.start] or |
5480 | /// [WrapCrossAlignment.end], or there's more than one child, then the |
5481 | /// [textDirection] (or the ambient [Directionality]) must not be null. |
5482 | final TextDirection? textDirection; |
5483 | |
5484 | /// Determines the order to lay children out vertically and how to interpret |
5485 | /// `start` and `end` in the vertical direction. |
5486 | /// |
5487 | /// If the [direction] is [Axis.vertical], this controls which order children |
5488 | /// are painted in (down or up), the meaning of the [alignment] property's |
5489 | /// [WrapAlignment.start] and [WrapAlignment.end] values. |
5490 | /// |
5491 | /// If the [direction] is [Axis.vertical], and either the [alignment] |
5492 | /// is either [WrapAlignment.start] or [WrapAlignment.end], or there's |
5493 | /// more than one child, then the [verticalDirection] must not be null. |
5494 | /// |
5495 | /// If the [direction] is [Axis.horizontal], this controls the order in which |
5496 | /// runs are positioned, the meaning of the [runAlignment] property's |
5497 | /// [WrapAlignment.start] and [WrapAlignment.end] values, as well as the |
5498 | /// [crossAxisAlignment] property's [WrapCrossAlignment.start] and |
5499 | /// [WrapCrossAlignment.end] values. |
5500 | /// |
5501 | /// If the [direction] is [Axis.horizontal], and either the |
5502 | /// [runAlignment] is either [WrapAlignment.start] or [WrapAlignment.end], the |
5503 | /// [crossAxisAlignment] is either [WrapCrossAlignment.start] or |
5504 | /// [WrapCrossAlignment.end], or there's more than one child, then the |
5505 | /// [verticalDirection] must not be null. |
5506 | final VerticalDirection verticalDirection; |
5507 | |
5508 | /// {@macro flutter.material.Material.clipBehavior} |
5509 | /// |
5510 | /// Defaults to [Clip.none]. |
5511 | final Clip clipBehavior; |
5512 | |
5513 | @override |
5514 | RenderWrap createRenderObject(BuildContext context) { |
5515 | return RenderWrap( |
5516 | direction: direction, |
5517 | alignment: alignment, |
5518 | spacing: spacing, |
5519 | runAlignment: runAlignment, |
5520 | runSpacing: runSpacing, |
5521 | crossAxisAlignment: crossAxisAlignment, |
5522 | textDirection: textDirection ?? Directionality.maybeOf(context), |
5523 | verticalDirection: verticalDirection, |
5524 | clipBehavior: clipBehavior, |
5525 | ); |
5526 | } |
5527 | |
5528 | @override |
5529 | void updateRenderObject(BuildContext context, RenderWrap renderObject) { |
5530 | renderObject |
5531 | ..direction = direction |
5532 | ..alignment = alignment |
5533 | ..spacing = spacing |
5534 | ..runAlignment = runAlignment |
5535 | ..runSpacing = runSpacing |
5536 | ..crossAxisAlignment = crossAxisAlignment |
5537 | ..textDirection = textDirection ?? Directionality.maybeOf(context) |
5538 | ..verticalDirection = verticalDirection |
5539 | ..clipBehavior = clipBehavior; |
5540 | } |
5541 | |
5542 | @override |
5543 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
5544 | super.debugFillProperties(properties); |
5545 | properties.add(EnumProperty<Axis>('direction' , direction)); |
5546 | properties.add(EnumProperty<WrapAlignment>('alignment' , alignment)); |
5547 | properties.add(DoubleProperty('spacing' , spacing)); |
5548 | properties.add(EnumProperty<WrapAlignment>('runAlignment' , runAlignment)); |
5549 | properties.add(DoubleProperty('runSpacing' , runSpacing)); |
5550 | properties.add(EnumProperty<WrapCrossAlignment>('crossAxisAlignment' , crossAxisAlignment)); |
5551 | properties.add(EnumProperty<TextDirection>('textDirection' , textDirection, defaultValue: null)); |
5552 | properties.add(EnumProperty<VerticalDirection>('verticalDirection' , verticalDirection, defaultValue: VerticalDirection.down)); |
5553 | } |
5554 | } |
5555 | |
5556 | /// A widget that sizes and positions children efficiently, according to the |
5557 | /// logic in a [FlowDelegate]. |
5558 | /// |
5559 | /// {@youtube 560 315 https://www.youtube.com/watch?v=NG6pvXpnIso} |
5560 | /// |
5561 | /// Flow layouts are optimized for repositioning children using transformation |
5562 | /// matrices. |
5563 | /// |
5564 | /// The flow container is sized independently from the children by the |
5565 | /// [FlowDelegate.getSize] function of the delegate. The children are then sized |
5566 | /// independently given the constraints from the |
5567 | /// [FlowDelegate.getConstraintsForChild] function. |
5568 | /// |
5569 | /// Rather than positioning the children during layout, the children are |
5570 | /// positioned using transformation matrices during the paint phase using the |
5571 | /// matrices from the [FlowDelegate.paintChildren] function. The children can be |
5572 | /// repositioned efficiently by only _repainting_ the flow, which happens |
5573 | /// without the children being laid out again (contrast this with a [Stack], |
5574 | /// which does the sizing and positioning together during layout). |
5575 | /// |
5576 | /// The most efficient way to trigger a repaint of the flow is to supply an |
5577 | /// animation to the constructor of the [FlowDelegate]. The flow will listen to |
5578 | /// this animation and repaint whenever the animation ticks, avoiding both the |
5579 | /// build and layout phases of the pipeline. |
5580 | /// |
5581 | /// {@tool dartpad} |
5582 | /// This example uses the [Flow] widget to create a menu that opens and closes |
5583 | /// as it is interacted with, shown above. The color of the button in the menu |
5584 | /// changes to indicate which one has been selected. |
5585 | /// |
5586 | /// ** See code in examples/api/lib/widgets/basic/flow.0.dart ** |
5587 | /// {@end-tool} |
5588 | /// |
5589 | /// ## Hit testing and hidden [Flow] widgets |
5590 | /// |
5591 | /// The [Flow] widget recomputes its children's positions (as used by hit |
5592 | /// testing) during the _paint_ phase rather than during the _layout_ phase. |
5593 | /// |
5594 | /// Widgets like [Opacity] avoid painting their children when those children |
5595 | /// would be invisible due to their opacity being zero. |
5596 | /// |
5597 | /// Unfortunately, this means that hiding a [Flow] widget using an [Opacity] |
5598 | /// widget will cause bugs when the user attempts to interact with the hidden |
5599 | /// region, for example, by tapping it or clicking it. |
5600 | /// |
5601 | /// Such bugs will manifest either as out-of-date geometry (taps going to |
5602 | /// different widgets than might be expected by the currently-specified |
5603 | /// [FlowDelegate]s), or exceptions (e.g. if the last time the [Flow] was |
5604 | /// painted, a different set of children was specified). |
5605 | /// |
5606 | /// To avoid this, when hiding a [Flow] widget with an [Opacity] widget (or |
5607 | /// [AnimatedOpacity] or similar), it is wise to also disable hit testing on the |
5608 | /// widget by using [IgnorePointer]. This is generally good advice anyway as |
5609 | /// hit-testing invisible widgets is often confusing for the user. |
5610 | /// |
5611 | /// See also: |
5612 | /// |
5613 | /// * [Wrap], which provides the layout model that some other frameworks call |
5614 | /// "flow", and is otherwise unrelated to [Flow]. |
5615 | /// * [Stack], which arranges children relative to the edges of the container. |
5616 | /// * [CustomSingleChildLayout], which uses a delegate to control the layout of |
5617 | /// a single child. |
5618 | /// * [CustomMultiChildLayout], which uses a delegate to position multiple |
5619 | /// children. |
5620 | /// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). |
5621 | class Flow extends MultiChildRenderObjectWidget { |
5622 | /// Creates a flow layout. |
5623 | /// |
5624 | /// Wraps each of the given children in a [RepaintBoundary] to avoid |
5625 | /// repainting the children when the flow repaints. |
5626 | Flow({ |
5627 | super.key, |
5628 | required this.delegate, |
5629 | List<Widget> children = const <Widget>[], |
5630 | this.clipBehavior = Clip.hardEdge, |
5631 | }) : super(children: RepaintBoundary.wrapAll(children)); |
5632 | // https://github.com/dart-lang/sdk/issues/29277 |
5633 | |
5634 | /// Creates a flow layout. |
5635 | /// |
5636 | /// Does not wrap the given children in repaint boundaries, unlike the default |
5637 | /// constructor. Useful when the child is trivial to paint or already contains |
5638 | /// a repaint boundary. |
5639 | const Flow.unwrapped({ |
5640 | super.key, |
5641 | required this.delegate, |
5642 | super.children, |
5643 | this.clipBehavior = Clip.hardEdge, |
5644 | }); |
5645 | |
5646 | /// The delegate that controls the transformation matrices of the children. |
5647 | final FlowDelegate delegate; |
5648 | |
5649 | /// {@macro flutter.material.Material.clipBehavior} |
5650 | /// |
5651 | /// Defaults to [Clip.none]. |
5652 | final Clip clipBehavior; |
5653 | |
5654 | @override |
5655 | RenderFlow createRenderObject(BuildContext context) => RenderFlow(delegate: delegate, clipBehavior: clipBehavior); |
5656 | |
5657 | @override |
5658 | void updateRenderObject(BuildContext context, RenderFlow renderObject) { |
5659 | renderObject.delegate = delegate; |
5660 | renderObject.clipBehavior = clipBehavior; |
5661 | } |
5662 | } |
5663 | |
5664 | /// A paragraph of rich text. |
5665 | /// |
5666 | /// {@youtube 560 315 https://www.youtube.com/watch?v=rykDVh-QFfw} |
5667 | /// |
5668 | /// The [RichText] widget displays text that uses multiple different styles. The |
5669 | /// text to display is described using a tree of [TextSpan] objects, each of |
5670 | /// which has an associated style that is used for that subtree. The text might |
5671 | /// break across multiple lines or might all be displayed on the same line |
5672 | /// depending on the layout constraints. |
5673 | /// |
5674 | /// Text displayed in a [RichText] widget must be explicitly styled. When |
5675 | /// picking which style to use, consider using [DefaultTextStyle.of] the current |
5676 | /// [BuildContext] to provide defaults. For more details on how to style text in |
5677 | /// a [RichText] widget, see the documentation for [TextStyle]. |
5678 | /// |
5679 | /// Consider using the [Text] widget to integrate with the [DefaultTextStyle] |
5680 | /// automatically. When all the text uses the same style, the default constructor |
5681 | /// is less verbose. The [Text.rich] constructor allows you to style multiple |
5682 | /// spans with the default text style while still allowing specified styles per |
5683 | /// span. |
5684 | /// |
5685 | /// {@tool snippet} |
5686 | /// |
5687 | /// This sample demonstrates how to mix and match text with different text |
5688 | /// styles using the [RichText] Widget. It displays the text "Hello bold world," |
5689 | /// emphasizing the word "bold" using a bold font weight. |
5690 | /// |
5691 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/rich_text.png) |
5692 | /// |
5693 | /// ```dart |
5694 | /// RichText( |
5695 | /// text: TextSpan( |
5696 | /// text: 'Hello ', |
5697 | /// style: DefaultTextStyle.of(context).style, |
5698 | /// children: const <TextSpan>[ |
5699 | /// TextSpan(text: 'bold', style: TextStyle(fontWeight: FontWeight.bold)), |
5700 | /// TextSpan(text: ' world!'), |
5701 | /// ], |
5702 | /// ), |
5703 | /// ) |
5704 | /// ``` |
5705 | /// {@end-tool} |
5706 | /// |
5707 | /// ## Selections |
5708 | /// |
5709 | /// To make this [RichText] Selectable, the [RichText] needs to be in the |
5710 | /// subtree of a [SelectionArea] or [SelectableRegion] and a |
5711 | /// [SelectionRegistrar] needs to be assigned to the |
5712 | /// [RichText.selectionRegistrar]. One can use |
5713 | /// [SelectionContainer.maybeOf] to get the [SelectionRegistrar] from a |
5714 | /// context. This enables users to select the text in [RichText]s with mice or |
5715 | /// touch events. |
5716 | /// |
5717 | /// The [selectionColor] also needs to be set if the selection is enabled to |
5718 | /// draw the selection highlights. |
5719 | /// |
5720 | /// {@tool snippet} |
5721 | /// |
5722 | /// This sample demonstrates how to assign a [SelectionRegistrar] for RichTexts |
5723 | /// in the SelectionArea subtree. |
5724 | /// |
5725 | /// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/rich_text.png) |
5726 | /// |
5727 | /// ```dart |
5728 | /// RichText( |
5729 | /// text: const TextSpan(text: 'Hello'), |
5730 | /// selectionRegistrar: SelectionContainer.maybeOf(context), |
5731 | /// selectionColor: const Color(0xAF6694e8), |
5732 | /// ) |
5733 | /// ``` |
5734 | /// {@end-tool} |
5735 | /// |
5736 | /// See also: |
5737 | /// |
5738 | /// * [TextStyle], which discusses how to style text. |
5739 | /// * [TextSpan], which is used to describe the text in a paragraph. |
5740 | /// * [Text], which automatically applies the ambient styles described by a |
5741 | /// [DefaultTextStyle] to a single string. |
5742 | /// * [Text.rich], a const text widget that provides similar functionality |
5743 | /// as [RichText]. [Text.rich] will inherit [TextStyle] from [DefaultTextStyle]. |
5744 | /// * [SelectableRegion], which provides an overview of the selection system. |
5745 | class RichText extends MultiChildRenderObjectWidget { |
5746 | /// Creates a paragraph of rich text. |
5747 | /// |
5748 | /// The [maxLines] property may be null (and indeed defaults to null), but if |
5749 | /// it is not null, it must be greater than zero. |
5750 | /// |
5751 | /// The [textDirection], if null, defaults to the ambient [Directionality], |
5752 | /// which in that case must not be null. |
5753 | RichText({ |
5754 | super.key, |
5755 | required this.text, |
5756 | this.textAlign = TextAlign.start, |
5757 | this.textDirection, |
5758 | this.softWrap = true, |
5759 | this.overflow = TextOverflow.clip, |
5760 | @Deprecated( |
5761 | 'Use textScaler instead. ' |
5762 | 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. ' |
5763 | 'This feature was deprecated after v3.12.0-2.0.pre.' , |
5764 | ) |
5765 | double textScaleFactor = 1.0, |
5766 | TextScaler textScaler = TextScaler.noScaling, |
5767 | this.maxLines, |
5768 | this.locale, |
5769 | this.strutStyle, |
5770 | this.textWidthBasis = TextWidthBasis.parent, |
5771 | this.textHeightBehavior, |
5772 | this.selectionRegistrar, |
5773 | this.selectionColor, |
5774 | }) : assert(maxLines == null || maxLines > 0), |
5775 | assert(selectionRegistrar == null || selectionColor != null), |
5776 | assert(textScaleFactor == 1.0 || identical(textScaler, TextScaler.noScaling), 'Use textScaler instead.' ), |
5777 | textScaler = _effectiveTextScalerFrom(textScaler, textScaleFactor), |
5778 | super(children: WidgetSpan.extractFromInlineSpan(text, _effectiveTextScalerFrom(textScaler, textScaleFactor))); |
5779 | |
5780 | static TextScaler _effectiveTextScalerFrom(TextScaler textScaler, double textScaleFactor) { |
5781 | return switch ((textScaler, textScaleFactor)) { |
5782 | (final TextScaler scaler, 1.0) => scaler, |
5783 | (TextScaler.noScaling, final double textScaleFactor) => TextScaler.linear(textScaleFactor), |
5784 | (final TextScaler scaler, _) => scaler, |
5785 | }; |
5786 | } |
5787 | |
5788 | /// The text to display in this widget. |
5789 | final InlineSpan text; |
5790 | |
5791 | /// How the text should be aligned horizontally. |
5792 | final TextAlign textAlign; |
5793 | |
5794 | /// The directionality of the text. |
5795 | /// |
5796 | /// This decides how [textAlign] values like [TextAlign.start] and |
5797 | /// [TextAlign.end] are interpreted. |
5798 | /// |
5799 | /// This is also used to disambiguate how to render bidirectional text. For |
5800 | /// example, if the [text] is an English phrase followed by a Hebrew phrase, |
5801 | /// in a [TextDirection.ltr] context the English phrase will be on the left |
5802 | /// and the Hebrew phrase to its right, while in a [TextDirection.rtl] |
5803 | /// context, the English phrase will be on the right and the Hebrew phrase on |
5804 | /// its left. |
5805 | /// |
5806 | /// Defaults to the ambient [Directionality], if any. If there is no ambient |
5807 | /// [Directionality], then this must not be null. |
5808 | final TextDirection? textDirection; |
5809 | |
5810 | /// Whether the text should break at soft line breaks. |
5811 | /// |
5812 | /// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space. |
5813 | final bool softWrap; |
5814 | |
5815 | /// How visual overflow should be handled. |
5816 | final TextOverflow overflow; |
5817 | |
5818 | /// Deprecated. Will be removed in a future version of Flutter. Use |
5819 | /// [textScaler] instead. |
5820 | /// |
5821 | /// The number of font pixels for each logical pixel. |
5822 | /// |
5823 | /// For example, if the text scale factor is 1.5, text will be 50% larger than |
5824 | /// the specified font size. |
5825 | @Deprecated( |
5826 | 'Use textScaler instead. ' |
5827 | 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. ' |
5828 | 'This feature was deprecated after v3.12.0-2.0.pre.' , |
5829 | ) |
5830 | double get textScaleFactor => textScaler.textScaleFactor; |
5831 | |
5832 | /// {@macro flutter.painting.textPainter.textScaler} |
5833 | final TextScaler textScaler; |
5834 | |
5835 | /// An optional maximum number of lines for the text to span, wrapping if necessary. |
5836 | /// If the text exceeds the given number of lines, it will be truncated according |
5837 | /// to [overflow]. |
5838 | /// |
5839 | /// If this is 1, text will not wrap. Otherwise, text will be wrapped at the |
5840 | /// edge of the box. |
5841 | final int? maxLines; |
5842 | |
5843 | /// Used to select a font when the same Unicode character can |
5844 | /// be rendered differently, depending on the locale. |
5845 | /// |
5846 | /// It's rarely necessary to set this property. By default its value |
5847 | /// is inherited from the enclosing app with `Localizations.localeOf(context)`. |
5848 | /// |
5849 | /// See [RenderParagraph.locale] for more information. |
5850 | final Locale? locale; |
5851 | |
5852 | /// {@macro flutter.painting.textPainter.strutStyle} |
5853 | final StrutStyle? strutStyle; |
5854 | |
5855 | /// {@macro flutter.painting.textPainter.textWidthBasis} |
5856 | final TextWidthBasis textWidthBasis; |
5857 | |
5858 | /// {@macro dart.ui.textHeightBehavior} |
5859 | final ui.TextHeightBehavior? textHeightBehavior; |
5860 | |
5861 | /// The [SelectionRegistrar] this rich text is subscribed to. |
5862 | /// |
5863 | /// If this is set, [selectionColor] must be non-null. |
5864 | final SelectionRegistrar? selectionRegistrar; |
5865 | |
5866 | /// The color to use when painting the selection. |
5867 | /// |
5868 | /// This is ignored if [selectionRegistrar] is null. |
5869 | /// |
5870 | /// See the section on selections in the [RichText] top-level API |
5871 | /// documentation for more details on enabling selection in [RichText] |
5872 | /// widgets. |
5873 | final Color? selectionColor; |
5874 | |
5875 | @override |
5876 | RenderParagraph createRenderObject(BuildContext context) { |
5877 | assert(textDirection != null || debugCheckHasDirectionality(context)); |
5878 | return RenderParagraph(text, |
5879 | textAlign: textAlign, |
5880 | textDirection: textDirection ?? Directionality.of(context), |
5881 | softWrap: softWrap, |
5882 | overflow: overflow, |
5883 | textScaler: textScaler, |
5884 | maxLines: maxLines, |
5885 | strutStyle: strutStyle, |
5886 | textWidthBasis: textWidthBasis, |
5887 | textHeightBehavior: textHeightBehavior, |
5888 | locale: locale ?? Localizations.maybeLocaleOf(context), |
5889 | registrar: selectionRegistrar, |
5890 | selectionColor: selectionColor, |
5891 | ); |
5892 | } |
5893 | |
5894 | @override |
5895 | void updateRenderObject(BuildContext context, RenderParagraph renderObject) { |
5896 | assert(textDirection != null || debugCheckHasDirectionality(context)); |
5897 | renderObject |
5898 | ..text = text |
5899 | ..textAlign = textAlign |
5900 | ..textDirection = textDirection ?? Directionality.of(context) |
5901 | ..softWrap = softWrap |
5902 | ..overflow = overflow |
5903 | ..textScaler = textScaler |
5904 | ..maxLines = maxLines |
5905 | ..strutStyle = strutStyle |
5906 | ..textWidthBasis = textWidthBasis |
5907 | ..textHeightBehavior = textHeightBehavior |
5908 | ..locale = locale ?? Localizations.maybeLocaleOf(context) |
5909 | ..registrar = selectionRegistrar |
5910 | ..selectionColor = selectionColor; |
5911 | } |
5912 | |
5913 | @override |
5914 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
5915 | super.debugFillProperties(properties); |
5916 | properties.add(EnumProperty<TextAlign>('textAlign' , textAlign, defaultValue: TextAlign.start)); |
5917 | properties.add(EnumProperty<TextDirection>('textDirection' , textDirection, defaultValue: null)); |
5918 | properties.add(FlagProperty('softWrap' , value: softWrap, ifTrue: 'wrapping at box width' , ifFalse: 'no wrapping except at line break characters' , showName: true)); |
5919 | properties.add(EnumProperty<TextOverflow>('overflow' , overflow, defaultValue: TextOverflow.clip)); |
5920 | properties.add(DiagnosticsProperty<TextScaler>('textScaler' , textScaler, defaultValue: TextScaler.noScaling)); |
5921 | properties.add(IntProperty('maxLines' , maxLines, ifNull: 'unlimited' )); |
5922 | properties.add(EnumProperty<TextWidthBasis>('textWidthBasis' , textWidthBasis, defaultValue: TextWidthBasis.parent)); |
5923 | properties.add(StringProperty('text' , text.toPlainText())); |
5924 | properties.add(DiagnosticsProperty<Locale>('locale' , locale, defaultValue: null)); |
5925 | properties.add(DiagnosticsProperty<StrutStyle>('strutStyle' , strutStyle, defaultValue: null)); |
5926 | properties.add(DiagnosticsProperty<TextHeightBehavior>('textHeightBehavior' , textHeightBehavior, defaultValue: null)); |
5927 | } |
5928 | } |
5929 | |
5930 | /// A widget that displays a [dart:ui.Image] directly. |
5931 | /// |
5932 | /// The image is painted using [paintImage], which describes the meanings of the |
5933 | /// various fields on this class in more detail. |
5934 | /// |
5935 | /// The [image] is not disposed of by this widget. Creators of the widget are |
5936 | /// expected to call [Image.dispose] on the [image] once the [RawImage] is no |
5937 | /// longer buildable. |
5938 | /// |
5939 | /// This widget is rarely used directly. Instead, consider using [Image]. |
5940 | class RawImage extends LeafRenderObjectWidget { |
5941 | /// Creates a widget that displays an image. |
5942 | /// |
5943 | /// The [scale], [alignment], [repeat], [matchTextDirection] and [filterQuality] arguments must |
5944 | /// not be null. |
5945 | const RawImage({ |
5946 | super.key, |
5947 | this.image, |
5948 | this.debugImageLabel, |
5949 | this.width, |
5950 | this.height, |
5951 | this.scale = 1.0, |
5952 | this.color, |
5953 | this.opacity, |
5954 | this.colorBlendMode, |
5955 | this.fit, |
5956 | this.alignment = Alignment.center, |
5957 | this.repeat = ImageRepeat.noRepeat, |
5958 | this.centerSlice, |
5959 | this.matchTextDirection = false, |
5960 | this.invertColors = false, |
5961 | this.filterQuality = FilterQuality.low, |
5962 | this.isAntiAlias = false, |
5963 | }); |
5964 | |
5965 | /// The image to display. |
5966 | /// |
5967 | /// Since a [RawImage] is stateless, it does not ever dispose this image. |
5968 | /// Creators of a [RawImage] are expected to call [Image.dispose] on this |
5969 | /// image handle when the [RawImage] will no longer be needed. |
5970 | final ui.Image? image; |
5971 | |
5972 | /// A string identifying the source of the image. |
5973 | final String? debugImageLabel; |
5974 | |
5975 | /// If non-null, require the image to have this width. |
5976 | /// |
5977 | /// If null, the image will pick a size that best preserves its intrinsic |
5978 | /// aspect ratio. |
5979 | final double? width; |
5980 | |
5981 | /// If non-null, require the image to have this height. |
5982 | /// |
5983 | /// If null, the image will pick a size that best preserves its intrinsic |
5984 | /// aspect ratio. |
5985 | final double? height; |
5986 | |
5987 | /// Specifies the image's scale. |
5988 | /// |
5989 | /// Used when determining the best display size for the image. |
5990 | final double scale; |
5991 | |
5992 | /// If non-null, this color is blended with each image pixel using [colorBlendMode]. |
5993 | final Color? color; |
5994 | |
5995 | /// If non-null, the value from the [Animation] is multiplied with the opacity |
5996 | /// of each image pixel before painting onto the canvas. |
5997 | /// |
5998 | /// This is more efficient than using [FadeTransition] to change the opacity |
5999 | /// of an image. |
6000 | final Animation<double>? opacity; |
6001 | |
6002 | /// Used to set the filterQuality of the image. |
6003 | /// |
6004 | /// Defaults to [FilterQuality.low] to scale the image, which corresponds to |
6005 | /// bilinear interpolation. |
6006 | final FilterQuality filterQuality; |
6007 | |
6008 | /// Used to combine [color] with this image. |
6009 | /// |
6010 | /// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is |
6011 | /// the source and this image is the destination. |
6012 | /// |
6013 | /// See also: |
6014 | /// |
6015 | /// * [BlendMode], which includes an illustration of the effect of each blend mode. |
6016 | final BlendMode? colorBlendMode; |
6017 | |
6018 | /// How to inscribe the image into the space allocated during layout. |
6019 | /// |
6020 | /// The default varies based on the other fields. See the discussion at |
6021 | /// [paintImage]. |
6022 | final BoxFit? fit; |
6023 | |
6024 | /// How to align the image within its bounds. |
6025 | /// |
6026 | /// The alignment aligns the given position in the image to the given position |
6027 | /// in the layout bounds. For example, an [Alignment] alignment of (-1.0, |
6028 | /// -1.0) aligns the image to the top-left corner of its layout bounds, while a |
6029 | /// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the |
6030 | /// image with the bottom right corner of its layout bounds. Similarly, an |
6031 | /// alignment of (0.0, 1.0) aligns the bottom middle of the image with the |
6032 | /// middle of the bottom edge of its layout bounds. |
6033 | /// |
6034 | /// To display a subpart of an image, consider using a [CustomPainter] and |
6035 | /// [Canvas.drawImageRect]. |
6036 | /// |
6037 | /// If the [alignment] is [TextDirection]-dependent (i.e. if it is a |
6038 | /// [AlignmentDirectional]), then an ambient [Directionality] widget |
6039 | /// must be in scope. |
6040 | /// |
6041 | /// Defaults to [Alignment.center]. |
6042 | /// |
6043 | /// See also: |
6044 | /// |
6045 | /// * [Alignment], a class with convenient constants typically used to |
6046 | /// specify an [AlignmentGeometry]. |
6047 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments |
6048 | /// relative to text direction. |
6049 | final AlignmentGeometry alignment; |
6050 | |
6051 | /// How to paint any portions of the layout bounds not covered by the image. |
6052 | final ImageRepeat repeat; |
6053 | |
6054 | /// The center slice for a nine-patch image. |
6055 | /// |
6056 | /// The region of the image inside the center slice will be stretched both |
6057 | /// horizontally and vertically to fit the image into its destination. The |
6058 | /// region of the image above and below the center slice will be stretched |
6059 | /// only horizontally and the region of the image to the left and right of |
6060 | /// the center slice will be stretched only vertically. |
6061 | final Rect? centerSlice; |
6062 | |
6063 | /// Whether to paint the image in the direction of the [TextDirection]. |
6064 | /// |
6065 | /// If this is true, then in [TextDirection.ltr] contexts, the image will be |
6066 | /// drawn with its origin in the top left (the "normal" painting direction for |
6067 | /// images); and in [TextDirection.rtl] contexts, the image will be drawn with |
6068 | /// a scaling factor of -1 in the horizontal direction so that the origin is |
6069 | /// in the top right. |
6070 | /// |
6071 | /// This is occasionally used with images in right-to-left environments, for |
6072 | /// images that were designed for left-to-right locales. Be careful, when |
6073 | /// using this, to not flip images with integral shadows, text, or other |
6074 | /// effects that will look incorrect when flipped. |
6075 | /// |
6076 | /// If this is true, there must be an ambient [Directionality] widget in |
6077 | /// scope. |
6078 | final bool matchTextDirection; |
6079 | |
6080 | /// Whether the colors of the image are inverted when drawn. |
6081 | /// |
6082 | /// Inverting the colors of an image applies a new color filter to the paint. |
6083 | /// If there is another specified color filter, the invert will be applied |
6084 | /// after it. This is primarily used for implementing smart invert on iOS. |
6085 | /// |
6086 | /// See also: |
6087 | /// |
6088 | /// * [Paint.invertColors], for the dart:ui implementation. |
6089 | final bool invertColors; |
6090 | |
6091 | /// Whether to paint the image with anti-aliasing. |
6092 | /// |
6093 | /// Anti-aliasing alleviates the sawtooth artifact when the image is rotated. |
6094 | final bool isAntiAlias; |
6095 | |
6096 | @override |
6097 | RenderImage createRenderObject(BuildContext context) { |
6098 | assert((!matchTextDirection && alignment is Alignment) || debugCheckHasDirectionality(context)); |
6099 | assert( |
6100 | image?.debugGetOpenHandleStackTraces()?.isNotEmpty ?? true, |
6101 | 'Creator of a RawImage disposed of the image when the RawImage still ' |
6102 | 'needed it.' , |
6103 | ); |
6104 | return RenderImage( |
6105 | image: image?.clone(), |
6106 | debugImageLabel: debugImageLabel, |
6107 | width: width, |
6108 | height: height, |
6109 | scale: scale, |
6110 | color: color, |
6111 | opacity: opacity, |
6112 | colorBlendMode: colorBlendMode, |
6113 | fit: fit, |
6114 | alignment: alignment, |
6115 | repeat: repeat, |
6116 | centerSlice: centerSlice, |
6117 | matchTextDirection: matchTextDirection, |
6118 | textDirection: matchTextDirection || alignment is! Alignment ? Directionality.of(context) : null, |
6119 | invertColors: invertColors, |
6120 | isAntiAlias: isAntiAlias, |
6121 | filterQuality: filterQuality, |
6122 | ); |
6123 | } |
6124 | |
6125 | @override |
6126 | void updateRenderObject(BuildContext context, RenderImage renderObject) { |
6127 | assert( |
6128 | image?.debugGetOpenHandleStackTraces()?.isNotEmpty ?? true, |
6129 | 'Creator of a RawImage disposed of the image when the RawImage still ' |
6130 | 'needed it.' , |
6131 | ); |
6132 | renderObject |
6133 | ..image = image?.clone() |
6134 | ..debugImageLabel = debugImageLabel |
6135 | ..width = width |
6136 | ..height = height |
6137 | ..scale = scale |
6138 | ..color = color |
6139 | ..opacity = opacity |
6140 | ..colorBlendMode = colorBlendMode |
6141 | ..fit = fit |
6142 | ..alignment = alignment |
6143 | ..repeat = repeat |
6144 | ..centerSlice = centerSlice |
6145 | ..matchTextDirection = matchTextDirection |
6146 | ..textDirection = matchTextDirection || alignment is! Alignment ? Directionality.of(context) : null |
6147 | ..invertColors = invertColors |
6148 | ..isAntiAlias = isAntiAlias |
6149 | ..filterQuality = filterQuality; |
6150 | } |
6151 | |
6152 | @override |
6153 | void didUnmountRenderObject(RenderImage renderObject) { |
6154 | // Have the render object dispose its image handle. |
6155 | renderObject.image = null; |
6156 | } |
6157 | |
6158 | @override |
6159 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
6160 | super.debugFillProperties(properties); |
6161 | properties.add(DiagnosticsProperty<ui.Image>('image' , image)); |
6162 | properties.add(DoubleProperty('width' , width, defaultValue: null)); |
6163 | properties.add(DoubleProperty('height' , height, defaultValue: null)); |
6164 | properties.add(DoubleProperty('scale' , scale, defaultValue: 1.0)); |
6165 | properties.add(ColorProperty('color' , color, defaultValue: null)); |
6166 | properties.add(DiagnosticsProperty<Animation<double>?>('opacity' , opacity, defaultValue: null)); |
6167 | properties.add(EnumProperty<BlendMode>('colorBlendMode' , colorBlendMode, defaultValue: null)); |
6168 | properties.add(EnumProperty<BoxFit>('fit' , fit, defaultValue: null)); |
6169 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment, defaultValue: null)); |
6170 | properties.add(EnumProperty<ImageRepeat>('repeat' , repeat, defaultValue: ImageRepeat.noRepeat)); |
6171 | properties.add(DiagnosticsProperty<Rect>('centerSlice' , centerSlice, defaultValue: null)); |
6172 | properties.add(FlagProperty('matchTextDirection' , value: matchTextDirection, ifTrue: 'match text direction' )); |
6173 | properties.add(DiagnosticsProperty<bool>('invertColors' , invertColors)); |
6174 | properties.add(EnumProperty<FilterQuality>('filterQuality' , filterQuality)); |
6175 | } |
6176 | } |
6177 | |
6178 | /// A widget that determines the default asset bundle for its descendants. |
6179 | /// |
6180 | /// For example, used by [Image] to determine which bundle to use for |
6181 | /// [AssetImage]s if no bundle is specified explicitly. |
6182 | /// |
6183 | /// {@tool snippet} |
6184 | /// |
6185 | /// This can be used in tests to override what the current asset bundle is, thus |
6186 | /// allowing specific resources to be injected into the widget under test. |
6187 | /// |
6188 | /// For example, a test could create a test asset bundle like this: |
6189 | /// |
6190 | /// ```dart |
6191 | /// class TestAssetBundle extends CachingAssetBundle { |
6192 | /// @override |
6193 | /// Future<ByteData> load(String key) async { |
6194 | /// if (key == 'resources/test') { |
6195 | /// return ByteData.sublistView(utf8.encode('Hello World!')); |
6196 | /// } |
6197 | /// return ByteData(0); |
6198 | /// } |
6199 | /// } |
6200 | /// ``` |
6201 | /// {@end-tool} |
6202 | /// {@tool snippet} |
6203 | /// |
6204 | /// ...then wrap the widget under test with a [DefaultAssetBundle] using this |
6205 | /// bundle implementation: |
6206 | /// |
6207 | /// ```dart |
6208 | /// // continuing from previous example... |
6209 | /// await tester.pumpWidget( |
6210 | /// MaterialApp( |
6211 | /// home: DefaultAssetBundle( |
6212 | /// bundle: TestAssetBundle(), |
6213 | /// child: const TestWidget(), |
6214 | /// ), |
6215 | /// ), |
6216 | /// ); |
6217 | /// ``` |
6218 | /// {@end-tool} |
6219 | /// |
6220 | /// Assuming that `TestWidget` uses [DefaultAssetBundle.of] to obtain its |
6221 | /// [AssetBundle], it will now see the `TestAssetBundle`'s "Hello World!" data |
6222 | /// when requesting the "resources/test" asset. |
6223 | /// |
6224 | /// See also: |
6225 | /// |
6226 | /// * [AssetBundle], the interface for asset bundles. |
6227 | /// * [rootBundle], the default asset bundle. |
6228 | class DefaultAssetBundle extends InheritedWidget { |
6229 | /// Creates a widget that determines the default asset bundle for its descendants. |
6230 | const DefaultAssetBundle({ |
6231 | super.key, |
6232 | required this.bundle, |
6233 | required super.child, |
6234 | }); |
6235 | |
6236 | /// The bundle to use as a default. |
6237 | final AssetBundle bundle; |
6238 | |
6239 | /// The bundle from the closest instance of this class that encloses |
6240 | /// the given context. |
6241 | /// |
6242 | /// If there is no [DefaultAssetBundle] ancestor widget in the tree |
6243 | /// at the given context, then this will return the [rootBundle]. |
6244 | /// |
6245 | /// Typical usage is as follows: |
6246 | /// |
6247 | /// ```dart |
6248 | /// AssetBundle bundle = DefaultAssetBundle.of(context); |
6249 | /// ``` |
6250 | static AssetBundle of(BuildContext context) { |
6251 | final DefaultAssetBundle? result = context.dependOnInheritedWidgetOfExactType<DefaultAssetBundle>(); |
6252 | return result?.bundle ?? rootBundle; |
6253 | } |
6254 | |
6255 | @override |
6256 | bool updateShouldNotify(DefaultAssetBundle oldWidget) => bundle != oldWidget.bundle; |
6257 | } |
6258 | |
6259 | /// An adapter for placing a specific [RenderBox] in the widget tree. |
6260 | /// |
6261 | /// A given render object can be placed at most once in the widget tree. This |
6262 | /// widget enforces that restriction by keying itself using a [GlobalObjectKey] |
6263 | /// for the given render object. |
6264 | /// |
6265 | /// This widget will call [RenderObject.dispose] on the [renderBox] when it is |
6266 | /// unmounted. After that point, the [renderBox] will be unusable. If any |
6267 | /// children have been added to the [renderBox], they must be disposed in the |
6268 | /// [onUnmount] callback. |
6269 | class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget { |
6270 | /// Creates an adapter for placing a specific [RenderBox] in the widget tree. |
6271 | WidgetToRenderBoxAdapter({ |
6272 | required this.renderBox, |
6273 | this.onBuild, |
6274 | this.onUnmount, |
6275 | }) : super(key: GlobalObjectKey(renderBox)); |
6276 | |
6277 | /// The render box to place in the widget tree. |
6278 | /// |
6279 | /// This widget takes ownership of the render object. When it is unmounted, |
6280 | /// it also calls [RenderObject.dispose]. |
6281 | final RenderBox renderBox; |
6282 | |
6283 | /// Called when it is safe to update the render box and its descendants. If |
6284 | /// you update the RenderObject subtree under this widget outside of |
6285 | /// invocations of this callback, features like hit-testing will fail as the |
6286 | /// tree will be dirty. |
6287 | final VoidCallback? onBuild; |
6288 | |
6289 | /// Called when it is safe to dispose of children that were manually added to |
6290 | /// the [renderBox]. |
6291 | /// |
6292 | /// Do not dispose the [renderBox] itself, as it will be disposed by the |
6293 | /// framework automatically. However, during that process the framework will |
6294 | /// check that all children of the [renderBox] have also been disposed. |
6295 | /// Typically, child [RenderObject]s are disposed by corresponding [Element]s |
6296 | /// when they are unmounted. However, child render objects that were manually |
6297 | /// added do not have corresponding [Element]s to manage their lifecycle, and |
6298 | /// need to be manually disposed here. |
6299 | /// |
6300 | /// See also: |
6301 | /// |
6302 | /// * [RenderObjectElement.unmount], which invokes this callback before |
6303 | /// disposing of its render object. |
6304 | /// * [RenderObject.dispose], which instructs a render object to release |
6305 | /// any resources it may be holding. |
6306 | final VoidCallback? onUnmount; |
6307 | |
6308 | @override |
6309 | RenderBox createRenderObject(BuildContext context) => renderBox; |
6310 | |
6311 | @override |
6312 | void updateRenderObject(BuildContext context, RenderBox renderObject) { |
6313 | onBuild?.call(); |
6314 | } |
6315 | |
6316 | @override |
6317 | void didUnmountRenderObject(RenderObject renderObject) { |
6318 | assert(renderObject == renderBox); |
6319 | onUnmount?.call(); |
6320 | } |
6321 | } |
6322 | |
6323 | |
6324 | // EVENT HANDLING |
6325 | |
6326 | /// A widget that calls callbacks in response to common pointer events. |
6327 | /// |
6328 | /// It listens to events that can construct gestures, such as when the |
6329 | /// pointer is pressed, moved, then released or canceled. |
6330 | /// |
6331 | /// It does not listen to events that are exclusive to mouse, such as when the |
6332 | /// mouse enters, exits or hovers a region without pressing any buttons. For |
6333 | /// these events, use [MouseRegion]. |
6334 | /// |
6335 | /// Rather than listening for raw pointer events, consider listening for |
6336 | /// higher-level gestures using [GestureDetector]. |
6337 | /// |
6338 | /// ## Layout behavior |
6339 | /// |
6340 | /// _See [BoxConstraints] for an introduction to box layout models._ |
6341 | /// |
6342 | /// If it has a child, this widget defers to the child for sizing behavior. If |
6343 | /// it does not have a child, it grows to fit the parent instead. |
6344 | /// |
6345 | /// {@tool dartpad} |
6346 | /// This example makes a [Container] react to being touched, showing a count of |
6347 | /// the number of pointer downs and ups. |
6348 | /// |
6349 | /// ** See code in examples/api/lib/widgets/basic/listener.0.dart ** |
6350 | /// {@end-tool} |
6351 | class Listener extends SingleChildRenderObjectWidget { |
6352 | /// Creates a widget that forwards point events to callbacks. |
6353 | /// |
6354 | /// The [behavior] argument defaults to [HitTestBehavior.deferToChild]. |
6355 | const Listener({ |
6356 | super.key, |
6357 | this.onPointerDown, |
6358 | this.onPointerMove, |
6359 | this.onPointerUp, |
6360 | this.onPointerHover, |
6361 | this.onPointerCancel, |
6362 | this.onPointerPanZoomStart, |
6363 | this.onPointerPanZoomUpdate, |
6364 | this.onPointerPanZoomEnd, |
6365 | this.onPointerSignal, |
6366 | this.behavior = HitTestBehavior.deferToChild, |
6367 | super.child, |
6368 | }); |
6369 | |
6370 | /// Called when a pointer comes into contact with the screen (for touch |
6371 | /// pointers), or has its button pressed (for mouse pointers) at this widget's |
6372 | /// location. |
6373 | final PointerDownEventListener? onPointerDown; |
6374 | |
6375 | /// Called when a pointer that triggered an [onPointerDown] changes position. |
6376 | final PointerMoveEventListener? onPointerMove; |
6377 | |
6378 | /// Called when a pointer that triggered an [onPointerDown] is no longer in |
6379 | /// contact with the screen. |
6380 | final PointerUpEventListener? onPointerUp; |
6381 | |
6382 | /// Called when a pointer that has not triggered an [onPointerDown] changes |
6383 | /// position. |
6384 | /// |
6385 | /// This is only fired for pointers which report their location when not down |
6386 | /// (e.g. mouse pointers, but not most touch pointers). |
6387 | final PointerHoverEventListener? onPointerHover; |
6388 | |
6389 | /// Called when the input from a pointer that triggered an [onPointerDown] is |
6390 | /// no longer directed towards this receiver. |
6391 | final PointerCancelEventListener? onPointerCancel; |
6392 | |
6393 | /// Called when a pan/zoom begins such as from a trackpad gesture. |
6394 | final PointerPanZoomStartEventListener? onPointerPanZoomStart; |
6395 | |
6396 | /// Called when a pan/zoom is updated. |
6397 | final PointerPanZoomUpdateEventListener? onPointerPanZoomUpdate; |
6398 | |
6399 | /// Called when a pan/zoom finishes. |
6400 | final PointerPanZoomEndEventListener? onPointerPanZoomEnd; |
6401 | |
6402 | /// Called when a pointer signal occurs over this object. |
6403 | /// |
6404 | /// See also: |
6405 | /// |
6406 | /// * [PointerSignalEvent], which goes into more detail on pointer signal |
6407 | /// events. |
6408 | final PointerSignalEventListener? onPointerSignal; |
6409 | |
6410 | /// How to behave during hit testing. |
6411 | final HitTestBehavior behavior; |
6412 | |
6413 | @override |
6414 | RenderPointerListener createRenderObject(BuildContext context) { |
6415 | return RenderPointerListener( |
6416 | onPointerDown: onPointerDown, |
6417 | onPointerMove: onPointerMove, |
6418 | onPointerUp: onPointerUp, |
6419 | onPointerHover: onPointerHover, |
6420 | onPointerCancel: onPointerCancel, |
6421 | onPointerPanZoomStart: onPointerPanZoomStart, |
6422 | onPointerPanZoomUpdate: onPointerPanZoomUpdate, |
6423 | onPointerPanZoomEnd: onPointerPanZoomEnd, |
6424 | onPointerSignal: onPointerSignal, |
6425 | behavior: behavior, |
6426 | ); |
6427 | } |
6428 | |
6429 | @override |
6430 | void updateRenderObject(BuildContext context, RenderPointerListener renderObject) { |
6431 | renderObject |
6432 | ..onPointerDown = onPointerDown |
6433 | ..onPointerMove = onPointerMove |
6434 | ..onPointerUp = onPointerUp |
6435 | ..onPointerHover = onPointerHover |
6436 | ..onPointerCancel = onPointerCancel |
6437 | ..onPointerPanZoomStart = onPointerPanZoomStart |
6438 | ..onPointerPanZoomUpdate = onPointerPanZoomUpdate |
6439 | ..onPointerPanZoomEnd = onPointerPanZoomEnd |
6440 | ..onPointerSignal = onPointerSignal |
6441 | ..behavior = behavior; |
6442 | } |
6443 | |
6444 | @override |
6445 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
6446 | super.debugFillProperties(properties); |
6447 | final List<String> listeners = <String>[ |
6448 | if (onPointerDown != null) 'down' , |
6449 | if (onPointerMove != null) 'move' , |
6450 | if (onPointerUp != null) 'up' , |
6451 | if (onPointerHover != null) 'hover' , |
6452 | if (onPointerCancel != null) 'cancel' , |
6453 | if (onPointerPanZoomStart != null) 'panZoomStart' , |
6454 | if (onPointerPanZoomUpdate != null) 'panZoomUpdate' , |
6455 | if (onPointerPanZoomEnd != null) 'panZoomEnd' , |
6456 | if (onPointerSignal != null) 'signal' , |
6457 | ]; |
6458 | properties.add(IterableProperty<String>('listeners' , listeners, ifEmpty: '<none>' )); |
6459 | properties.add(EnumProperty<HitTestBehavior>('behavior' , behavior)); |
6460 | } |
6461 | } |
6462 | |
6463 | /// A widget that tracks the movement of mice. |
6464 | /// |
6465 | /// {@youtube 560 315 https://www.youtube.com/watch?v=1oF3pI5umck} |
6466 | /// |
6467 | /// [MouseRegion] is used |
6468 | /// when it is needed to compare the list of objects that a mouse pointer is |
6469 | /// hovering over between this frame and the last frame. This means entering |
6470 | /// events, exiting events, and mouse cursors. |
6471 | /// |
6472 | /// To listen to general pointer events, use [Listener], or more preferably, |
6473 | /// [GestureDetector]. |
6474 | /// |
6475 | /// ## Layout behavior |
6476 | /// |
6477 | /// _See [BoxConstraints] for an introduction to box layout models._ |
6478 | /// |
6479 | /// If it has a child, this widget defers to the child for sizing behavior. If |
6480 | /// it does not have a child, it grows to fit the parent instead. |
6481 | /// |
6482 | /// {@tool dartpad} |
6483 | /// This example makes a [Container] react to being entered by a mouse |
6484 | /// pointer, showing a count of the number of entries and exits. |
6485 | /// |
6486 | /// ** See code in examples/api/lib/widgets/basic/mouse_region.0.dart ** |
6487 | /// {@end-tool} |
6488 | /// |
6489 | /// See also: |
6490 | /// |
6491 | /// * [Listener], a similar widget that tracks pointer events when the pointer |
6492 | /// has buttons pressed. |
6493 | class MouseRegion extends SingleChildRenderObjectWidget { |
6494 | /// Creates a widget that forwards mouse events to callbacks. |
6495 | /// |
6496 | /// By default, all callbacks are empty, [cursor] is [MouseCursor.defer], and |
6497 | /// [opaque] is true. |
6498 | const MouseRegion({ |
6499 | super.key, |
6500 | this.onEnter, |
6501 | this.onExit, |
6502 | this.onHover, |
6503 | this.cursor = MouseCursor.defer, |
6504 | this.opaque = true, |
6505 | this.hitTestBehavior, |
6506 | super.child, |
6507 | }); |
6508 | |
6509 | /// Triggered when a mouse pointer has entered this widget. |
6510 | /// |
6511 | /// This callback is triggered when the pointer, with or without buttons |
6512 | /// pressed, has started to be contained by the region of this widget. More |
6513 | /// specifically, the callback is triggered by the following cases: |
6514 | /// |
6515 | /// * This widget has appeared under a pointer. |
6516 | /// * This widget has moved to under a pointer. |
6517 | /// * A new pointer has been added to somewhere within this widget. |
6518 | /// * An existing pointer has moved into this widget. |
6519 | /// |
6520 | /// This callback is not always matched by an [onExit]. If the [MouseRegion] |
6521 | /// is unmounted while being hovered by a pointer, the [onExit] of the widget |
6522 | /// callback will never called. For more details, see [onExit]. |
6523 | /// |
6524 | /// {@template flutter.widgets.MouseRegion.onEnter.triggerTime} |
6525 | /// The time that this callback is triggered is always between frames: either |
6526 | /// during the post-frame callbacks, or during the callback of a pointer |
6527 | /// event. |
6528 | /// {@endtemplate} |
6529 | /// |
6530 | /// See also: |
6531 | /// |
6532 | /// * [onExit], which is triggered when a mouse pointer exits the region. |
6533 | /// * [MouseTrackerAnnotation.onEnter], which is how this callback is |
6534 | /// internally implemented. |
6535 | final PointerEnterEventListener? onEnter; |
6536 | |
6537 | /// Triggered when a pointer moves into a position within this widget without |
6538 | /// buttons pressed. |
6539 | /// |
6540 | /// Usually this is only fired for pointers which report their location when |
6541 | /// not down (e.g. mouse pointers). Certain devices also fire this event on |
6542 | /// single taps in accessibility mode. |
6543 | /// |
6544 | /// This callback is not triggered by the movement of the widget. |
6545 | /// |
6546 | /// The time that this callback is triggered is during the callback of a |
6547 | /// pointer event, which is always between frames. |
6548 | /// |
6549 | /// See also: |
6550 | /// |
6551 | /// * [Listener.onPointerHover], which does the same job. Prefer using |
6552 | /// [Listener.onPointerHover], since hover events are similar to other regular |
6553 | /// events. |
6554 | final PointerHoverEventListener? onHover; |
6555 | |
6556 | /// Triggered when a mouse pointer has exited this widget when the widget is |
6557 | /// still mounted. |
6558 | /// |
6559 | /// This callback is triggered when the pointer, with or without buttons |
6560 | /// pressed, has stopped being contained by the region of this widget, except |
6561 | /// when the exit is caused by the disappearance of this widget. More |
6562 | /// specifically, this callback is triggered by the following cases: |
6563 | /// |
6564 | /// * A pointer that is hovering this widget has moved away. |
6565 | /// * A pointer that is hovering this widget has been removed. |
6566 | /// * This widget, which is being hovered by a pointer, has moved away. |
6567 | /// |
6568 | /// And is __not__ triggered by the following case: |
6569 | /// |
6570 | /// * This widget, which is being hovered by a pointer, has disappeared. |
6571 | /// |
6572 | /// This means that a [MouseRegion.onExit] might not be matched by a |
6573 | /// [MouseRegion.onEnter]. |
6574 | /// |
6575 | /// This restriction aims to prevent a common misuse: if [State.setState] is |
6576 | /// called during [MouseRegion.onExit] without checking whether the widget is |
6577 | /// still mounted, an exception will occur. This is because the callback is |
6578 | /// triggered during the post-frame phase, at which point the widget has been |
6579 | /// unmounted. Since [State.setState] is exclusive to widgets, the restriction |
6580 | /// is specific to [MouseRegion], and does not apply to its lower-level |
6581 | /// counterparts, [RenderMouseRegion] and [MouseTrackerAnnotation]. |
6582 | /// |
6583 | /// There are a few ways to mitigate this restriction: |
6584 | /// |
6585 | /// * If the hover state is completely contained within a widget that |
6586 | /// unconditionally creates this [MouseRegion], then this will not be a |
6587 | /// concern, since after the [MouseRegion] is unmounted the state is no |
6588 | /// longer used. |
6589 | /// * Otherwise, the outer widget very likely has access to the variable that |
6590 | /// controls whether this [MouseRegion] is present. If so, call [onExit] at |
6591 | /// the event that turns the condition from true to false. |
6592 | /// * In cases where the solutions above won't work, you can always |
6593 | /// override [State.dispose] and call [onExit], or create your own widget |
6594 | /// using [RenderMouseRegion]. |
6595 | /// |
6596 | /// {@tool dartpad} |
6597 | /// The following example shows a blue rectangular that turns yellow when |
6598 | /// hovered. Since the hover state is completely contained within a widget |
6599 | /// that unconditionally creates the `MouseRegion`, you can ignore the |
6600 | /// aforementioned restriction. |
6601 | /// |
6602 | /// ** See code in examples/api/lib/widgets/basic/mouse_region.on_exit.0.dart ** |
6603 | /// {@end-tool} |
6604 | /// |
6605 | /// {@tool dartpad} |
6606 | /// The following example shows a widget that hides its content one second |
6607 | /// after being hovered, and also exposes the enter and exit callbacks. |
6608 | /// Because the widget conditionally creates the `MouseRegion`, and leaks the |
6609 | /// hover state, it needs to take the restriction into consideration. In this |
6610 | /// case, since it has access to the event that triggers the disappearance of |
6611 | /// the `MouseRegion`, it triggers the exit callback during that event |
6612 | /// as well. |
6613 | /// |
6614 | /// ** See code in examples/api/lib/widgets/basic/mouse_region.on_exit.1.dart ** |
6615 | /// {@end-tool} |
6616 | /// |
6617 | /// {@macro flutter.widgets.MouseRegion.onEnter.triggerTime} |
6618 | /// |
6619 | /// See also: |
6620 | /// |
6621 | /// * [onEnter], which is triggered when a mouse pointer enters the region. |
6622 | /// * [RenderMouseRegion] and [MouseTrackerAnnotation.onExit], which are how |
6623 | /// this callback is internally implemented, but without the restriction. |
6624 | final PointerExitEventListener? onExit; |
6625 | |
6626 | /// The mouse cursor for mouse pointers that are hovering over the region. |
6627 | /// |
6628 | /// When a mouse enters the region, its cursor will be changed to the [cursor]. |
6629 | /// When the mouse leaves the region, the cursor will be decided by the region |
6630 | /// found at the new location. |
6631 | /// |
6632 | /// The [cursor] defaults to [MouseCursor.defer], deferring the choice of |
6633 | /// cursor to the next region behind it in hit-test order. |
6634 | final MouseCursor cursor; |
6635 | |
6636 | /// Whether this widget should prevent other [MouseRegion]s visually behind it |
6637 | /// from detecting the pointer. |
6638 | /// |
6639 | /// This changes the list of regions that a pointer hovers, thus affecting how |
6640 | /// their [onHover], [onEnter], [onExit], and [cursor] behave. |
6641 | /// |
6642 | /// If [opaque] is true, this widget will absorb the mouse pointer and |
6643 | /// prevent this widget's siblings (or any other widgets that are not |
6644 | /// ancestors or descendants of this widget) from detecting the mouse |
6645 | /// pointer even when the pointer is within their areas. |
6646 | /// |
6647 | /// If [opaque] is false, this object will not affect how [MouseRegion]s |
6648 | /// behind it behave, which will detect the mouse pointer as long as the |
6649 | /// pointer is within their areas. |
6650 | /// |
6651 | /// This defaults to true. |
6652 | final bool opaque; |
6653 | |
6654 | /// How to behave during hit testing. |
6655 | /// |
6656 | /// This defaults to [HitTestBehavior.opaque] if null. |
6657 | final HitTestBehavior? hitTestBehavior; |
6658 | |
6659 | @override |
6660 | RenderMouseRegion createRenderObject(BuildContext context) { |
6661 | return RenderMouseRegion( |
6662 | onEnter: onEnter, |
6663 | onHover: onHover, |
6664 | onExit: onExit, |
6665 | cursor: cursor, |
6666 | opaque: opaque, |
6667 | hitTestBehavior: hitTestBehavior, |
6668 | ); |
6669 | } |
6670 | |
6671 | @override |
6672 | void updateRenderObject(BuildContext context, RenderMouseRegion renderObject) { |
6673 | renderObject |
6674 | ..onEnter = onEnter |
6675 | ..onHover = onHover |
6676 | ..onExit = onExit |
6677 | ..cursor = cursor |
6678 | ..opaque = opaque |
6679 | ..hitTestBehavior = hitTestBehavior; |
6680 | } |
6681 | |
6682 | @override |
6683 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
6684 | super.debugFillProperties(properties); |
6685 | final List<String> listeners = <String>[]; |
6686 | if (onEnter != null) { |
6687 | listeners.add('enter' ); |
6688 | } |
6689 | if (onExit != null) { |
6690 | listeners.add('exit' ); |
6691 | } |
6692 | if (onHover != null) { |
6693 | listeners.add('hover' ); |
6694 | } |
6695 | properties.add(IterableProperty<String>('listeners' , listeners, ifEmpty: '<none>' )); |
6696 | properties.add(DiagnosticsProperty<MouseCursor>('cursor' , cursor, defaultValue: null)); |
6697 | properties.add(DiagnosticsProperty<bool>('opaque' , opaque, defaultValue: true)); |
6698 | } |
6699 | } |
6700 | |
6701 | /// A widget that creates a separate display list for its child. |
6702 | /// |
6703 | /// {@youtube 560 315 https://www.youtube.com/watch?v=cVAGLDuc2xE} |
6704 | /// |
6705 | /// This widget creates a separate display list for its child, which |
6706 | /// can improve performance if the subtree repaints at different times than |
6707 | /// the surrounding parts of the tree. |
6708 | /// |
6709 | /// This is useful since [RenderObject.paint] may be triggered even if its |
6710 | /// associated [Widget] instances did not change or rebuild. A [RenderObject] |
6711 | /// will repaint whenever any [RenderObject] that shares the same [Layer] is |
6712 | /// marked as being dirty and needing paint (see [RenderObject.markNeedsPaint]), |
6713 | /// such as when an ancestor scrolls or when an ancestor or descendant animates. |
6714 | /// |
6715 | /// Containing [RenderObject.paint] to parts of the render subtree that are |
6716 | /// actually visually changing using [RepaintBoundary] explicitly or implicitly |
6717 | /// is therefore critical to minimizing redundant work and improving the app's |
6718 | /// performance. |
6719 | /// |
6720 | /// When a [RenderObject] is flagged as needing to paint via |
6721 | /// [RenderObject.markNeedsPaint], the nearest ancestor [RenderObject] with |
6722 | /// [RenderObject.isRepaintBoundary], up to possibly the root of the application, |
6723 | /// is requested to repaint. That nearest ancestor's [RenderObject.paint] method |
6724 | /// will cause _all_ of its descendant [RenderObject]s to repaint in the same |
6725 | /// layer. |
6726 | /// |
6727 | /// [RepaintBoundary] is therefore used, both while propagating the |
6728 | /// `markNeedsPaint` flag up the render tree and while traversing down the |
6729 | /// render tree via [PaintingContext.paintChild], to strategically contain |
6730 | /// repaints to the render subtree that visually changed for performance. This |
6731 | /// is done because the [RepaintBoundary] widget creates a [RenderObject] that |
6732 | /// always has a [Layer], decoupling ancestor render objects from the descendant |
6733 | /// render objects. |
6734 | /// |
6735 | /// [RepaintBoundary] has the further side-effect of possibly hinting to the |
6736 | /// engine that it should further optimize animation performance if the render |
6737 | /// subtree behind the [RepaintBoundary] is sufficiently complex and is static |
6738 | /// while the surrounding tree changes frequently. In those cases, the engine |
6739 | /// may choose to pay a one time cost of rasterizing and caching the pixel |
6740 | /// values of the subtree for faster future GPU re-rendering speed. |
6741 | /// |
6742 | /// Several framework widgets insert [RepaintBoundary] widgets to mark natural |
6743 | /// separation points in applications. For instance, contents in Material Design |
6744 | /// drawers typically don't change while the drawer opens and closes, so |
6745 | /// repaints are automatically contained to regions inside or outside the drawer |
6746 | /// when using the [Drawer] widget during transitions. |
6747 | /// |
6748 | /// See also: |
6749 | /// |
6750 | /// * [debugRepaintRainbowEnabled], a debugging flag to help visually monitor |
6751 | /// render tree repaints in a running app. |
6752 | /// * [debugProfilePaintsEnabled], a debugging flag to show render tree |
6753 | /// repaints in the observatory's timeline view. |
6754 | class RepaintBoundary extends SingleChildRenderObjectWidget { |
6755 | /// Creates a widget that isolates repaints. |
6756 | const RepaintBoundary({ super.key, super.child }); |
6757 | |
6758 | /// Wraps the given child in a [RepaintBoundary]. |
6759 | /// |
6760 | /// The key for the [RepaintBoundary] is derived either from the child's key |
6761 | /// (if the child has a non-null key) or from the given `childIndex`. |
6762 | factory RepaintBoundary.wrap(Widget child, int childIndex) { |
6763 | final Key key = child.key != null ? ValueKey<Key>(child.key!) : ValueKey<int>(childIndex); |
6764 | return RepaintBoundary(key: key, child: child); |
6765 | } |
6766 | |
6767 | /// Wraps each of the given children in [RepaintBoundary]s. |
6768 | /// |
6769 | /// The key for each [RepaintBoundary] is derived either from the wrapped |
6770 | /// child's key (if the wrapped child has a non-null key) or from the wrapped |
6771 | /// child's index in the list. |
6772 | static List<RepaintBoundary> wrapAll(List<Widget> widgets) => <RepaintBoundary>[ |
6773 | for (int i = 0; i < widgets.length; ++i) RepaintBoundary.wrap(widgets[i], i), |
6774 | ]; |
6775 | |
6776 | @override |
6777 | RenderRepaintBoundary createRenderObject(BuildContext context) => RenderRepaintBoundary(); |
6778 | } |
6779 | |
6780 | /// A widget that is invisible during hit testing. |
6781 | /// |
6782 | /// When [ignoring] is true, this widget (and its subtree) is invisible |
6783 | /// to hit testing. It still consumes space during layout and paints its child |
6784 | /// as usual. It just cannot be the target of located events, because it returns |
6785 | /// false from [RenderBox.hitTest]. |
6786 | /// |
6787 | /// {@youtube 560 315 https://www.youtube.com/watch?v=qV9pqHWxYgI} |
6788 | /// |
6789 | /// {@tool dartpad} |
6790 | /// The following sample has an [IgnorePointer] widget wrapping the `Column` |
6791 | /// which contains a button. |
6792 | /// When [ignoring] is set to `true` anything inside the `Column` can |
6793 | /// not be tapped. When [ignoring] is set to `false` anything |
6794 | /// inside the `Column` can be tapped. |
6795 | /// |
6796 | /// ** See code in examples/api/lib/widgets/basic/ignore_pointer.0.dart ** |
6797 | /// {@end-tool} |
6798 | /// |
6799 | /// ## Semantics |
6800 | /// |
6801 | /// Using this class may also affect how the semantics subtree underneath is |
6802 | /// collected. |
6803 | /// |
6804 | /// {@template flutter.widgets.IgnorePointer.semantics} |
6805 | /// If [ignoring] is true, pointer-related [SemanticsAction]s are removed from |
6806 | /// the semantics subtree. Otherwise, the subtree remains untouched. |
6807 | /// {@endtemplate} |
6808 | /// |
6809 | /// {@template flutter.widgets.IgnorePointer.ignoringSemantics} |
6810 | /// The usages of [ignoringSemantics] are deprecated and not recommended. This |
6811 | /// property was introduced to workaround the semantics behavior of the |
6812 | /// [IgnorePointer] and its friends before v3.8.0-12.0.pre. |
6813 | /// |
6814 | /// Before that version, entire semantics subtree is dropped if [ignoring] is |
6815 | /// true. Developers can only use [ignoringSemantics] to preserver the semantics |
6816 | /// subtrees. |
6817 | /// |
6818 | /// After that version, with [ignoring] set to true, it only prevents semantics |
6819 | /// user actions in the semantics subtree but leaves the other |
6820 | /// [SemanticsProperties] intact. Therefore, the [ignoringSemantics] is no |
6821 | /// longer needed. |
6822 | /// |
6823 | /// If [ignoringSemantics] is true, the semantics subtree is dropped. Therefore, |
6824 | /// the subtree will be invisible to assistive technologies. |
6825 | /// |
6826 | /// If [ignoringSemantics] is false, the semantics subtree is collected as |
6827 | /// usual. |
6828 | /// {@endtemplate} |
6829 | /// |
6830 | /// See also: |
6831 | /// |
6832 | /// * [AbsorbPointer], which also prevents its children from receiving pointer |
6833 | /// events but is itself visible to hit testing. |
6834 | /// * [SliverIgnorePointer], the sliver version of this widget. |
6835 | class IgnorePointer extends SingleChildRenderObjectWidget { |
6836 | /// Creates a widget that is invisible to hit testing. |
6837 | const IgnorePointer({ |
6838 | super.key, |
6839 | this.ignoring = true, |
6840 | @Deprecated( |
6841 | 'Use ExcludeSemantics or create a custom ignore pointer widget instead. ' |
6842 | 'This feature was deprecated after v3.8.0-12.0.pre.' |
6843 | ) |
6844 | this.ignoringSemantics, |
6845 | super.child, |
6846 | }); |
6847 | |
6848 | /// Whether this widget is ignored during hit testing. |
6849 | /// |
6850 | /// Regardless of whether this widget is ignored during hit testing, it will |
6851 | /// still consume space during layout and be visible during painting. |
6852 | /// |
6853 | /// {@macro flutter.widgets.IgnorePointer.semantics} |
6854 | /// |
6855 | /// Defaults to true. |
6856 | final bool ignoring; |
6857 | |
6858 | /// Whether the semantics of this widget is ignored when compiling the |
6859 | /// semantics subtree. |
6860 | /// |
6861 | /// {@macro flutter.widgets.IgnorePointer.ignoringSemantics} |
6862 | /// |
6863 | /// See [SemanticsNode] for additional information about the semantics tree. |
6864 | @Deprecated( |
6865 | 'Use ExcludeSemantics or create a custom ignore pointer widget instead. ' |
6866 | 'This feature was deprecated after v3.8.0-12.0.pre.' |
6867 | ) |
6868 | final bool? ignoringSemantics; |
6869 | |
6870 | @override |
6871 | RenderIgnorePointer createRenderObject(BuildContext context) { |
6872 | return RenderIgnorePointer( |
6873 | ignoring: ignoring, |
6874 | ignoringSemantics: ignoringSemantics, |
6875 | ); |
6876 | } |
6877 | |
6878 | @override |
6879 | void updateRenderObject(BuildContext context, RenderIgnorePointer renderObject) { |
6880 | renderObject |
6881 | ..ignoring = ignoring |
6882 | ..ignoringSemantics = ignoringSemantics; |
6883 | } |
6884 | |
6885 | @override |
6886 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
6887 | super.debugFillProperties(properties); |
6888 | properties.add(DiagnosticsProperty<bool>('ignoring' , ignoring)); |
6889 | properties.add(DiagnosticsProperty<bool>('ignoringSemantics' , ignoringSemantics, defaultValue: null)); |
6890 | } |
6891 | } |
6892 | |
6893 | /// A widget that absorbs pointers during hit testing. |
6894 | /// |
6895 | /// When [absorbing] is true, this widget prevents its subtree from receiving |
6896 | /// pointer events by terminating hit testing at itself. It still consumes space |
6897 | /// during layout and paints its child as usual. It just prevents its children |
6898 | /// from being the target of located events, because it returns true from |
6899 | /// [RenderBox.hitTest]. |
6900 | /// |
6901 | /// When [ignoringSemantics] is true, the subtree will be invisible to |
6902 | /// the semantics layer (and thus e.g. accessibility tools). |
6903 | /// |
6904 | /// {@youtube 560 315 https://www.youtube.com/watch?v=65HoWqBboI8} |
6905 | /// |
6906 | /// {@tool dartpad} |
6907 | /// The following sample has an [AbsorbPointer] widget wrapping the button on |
6908 | /// top of the stack, which absorbs pointer events, preventing its child button |
6909 | /// __and__ the button below it in the stack from receiving the pointer events. |
6910 | /// |
6911 | /// ** See code in examples/api/lib/widgets/basic/absorb_pointer.0.dart ** |
6912 | /// {@end-tool} |
6913 | /// |
6914 | /// ## Semantics |
6915 | /// |
6916 | /// Using this class may also affect how the semantics subtree underneath is |
6917 | /// collected. |
6918 | /// |
6919 | /// {@template flutter.widgets.AbsorbPointer.semantics} |
6920 | /// If [absorbing] is true, pointer-related [SemanticsAction]s are removed from |
6921 | /// the semantics subtree. Otherwise, the subtree remains untouched. |
6922 | /// {@endtemplate} |
6923 | /// |
6924 | /// {@template flutter.widgets.AbsorbPointer.ignoringSemantics} |
6925 | /// The usages of [ignoringSemantics] are deprecated and not recommended. This |
6926 | /// property was introduced to workaround the semantics behavior of the |
6927 | /// [IgnorePointer] and its friends before v3.8.0-12.0.pre. |
6928 | /// |
6929 | /// Before that version, entire semantics subtree is dropped if [absorbing] is |
6930 | /// true. Developers can only use [ignoringSemantics] to preserver the semantics |
6931 | /// subtrees. |
6932 | /// |
6933 | /// After that version, with [absorbing] set to true, it only prevents semantics |
6934 | /// user actions in the semantics subtree but leaves the other |
6935 | /// [SemanticsProperties] intact. Therefore, the [ignoringSemantics] is no |
6936 | /// longer needed. |
6937 | /// |
6938 | /// If [ignoringSemantics] is true, the semantics subtree is dropped. Therefore, |
6939 | /// the subtree will be invisible to assistive technologies. |
6940 | /// |
6941 | /// If [ignoringSemantics] is false, the semantics subtree is collected as |
6942 | /// usual. |
6943 | /// {@endtemplate} |
6944 | /// |
6945 | /// See also: |
6946 | /// |
6947 | /// * [IgnorePointer], which also prevents its children from receiving pointer |
6948 | /// events but is itself invisible to hit testing. |
6949 | class AbsorbPointer extends SingleChildRenderObjectWidget { |
6950 | /// Creates a widget that absorbs pointers during hit testing. |
6951 | const AbsorbPointer({ |
6952 | super.key, |
6953 | this.absorbing = true, |
6954 | @Deprecated( |
6955 | 'Use ExcludeSemantics or create a custom absorb pointer widget instead. ' |
6956 | 'This feature was deprecated after v3.8.0-12.0.pre.' |
6957 | ) |
6958 | this.ignoringSemantics, |
6959 | super.child, |
6960 | }); |
6961 | |
6962 | /// Whether this widget absorbs pointers during hit testing. |
6963 | /// |
6964 | /// Regardless of whether this render object absorbs pointers during hit |
6965 | /// testing, it will still consume space during layout and be visible during |
6966 | /// painting. |
6967 | /// |
6968 | /// {@macro flutter.widgets.AbsorbPointer.semantics} |
6969 | /// |
6970 | /// Defaults to true. |
6971 | final bool absorbing; |
6972 | |
6973 | /// Whether the semantics of this render object is ignored when compiling the |
6974 | /// semantics tree. |
6975 | /// |
6976 | /// {@macro flutter.widgets.AbsorbPointer.ignoringSemantics} |
6977 | /// |
6978 | /// See [SemanticsNode] for additional information about the semantics tree. |
6979 | @Deprecated( |
6980 | 'Use ExcludeSemantics or create a custom absorb pointer widget instead. ' |
6981 | 'This feature was deprecated after v3.8.0-12.0.pre.' |
6982 | ) |
6983 | final bool? ignoringSemantics; |
6984 | |
6985 | @override |
6986 | RenderAbsorbPointer createRenderObject(BuildContext context) { |
6987 | return RenderAbsorbPointer( |
6988 | absorbing: absorbing, |
6989 | ignoringSemantics: ignoringSemantics, |
6990 | ); |
6991 | } |
6992 | |
6993 | @override |
6994 | void updateRenderObject(BuildContext context, RenderAbsorbPointer renderObject) { |
6995 | renderObject |
6996 | ..absorbing = absorbing |
6997 | ..ignoringSemantics = ignoringSemantics; |
6998 | } |
6999 | |
7000 | @override |
7001 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
7002 | super.debugFillProperties(properties); |
7003 | properties.add(DiagnosticsProperty<bool>('absorbing' , absorbing)); |
7004 | properties.add(DiagnosticsProperty<bool>('ignoringSemantics' , ignoringSemantics, defaultValue: null)); |
7005 | } |
7006 | } |
7007 | |
7008 | /// Holds opaque meta data in the render tree. |
7009 | /// |
7010 | /// Useful for decorating the render tree with information that will be consumed |
7011 | /// later. For example, you could store information in the render tree that will |
7012 | /// be used when the user interacts with the render tree but has no visual |
7013 | /// impact prior to the interaction. |
7014 | class MetaData extends SingleChildRenderObjectWidget { |
7015 | /// Creates a widget that hold opaque meta data. |
7016 | /// |
7017 | /// The [behavior] argument defaults to [HitTestBehavior.deferToChild]. |
7018 | const MetaData({ |
7019 | super.key, |
7020 | this.metaData, |
7021 | this.behavior = HitTestBehavior.deferToChild, |
7022 | super.child, |
7023 | }); |
7024 | |
7025 | /// Opaque meta data ignored by the render tree. |
7026 | final dynamic metaData; |
7027 | |
7028 | /// How to behave during hit testing. |
7029 | final HitTestBehavior behavior; |
7030 | |
7031 | @override |
7032 | RenderMetaData createRenderObject(BuildContext context) { |
7033 | return RenderMetaData( |
7034 | metaData: metaData, |
7035 | behavior: behavior, |
7036 | ); |
7037 | } |
7038 | |
7039 | @override |
7040 | void updateRenderObject(BuildContext context, RenderMetaData renderObject) { |
7041 | renderObject |
7042 | ..metaData = metaData |
7043 | ..behavior = behavior; |
7044 | } |
7045 | |
7046 | @override |
7047 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
7048 | super.debugFillProperties(properties); |
7049 | properties.add(EnumProperty<HitTestBehavior>('behavior' , behavior)); |
7050 | properties.add(DiagnosticsProperty<dynamic>('metaData' , metaData)); |
7051 | } |
7052 | } |
7053 | |
7054 | |
7055 | // UTILITY NODES |
7056 | |
7057 | /// A widget that annotates the widget tree with a description of the meaning of |
7058 | /// the widgets. |
7059 | /// |
7060 | /// Used by assistive technologies, search engines, and other semantic analysis |
7061 | /// software to determine the meaning of the application. |
7062 | /// |
7063 | /// {@youtube 560 315 https://www.youtube.com/watch?v=NvtMt_DtFrQ} |
7064 | /// |
7065 | /// See also: |
7066 | /// |
7067 | /// * [SemanticsProperties], which contains a complete documentation for each |
7068 | /// of the constructor parameters that belongs to semantics properties. |
7069 | /// * [MergeSemantics], which marks a subtree as being a single node for |
7070 | /// accessibility purposes. |
7071 | /// * [ExcludeSemantics], which excludes a subtree from the semantics tree |
7072 | /// (which might be useful if it is, e.g., totally decorative and not |
7073 | /// important to the user). |
7074 | /// * [RenderObject.describeSemanticsConfiguration], the rendering library API |
7075 | /// through which the [Semantics] widget is actually implemented. |
7076 | /// * [SemanticsNode], the object used by the rendering library to represent |
7077 | /// semantics in the semantics tree. |
7078 | /// * [SemanticsDebugger], an overlay to help visualize the semantics tree. Can |
7079 | /// be enabled using [WidgetsApp.showSemanticsDebugger], |
7080 | /// [MaterialApp.showSemanticsDebugger], or [CupertinoApp.showSemanticsDebugger]. |
7081 | @immutable |
7082 | class Semantics extends SingleChildRenderObjectWidget { |
7083 | /// Creates a semantic annotation. |
7084 | /// |
7085 | /// To create a `const` instance of [Semantics], use the |
7086 | /// [Semantics.fromProperties] constructor. |
7087 | /// |
7088 | /// See also: |
7089 | /// |
7090 | /// * [SemanticsProperties], which contains a complete documentation for each |
7091 | /// of the constructor parameters that belongs to semantics properties. |
7092 | /// * [SemanticsSortKey] for a class that determines accessibility traversal |
7093 | /// order. |
7094 | Semantics({ |
7095 | Key? key, |
7096 | Widget? child, |
7097 | bool container = false, |
7098 | bool explicitChildNodes = false, |
7099 | bool excludeSemantics = false, |
7100 | bool blockUserActions = false, |
7101 | bool? enabled, |
7102 | bool? checked, |
7103 | bool? mixed, |
7104 | bool? selected, |
7105 | bool? toggled, |
7106 | bool? button, |
7107 | bool? slider, |
7108 | bool? keyboardKey, |
7109 | bool? link, |
7110 | bool? header, |
7111 | bool? textField, |
7112 | bool? readOnly, |
7113 | bool? focusable, |
7114 | bool? focused, |
7115 | bool? inMutuallyExclusiveGroup, |
7116 | bool? obscured, |
7117 | bool? multiline, |
7118 | bool? scopesRoute, |
7119 | bool? namesRoute, |
7120 | bool? hidden, |
7121 | bool? image, |
7122 | bool? liveRegion, |
7123 | bool? expanded, |
7124 | int? maxValueLength, |
7125 | int? currentValueLength, |
7126 | String? identifier, |
7127 | String? label, |
7128 | AttributedString? attributedLabel, |
7129 | String? value, |
7130 | AttributedString? attributedValue, |
7131 | String? increasedValue, |
7132 | AttributedString? attributedIncreasedValue, |
7133 | String? decreasedValue, |
7134 | AttributedString? attributedDecreasedValue, |
7135 | String? hint, |
7136 | AttributedString? attributedHint, |
7137 | String? tooltip, |
7138 | String? onTapHint, |
7139 | String? onLongPressHint, |
7140 | TextDirection? textDirection, |
7141 | SemanticsSortKey? sortKey, |
7142 | SemanticsTag? tagForChildren, |
7143 | VoidCallback? onTap, |
7144 | VoidCallback? onLongPress, |
7145 | VoidCallback? onScrollLeft, |
7146 | VoidCallback? onScrollRight, |
7147 | VoidCallback? onScrollUp, |
7148 | VoidCallback? onScrollDown, |
7149 | VoidCallback? onIncrease, |
7150 | VoidCallback? onDecrease, |
7151 | VoidCallback? onCopy, |
7152 | VoidCallback? onCut, |
7153 | VoidCallback? onPaste, |
7154 | VoidCallback? onDismiss, |
7155 | MoveCursorHandler? onMoveCursorForwardByCharacter, |
7156 | MoveCursorHandler? onMoveCursorBackwardByCharacter, |
7157 | SetSelectionHandler? onSetSelection, |
7158 | SetTextHandler? onSetText, |
7159 | VoidCallback? onDidGainAccessibilityFocus, |
7160 | VoidCallback? onDidLoseAccessibilityFocus, |
7161 | Map<CustomSemanticsAction, VoidCallback>? customSemanticsActions, |
7162 | }) : this.fromProperties( |
7163 | key: key, |
7164 | child: child, |
7165 | container: container, |
7166 | explicitChildNodes: explicitChildNodes, |
7167 | excludeSemantics: excludeSemantics, |
7168 | blockUserActions: blockUserActions, |
7169 | properties: SemanticsProperties( |
7170 | enabled: enabled, |
7171 | checked: checked, |
7172 | mixed: mixed, |
7173 | expanded: expanded, |
7174 | toggled: toggled, |
7175 | selected: selected, |
7176 | button: button, |
7177 | slider: slider, |
7178 | keyboardKey: keyboardKey, |
7179 | link: link, |
7180 | header: header, |
7181 | textField: textField, |
7182 | readOnly: readOnly, |
7183 | focusable: focusable, |
7184 | focused: focused, |
7185 | inMutuallyExclusiveGroup: inMutuallyExclusiveGroup, |
7186 | obscured: obscured, |
7187 | multiline: multiline, |
7188 | scopesRoute: scopesRoute, |
7189 | namesRoute: namesRoute, |
7190 | hidden: hidden, |
7191 | image: image, |
7192 | liveRegion: liveRegion, |
7193 | maxValueLength: maxValueLength, |
7194 | currentValueLength: currentValueLength, |
7195 | identifier: identifier, |
7196 | label: label, |
7197 | attributedLabel: attributedLabel, |
7198 | value: value, |
7199 | attributedValue: attributedValue, |
7200 | increasedValue: increasedValue, |
7201 | attributedIncreasedValue: attributedIncreasedValue, |
7202 | decreasedValue: decreasedValue, |
7203 | attributedDecreasedValue: attributedDecreasedValue, |
7204 | hint: hint, |
7205 | attributedHint: attributedHint, |
7206 | tooltip: tooltip, |
7207 | textDirection: textDirection, |
7208 | sortKey: sortKey, |
7209 | tagForChildren: tagForChildren, |
7210 | onTap: onTap, |
7211 | onLongPress: onLongPress, |
7212 | onScrollLeft: onScrollLeft, |
7213 | onScrollRight: onScrollRight, |
7214 | onScrollUp: onScrollUp, |
7215 | onScrollDown: onScrollDown, |
7216 | onIncrease: onIncrease, |
7217 | onDecrease: onDecrease, |
7218 | onCopy: onCopy, |
7219 | onCut: onCut, |
7220 | onPaste: onPaste, |
7221 | onMoveCursorForwardByCharacter: onMoveCursorForwardByCharacter, |
7222 | onMoveCursorBackwardByCharacter: onMoveCursorBackwardByCharacter, |
7223 | onDidGainAccessibilityFocus: onDidGainAccessibilityFocus, |
7224 | onDidLoseAccessibilityFocus: onDidLoseAccessibilityFocus, |
7225 | onDismiss: onDismiss, |
7226 | onSetSelection: onSetSelection, |
7227 | onSetText: onSetText, |
7228 | customSemanticsActions: customSemanticsActions, |
7229 | hintOverrides: onTapHint != null || onLongPressHint != null ? |
7230 | SemanticsHintOverrides( |
7231 | onTapHint: onTapHint, |
7232 | onLongPressHint: onLongPressHint, |
7233 | ) : null, |
7234 | ), |
7235 | ); |
7236 | |
7237 | /// Creates a semantic annotation using [SemanticsProperties]. |
7238 | const Semantics.fromProperties({ |
7239 | super.key, |
7240 | super.child, |
7241 | this.container = false, |
7242 | this.explicitChildNodes = false, |
7243 | this.excludeSemantics = false, |
7244 | this.blockUserActions = false, |
7245 | required this.properties, |
7246 | }); |
7247 | |
7248 | /// Contains properties used by assistive technologies to make the application |
7249 | /// more accessible. |
7250 | final SemanticsProperties properties; |
7251 | |
7252 | /// If [container] is true, this widget will introduce a new |
7253 | /// node in the semantics tree. Otherwise, the semantics will be |
7254 | /// merged with the semantics of any ancestors (if the ancestor allows that). |
7255 | /// |
7256 | /// Whether descendants of this widget can add their semantic information to the |
7257 | /// [SemanticsNode] introduced by this configuration is controlled by |
7258 | /// [explicitChildNodes]. |
7259 | final bool container; |
7260 | |
7261 | /// Whether descendants of this widget are allowed to add semantic information |
7262 | /// to the [SemanticsNode] annotated by this widget. |
7263 | /// |
7264 | /// When set to false descendants are allowed to annotate [SemanticsNode]s of |
7265 | /// their parent with the semantic information they want to contribute to the |
7266 | /// semantic tree. |
7267 | /// When set to true the only way for descendants to contribute semantic |
7268 | /// information to the semantic tree is to introduce new explicit |
7269 | /// [SemanticsNode]s to the tree. |
7270 | /// |
7271 | /// If the semantics properties of this node include |
7272 | /// [SemanticsProperties.scopesRoute] set to true, then [explicitChildNodes] |
7273 | /// must be true also. |
7274 | /// |
7275 | /// This setting is often used in combination with [SemanticsConfiguration.isSemanticBoundary] |
7276 | /// to create semantic boundaries that are either writable or not for children. |
7277 | final bool explicitChildNodes; |
7278 | |
7279 | /// Whether to replace all child semantics with this node. |
7280 | /// |
7281 | /// Defaults to false. |
7282 | /// |
7283 | /// When this flag is set to true, all child semantics nodes are ignored. |
7284 | /// This can be used as a convenience for cases where a child is wrapped in |
7285 | /// an [ExcludeSemantics] widget and then another [Semantics] widget. |
7286 | final bool excludeSemantics; |
7287 | |
7288 | /// Whether to block user interactions for the rendering subtree. |
7289 | /// |
7290 | /// Setting this to true will prevent users from interacting with The |
7291 | /// rendering object configured by this widget and its subtree through |
7292 | /// pointer-related [SemanticsAction]s in assistive technologies. |
7293 | /// |
7294 | /// The [SemanticsNode] created from this widget is still focusable by |
7295 | /// assistive technologies. Only pointer-related [SemanticsAction]s, such as |
7296 | /// [SemanticsAction.tap] or its friends, are blocked. |
7297 | /// |
7298 | /// If this widget is merged into a parent semantics node, only the |
7299 | /// [SemanticsAction]s of this widget and the widgets in the subtree are |
7300 | /// blocked. |
7301 | /// |
7302 | /// For example: |
7303 | /// ```dart |
7304 | /// void _myTap() { } |
7305 | /// void _myLongPress() { } |
7306 | /// |
7307 | /// Widget build(BuildContext context) { |
7308 | /// return Semantics( |
7309 | /// onTap: _myTap, |
7310 | /// child: Semantics( |
7311 | /// blockUserActions: true, |
7312 | /// onLongPress: _myLongPress, |
7313 | /// child: const Text('label'), |
7314 | /// ), |
7315 | /// ); |
7316 | /// } |
7317 | /// ``` |
7318 | /// |
7319 | /// The result semantics node will still have `_myTap`, but the `_myLongPress` |
7320 | /// will be blocked. |
7321 | final bool blockUserActions; |
7322 | |
7323 | @override |
7324 | RenderSemanticsAnnotations createRenderObject(BuildContext context) { |
7325 | return RenderSemanticsAnnotations( |
7326 | container: container, |
7327 | explicitChildNodes: explicitChildNodes, |
7328 | excludeSemantics: excludeSemantics, |
7329 | blockUserActions: blockUserActions, |
7330 | properties: properties, |
7331 | textDirection: _getTextDirection(context), |
7332 | ); |
7333 | } |
7334 | |
7335 | TextDirection? _getTextDirection(BuildContext context) { |
7336 | if (properties.textDirection != null) { |
7337 | return properties.textDirection; |
7338 | } |
7339 | |
7340 | final bool containsText = properties.attributedLabel != null || |
7341 | properties.label != null || |
7342 | properties.value != null || |
7343 | properties.hint != null || |
7344 | properties.tooltip != null; |
7345 | |
7346 | if (!containsText) { |
7347 | return null; |
7348 | } |
7349 | |
7350 | return Directionality.maybeOf(context); |
7351 | } |
7352 | |
7353 | @override |
7354 | void updateRenderObject(BuildContext context, RenderSemanticsAnnotations renderObject) { |
7355 | renderObject |
7356 | ..container = container |
7357 | ..explicitChildNodes = explicitChildNodes |
7358 | ..excludeSemantics = excludeSemantics |
7359 | ..blockUserActions = blockUserActions |
7360 | ..properties = properties |
7361 | ..textDirection = _getTextDirection(context); |
7362 | } |
7363 | |
7364 | @override |
7365 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
7366 | super.debugFillProperties(properties); |
7367 | properties.add(DiagnosticsProperty<bool>('container' , container)); |
7368 | properties.add(DiagnosticsProperty<SemanticsProperties>('properties' , this.properties)); |
7369 | this.properties.debugFillProperties(properties); |
7370 | } |
7371 | } |
7372 | |
7373 | /// A widget that merges the semantics of its descendants. |
7374 | /// |
7375 | /// Causes all the semantics of the subtree rooted at this node to be |
7376 | /// merged into one node in the semantics tree. For example, if you |
7377 | /// have a widget with a Text node next to a checkbox widget, this |
7378 | /// could be used to merge the label from the Text node with the |
7379 | /// "checked" semantic state of the checkbox into a single node that |
7380 | /// had both the label and the checked state. Otherwise, the label |
7381 | /// would be presented as a separate feature than the checkbox, and |
7382 | /// the user would not be able to be sure that they were related. |
7383 | /// |
7384 | /// {@tool snippet} |
7385 | /// This snippet shows how to use [MergeSemantics] to merge the semantics of |
7386 | /// a [Checkbox] and [Text] widget. |
7387 | /// |
7388 | /// ```dart |
7389 | /// MergeSemantics( |
7390 | /// child: Row( |
7391 | /// children: <Widget>[ |
7392 | /// Checkbox( |
7393 | /// value: true, |
7394 | /// onChanged: (bool? value) {}, |
7395 | /// ), |
7396 | /// const Text('Settings'), |
7397 | /// ], |
7398 | /// ), |
7399 | /// ) |
7400 | /// ``` |
7401 | /// {@end-tool} |
7402 | /// |
7403 | /// Be aware that if two nodes in the subtree have conflicting |
7404 | /// semantics, the result may be nonsensical. For example, a subtree |
7405 | /// with a checked checkbox and an unchecked checkbox will be |
7406 | /// presented as checked. All the labels will be merged into a single |
7407 | /// string (with newlines separating each label from the other). If |
7408 | /// multiple nodes in the merged subtree can handle semantic gestures, |
7409 | /// the first one in tree order will be the one to receive the |
7410 | /// callbacks. |
7411 | class MergeSemantics extends SingleChildRenderObjectWidget { |
7412 | /// Creates a widget that merges the semantics of its descendants. |
7413 | const MergeSemantics({ super.key, super.child }); |
7414 | |
7415 | @override |
7416 | RenderMergeSemantics createRenderObject(BuildContext context) => RenderMergeSemantics(); |
7417 | } |
7418 | |
7419 | /// A widget that drops the semantics of all widget that were painted before it |
7420 | /// in the same semantic container. |
7421 | /// |
7422 | /// This is useful to hide widgets from accessibility tools that are painted |
7423 | /// behind a certain widget, e.g. an alert should usually disallow interaction |
7424 | /// with any widget located "behind" the alert (even when they are still |
7425 | /// partially visible). Similarly, an open [Drawer] blocks interactions with |
7426 | /// any widget outside the drawer. |
7427 | /// |
7428 | /// See also: |
7429 | /// |
7430 | /// * [ExcludeSemantics] which drops all semantics of its descendants. |
7431 | class BlockSemantics extends SingleChildRenderObjectWidget { |
7432 | /// Creates a widget that excludes the semantics of all widgets painted before |
7433 | /// it in the same semantic container. |
7434 | const BlockSemantics({ super.key, this.blocking = true, super.child }); |
7435 | |
7436 | /// Whether this widget is blocking semantics of all widget that were painted |
7437 | /// before it in the same semantic container. |
7438 | final bool blocking; |
7439 | |
7440 | @override |
7441 | RenderBlockSemantics createRenderObject(BuildContext context) => RenderBlockSemantics(blocking: blocking); |
7442 | |
7443 | @override |
7444 | void updateRenderObject(BuildContext context, RenderBlockSemantics renderObject) { |
7445 | renderObject.blocking = blocking; |
7446 | } |
7447 | |
7448 | @override |
7449 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
7450 | super.debugFillProperties(properties); |
7451 | properties.add(DiagnosticsProperty<bool>('blocking' , blocking)); |
7452 | } |
7453 | } |
7454 | |
7455 | /// A widget that drops all the semantics of its descendants. |
7456 | /// |
7457 | /// When [excluding] is true, this widget (and its subtree) is excluded from |
7458 | /// the semantics tree. |
7459 | /// |
7460 | /// This can be used to hide descendant widgets that would otherwise be |
7461 | /// reported but that would only be confusing. For example, the |
7462 | /// material library's [Chip] widget hides the avatar since it is |
7463 | /// redundant with the chip label. |
7464 | /// |
7465 | /// See also: |
7466 | /// |
7467 | /// * [BlockSemantics] which drops semantics of widgets earlier in the tree. |
7468 | class ExcludeSemantics extends SingleChildRenderObjectWidget { |
7469 | /// Creates a widget that drops all the semantics of its descendants. |
7470 | const ExcludeSemantics({ |
7471 | super.key, |
7472 | this.excluding = true, |
7473 | super.child, |
7474 | }); |
7475 | |
7476 | /// Whether this widget is excluded in the semantics tree. |
7477 | final bool excluding; |
7478 | |
7479 | @override |
7480 | RenderExcludeSemantics createRenderObject(BuildContext context) => RenderExcludeSemantics(excluding: excluding); |
7481 | |
7482 | @override |
7483 | void updateRenderObject(BuildContext context, RenderExcludeSemantics renderObject) { |
7484 | renderObject.excluding = excluding; |
7485 | } |
7486 | |
7487 | @override |
7488 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
7489 | super.debugFillProperties(properties); |
7490 | properties.add(DiagnosticsProperty<bool>('excluding' , excluding)); |
7491 | } |
7492 | } |
7493 | |
7494 | /// A widget that annotates the child semantics with an index. |
7495 | /// |
7496 | /// Semantic indexes are used by TalkBack/Voiceover to make announcements about |
7497 | /// the current scroll state. Certain widgets like the [ListView] will |
7498 | /// automatically provide a child index for building semantics. A user may wish |
7499 | /// to manually provide semantic indexes if not all child of the scrollable |
7500 | /// contribute semantics. |
7501 | /// |
7502 | /// {@tool snippet} |
7503 | /// |
7504 | /// The example below handles spacers in a scrollable that don't contribute |
7505 | /// semantics. The automatic indexes would give the spaces a semantic index, |
7506 | /// causing scroll announcements to erroneously state that there are four items |
7507 | /// visible. |
7508 | /// |
7509 | /// ```dart |
7510 | /// ListView( |
7511 | /// addSemanticIndexes: false, |
7512 | /// semanticChildCount: 2, |
7513 | /// children: const <Widget>[ |
7514 | /// IndexedSemantics(index: 0, child: Text('First')), |
7515 | /// Spacer(), |
7516 | /// IndexedSemantics(index: 1, child: Text('Second')), |
7517 | /// Spacer(), |
7518 | /// ], |
7519 | /// ) |
7520 | /// ``` |
7521 | /// {@end-tool} |
7522 | /// |
7523 | /// See also: |
7524 | /// |
7525 | /// * [CustomScrollView], for an explanation of index semantics. |
7526 | class IndexedSemantics extends SingleChildRenderObjectWidget { |
7527 | /// Creates a widget that annotated the first child semantics node with an index. |
7528 | const IndexedSemantics({ |
7529 | super.key, |
7530 | required this.index, |
7531 | super.child, |
7532 | }); |
7533 | |
7534 | /// The index used to annotate the first child semantics node. |
7535 | final int index; |
7536 | |
7537 | @override |
7538 | RenderIndexedSemantics createRenderObject(BuildContext context) => RenderIndexedSemantics(index: index); |
7539 | |
7540 | @override |
7541 | void updateRenderObject(BuildContext context, RenderIndexedSemantics renderObject) { |
7542 | renderObject.index = index; |
7543 | } |
7544 | @override |
7545 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
7546 | super.debugFillProperties(properties); |
7547 | properties.add(DiagnosticsProperty<int>('index' , index)); |
7548 | } |
7549 | } |
7550 | |
7551 | /// A widget that builds its child. |
7552 | /// |
7553 | /// Useful for attaching a key to an existing widget. |
7554 | class KeyedSubtree extends StatelessWidget { |
7555 | /// Creates a widget that builds its child. |
7556 | const KeyedSubtree({ |
7557 | super.key, |
7558 | required this.child, |
7559 | }); |
7560 | |
7561 | /// Creates a KeyedSubtree for child with a key that's based on the child's existing key or childIndex. |
7562 | factory KeyedSubtree.wrap(Widget child, int childIndex) { |
7563 | final Key key = child.key != null ? ValueKey<Key>(child.key!) : ValueKey<int>(childIndex); |
7564 | return KeyedSubtree(key: key, child: child); |
7565 | } |
7566 | |
7567 | /// The widget below this widget in the tree. |
7568 | /// |
7569 | /// {@macro flutter.widgets.ProxyWidget.child} |
7570 | final Widget child; |
7571 | |
7572 | /// Wrap each item in a KeyedSubtree whose key is based on the item's existing key or |
7573 | /// the sum of its list index and `baseIndex`. |
7574 | static List<Widget> ensureUniqueKeysForList(List<Widget> items, { int baseIndex = 0 }) { |
7575 | if (items.isEmpty) { |
7576 | return items; |
7577 | } |
7578 | |
7579 | final List<Widget> itemsWithUniqueKeys = <Widget>[]; |
7580 | int itemIndex = baseIndex; |
7581 | for (final Widget item in items) { |
7582 | itemsWithUniqueKeys.add(KeyedSubtree.wrap(item, itemIndex)); |
7583 | itemIndex += 1; |
7584 | } |
7585 | |
7586 | assert(!debugItemsHaveDuplicateKeys(itemsWithUniqueKeys)); |
7587 | return itemsWithUniqueKeys; |
7588 | } |
7589 | |
7590 | @override |
7591 | Widget build(BuildContext context) => child; |
7592 | } |
7593 | |
7594 | /// A stateless utility widget whose [build] method uses its |
7595 | /// [builder] callback to create the widget's child. |
7596 | /// |
7597 | /// {@youtube 560 315 https://www.youtube.com/watch?v=xXNOkIuSYuA} |
7598 | /// |
7599 | /// This widget is an inline alternative to defining a [StatelessWidget] |
7600 | /// subclass. For example, instead of defining a widget as follows: |
7601 | /// |
7602 | /// ```dart |
7603 | /// class Foo extends StatelessWidget { |
7604 | /// const Foo({super.key}); |
7605 | /// @override |
7606 | /// Widget build(BuildContext context) => const Text('foo'); |
7607 | /// } |
7608 | /// ``` |
7609 | /// |
7610 | /// ...and using it in the usual way: |
7611 | /// |
7612 | /// ```dart |
7613 | /// // continuing from previous example... |
7614 | /// const Center(child: Foo()) |
7615 | /// ``` |
7616 | /// |
7617 | /// ...one could instead define and use it in a single step, without |
7618 | /// defining a new widget class: |
7619 | /// |
7620 | /// ```dart |
7621 | /// Center( |
7622 | /// child: Builder( |
7623 | /// builder: (BuildContext context) => const Text('foo'), |
7624 | /// ), |
7625 | /// ) |
7626 | /// ``` |
7627 | /// |
7628 | /// The difference between either of the previous examples and |
7629 | /// creating a child directly without an intervening widget, is the |
7630 | /// extra [BuildContext] element that the additional widget adds. This |
7631 | /// is particularly noticeable when the tree contains an inherited |
7632 | /// widget that is referred to by a method like [Scaffold.of], |
7633 | /// which visits the child widget's BuildContext ancestors. |
7634 | /// |
7635 | /// In the following example the button's `onPressed` callback is unable |
7636 | /// to find the enclosing [ScaffoldState] with [Scaffold.of]: |
7637 | /// |
7638 | /// ```dart |
7639 | /// Widget build(BuildContext context) { |
7640 | /// return Scaffold( |
7641 | /// body: Center( |
7642 | /// child: TextButton( |
7643 | /// onPressed: () { |
7644 | /// // Fails because Scaffold.of() doesn't find anything |
7645 | /// // above this widget's context. |
7646 | /// print(Scaffold.of(context).hasAppBar); |
7647 | /// }, |
7648 | /// child: const Text('hasAppBar'), |
7649 | /// ) |
7650 | /// ), |
7651 | /// ); |
7652 | /// } |
7653 | /// ``` |
7654 | /// |
7655 | /// A [Builder] widget introduces an additional [BuildContext] element |
7656 | /// and so the [Scaffold.of] method succeeds. |
7657 | /// |
7658 | /// ```dart |
7659 | /// Widget build(BuildContext context) { |
7660 | /// return Scaffold( |
7661 | /// body: Builder( |
7662 | /// builder: (BuildContext context) { |
7663 | /// return Center( |
7664 | /// child: TextButton( |
7665 | /// onPressed: () { |
7666 | /// print(Scaffold.of(context).hasAppBar); |
7667 | /// }, |
7668 | /// child: const Text('hasAppBar'), |
7669 | /// ), |
7670 | /// ); |
7671 | /// }, |
7672 | /// ), |
7673 | /// ); |
7674 | /// } |
7675 | /// ``` |
7676 | /// |
7677 | /// See also: |
7678 | /// |
7679 | /// * [StatefulBuilder], A stateful utility widget whose [build] method uses its |
7680 | /// [builder] callback to create the widget's child. |
7681 | class Builder extends StatelessWidget { |
7682 | /// Creates a widget that delegates its build to a callback. |
7683 | const Builder({ |
7684 | super.key, |
7685 | required this.builder, |
7686 | }); |
7687 | |
7688 | /// Called to obtain the child widget. |
7689 | /// |
7690 | /// This function is called whenever this widget is included in its parent's |
7691 | /// build and the old widget (if any) that it synchronizes with has a distinct |
7692 | /// object identity. Typically the parent's build method will construct |
7693 | /// a new tree of widgets and so a new Builder child will not be [identical] |
7694 | /// to the corresponding old one. |
7695 | final WidgetBuilder builder; |
7696 | |
7697 | @override |
7698 | Widget build(BuildContext context) => builder(context); |
7699 | } |
7700 | |
7701 | /// Signature for the builder callback used by [StatefulBuilder]. |
7702 | /// |
7703 | /// Call `setState` to schedule the [StatefulBuilder] to rebuild. |
7704 | typedef StatefulWidgetBuilder = Widget Function(BuildContext context, StateSetter setState); |
7705 | |
7706 | /// A platonic widget that both has state and calls a closure to obtain its child widget. |
7707 | /// |
7708 | /// {@youtube 560 315 https://www.youtube.com/watch?v=syvT63CosNE} |
7709 | /// |
7710 | /// The [StateSetter] function passed to the [builder] is used to invoke a |
7711 | /// rebuild instead of a typical [State]'s [State.setState]. |
7712 | /// |
7713 | /// Since the [builder] is re-invoked when the [StateSetter] is called, any |
7714 | /// variables that represents state should be kept outside the [builder] function. |
7715 | /// |
7716 | /// {@tool snippet} |
7717 | /// |
7718 | /// This example shows using an inline StatefulBuilder that rebuilds and that |
7719 | /// also has state. |
7720 | /// |
7721 | /// ```dart |
7722 | /// await showDialog<void>( |
7723 | /// context: context, |
7724 | /// builder: (BuildContext context) { |
7725 | /// int? selectedRadio = 0; |
7726 | /// return AlertDialog( |
7727 | /// content: StatefulBuilder( |
7728 | /// builder: (BuildContext context, StateSetter setState) { |
7729 | /// return Column( |
7730 | /// mainAxisSize: MainAxisSize.min, |
7731 | /// children: List<Widget>.generate(4, (int index) { |
7732 | /// return Radio<int>( |
7733 | /// value: index, |
7734 | /// groupValue: selectedRadio, |
7735 | /// onChanged: (int? value) { |
7736 | /// setState(() => selectedRadio = value); |
7737 | /// }, |
7738 | /// ); |
7739 | /// }), |
7740 | /// ); |
7741 | /// }, |
7742 | /// ), |
7743 | /// ); |
7744 | /// }, |
7745 | /// ); |
7746 | /// ``` |
7747 | /// {@end-tool} |
7748 | /// |
7749 | /// See also: |
7750 | /// |
7751 | /// * [Builder], the platonic stateless widget. |
7752 | class StatefulBuilder extends StatefulWidget { |
7753 | /// Creates a widget that both has state and delegates its build to a callback. |
7754 | const StatefulBuilder({ |
7755 | super.key, |
7756 | required this.builder, |
7757 | }); |
7758 | |
7759 | /// Called to obtain the child widget. |
7760 | /// |
7761 | /// This function is called whenever this widget is included in its parent's |
7762 | /// build and the old widget (if any) that it synchronizes with has a distinct |
7763 | /// object identity. Typically the parent's build method will construct |
7764 | /// a new tree of widgets and so a new Builder child will not be [identical] |
7765 | /// to the corresponding old one. |
7766 | final StatefulWidgetBuilder builder; |
7767 | |
7768 | @override |
7769 | State<StatefulBuilder> createState() => _StatefulBuilderState(); |
7770 | } |
7771 | |
7772 | class _StatefulBuilderState extends State<StatefulBuilder> { |
7773 | @override |
7774 | Widget build(BuildContext context) => widget.builder(context, setState); |
7775 | } |
7776 | |
7777 | /// A widget that paints its area with a specified [Color] and then draws its |
7778 | /// child on top of that color. |
7779 | class ColoredBox extends SingleChildRenderObjectWidget { |
7780 | /// Creates a widget that paints its area with the specified [Color]. |
7781 | const ColoredBox({ required this.color, super.child, super.key }); |
7782 | |
7783 | /// The color to paint the background area with. |
7784 | final Color color; |
7785 | |
7786 | @override |
7787 | RenderObject createRenderObject(BuildContext context) { |
7788 | return _RenderColoredBox(color: color); |
7789 | } |
7790 | |
7791 | @override |
7792 | void updateRenderObject(BuildContext context, RenderObject renderObject) { |
7793 | (renderObject as _RenderColoredBox).color = color; |
7794 | } |
7795 | |
7796 | @override |
7797 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
7798 | super.debugFillProperties(properties); |
7799 | properties.add(DiagnosticsProperty<Color>('color' , color)); |
7800 | } |
7801 | } |
7802 | |
7803 | class _RenderColoredBox extends RenderProxyBoxWithHitTestBehavior { |
7804 | _RenderColoredBox({ required Color color }) |
7805 | : _color = color, |
7806 | super(behavior: HitTestBehavior.opaque); |
7807 | |
7808 | /// The fill color for this render object. |
7809 | Color get color => _color; |
7810 | Color _color; |
7811 | set color(Color value) { |
7812 | if (value == _color) { |
7813 | return; |
7814 | } |
7815 | _color = value; |
7816 | markNeedsPaint(); |
7817 | } |
7818 | |
7819 | @override |
7820 | void paint(PaintingContext context, Offset offset) { |
7821 | // It's tempting to want to optimize out this `drawRect()` call if the |
7822 | // color is transparent (alpha==0), but doing so would be incorrect. See |
7823 | // https://github.com/flutter/flutter/pull/72526#issuecomment-749185938 for |
7824 | // a good description of why. |
7825 | if (size > Size.zero) { |
7826 | context.canvas.drawRect(offset & size, Paint()..color = color); |
7827 | } |
7828 | if (child != null) { |
7829 | context.paintChild(child!, offset); |
7830 | } |
7831 | } |
7832 | } |
7833 | |