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