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 'package:flutter/rendering.dart'; |
6 | |
7 | import 'basic.dart'; |
8 | import 'framework.dart'; |
9 | import 'ticker_provider.dart'; |
10 | |
11 | /// Animated widget that automatically transitions its size over a given |
12 | /// duration whenever the given child's size changes. |
13 | /// |
14 | /// {@tool dartpad} |
15 | /// This example makes a [Container] react to being touched, causing the child |
16 | /// of the [AnimatedSize] widget, here a [FlutterLogo], to animate. |
17 | /// |
18 | /// ** See code in examples/api/lib/widgets/animated_size/animated_size.0.dart ** |
19 | /// {@end-tool} |
20 | /// |
21 | /// See also: |
22 | /// |
23 | /// * [SizeTransition], which changes its size based on an [Animation]. |
24 | class AnimatedSize extends StatefulWidget { |
25 | /// Creates a widget that animates its size to match that of its child. |
26 | const AnimatedSize({ |
27 | super.key, |
28 | this.child, |
29 | this.alignment = Alignment.center, |
30 | this.curve = Curves.linear, |
31 | required this.duration, |
32 | this.reverseDuration, |
33 | this.clipBehavior = Clip.hardEdge, |
34 | this.onEnd, |
35 | }); |
36 | |
37 | /// The widget below this widget in the tree. |
38 | /// |
39 | /// {@macro flutter.widgets.ProxyWidget.child} |
40 | final Widget? child; |
41 | |
42 | /// The alignment of the child within the parent when the parent is not yet |
43 | /// the same size as the child. |
44 | /// |
45 | /// The x and y values of the alignment control the horizontal and vertical |
46 | /// alignment, respectively. An x value of -1.0 means that the left edge of |
47 | /// the child is aligned with the left edge of the parent whereas an x value |
48 | /// of 1.0 means that the right edge of the child is aligned with the right |
49 | /// edge of the parent. Other values interpolate (and extrapolate) linearly. |
50 | /// For example, a value of 0.0 means that the center of the child is aligned |
51 | /// with the center of the parent. |
52 | /// |
53 | /// Defaults to [Alignment.center]. |
54 | /// |
55 | /// See also: |
56 | /// |
57 | /// * [Alignment], a class with convenient constants typically used to |
58 | /// specify an [AlignmentGeometry]. |
59 | /// * [AlignmentDirectional], like [Alignment] for specifying alignments |
60 | /// relative to text direction. |
61 | final AlignmentGeometry alignment; |
62 | |
63 | /// The animation curve when transitioning this widget's size to match the |
64 | /// child's size. |
65 | final Curve curve; |
66 | |
67 | /// The duration when transitioning this widget's size to match the child's |
68 | /// size. |
69 | final Duration duration; |
70 | |
71 | /// The duration when transitioning this widget's size to match the child's |
72 | /// size when going in reverse. |
73 | /// |
74 | /// If not specified, defaults to [duration]. |
75 | final Duration? reverseDuration; |
76 | |
77 | /// {@macro flutter.material.Material.clipBehavior} |
78 | /// |
79 | /// Defaults to [Clip.hardEdge]. |
80 | final Clip clipBehavior; |
81 | |
82 | /// Called every time an animation completes. |
83 | /// |
84 | /// This can be useful to trigger additional actions (e.g. another animation) |
85 | /// at the end of the current animation. |
86 | final VoidCallback? onEnd; |
87 | |
88 | @override |
89 | State<AnimatedSize> createState() => _AnimatedSizeState(); |
90 | } |
91 | |
92 | class _AnimatedSizeState |
93 | extends State<AnimatedSize> with SingleTickerProviderStateMixin { |
94 | @override |
95 | Widget build(BuildContext context) { |
96 | return _AnimatedSize( |
97 | alignment: widget.alignment, |
98 | curve: widget.curve, |
99 | duration: widget.duration, |
100 | reverseDuration: widget.reverseDuration, |
101 | vsync: this, |
102 | clipBehavior: widget.clipBehavior, |
103 | onEnd: widget.onEnd, |
104 | child: widget.child, |
105 | ); |
106 | } |
107 | } |
108 | |
109 | class _AnimatedSize extends SingleChildRenderObjectWidget { |
110 | const _AnimatedSize({ |
111 | super.child, |
112 | this.alignment = Alignment.center, |
113 | this.curve = Curves.linear, |
114 | required this.duration, |
115 | this.reverseDuration, |
116 | required this.vsync, |
117 | this.clipBehavior = Clip.hardEdge, |
118 | this.onEnd, |
119 | }); |
120 | |
121 | final AlignmentGeometry alignment; |
122 | final Curve curve; |
123 | final Duration duration; |
124 | final Duration? reverseDuration; |
125 | |
126 | /// The [TickerProvider] for this widget. |
127 | final TickerProvider vsync; |
128 | |
129 | final Clip clipBehavior; |
130 | |
131 | final VoidCallback? onEnd; |
132 | |
133 | @override |
134 | RenderAnimatedSize createRenderObject(BuildContext context) { |
135 | return RenderAnimatedSize( |
136 | alignment: alignment, |
137 | duration: duration, |
138 | reverseDuration: reverseDuration, |
139 | curve: curve, |
140 | vsync: vsync, |
141 | textDirection: Directionality.maybeOf(context), |
142 | clipBehavior: clipBehavior, |
143 | onEnd: onEnd, |
144 | ); |
145 | } |
146 | |
147 | @override |
148 | void updateRenderObject(BuildContext context, RenderAnimatedSize renderObject) { |
149 | renderObject |
150 | ..alignment = alignment |
151 | ..duration = duration |
152 | ..reverseDuration = reverseDuration |
153 | ..curve = curve |
154 | ..vsync = vsync |
155 | ..textDirection = Directionality.maybeOf(context) |
156 | ..clipBehavior = clipBehavior |
157 | ..onEnd = onEnd; |
158 | } |
159 | |
160 | @override |
161 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
162 | super.debugFillProperties(properties); |
163 | properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment' , alignment, defaultValue: Alignment.topCenter)); |
164 | properties.add(IntProperty('duration' , duration.inMilliseconds, unit: 'ms' )); |
165 | properties.add(IntProperty('reverseDuration' , reverseDuration?.inMilliseconds, unit: 'ms' , defaultValue: null)); |
166 | } |
167 | } |
168 | |