@@ -48,13 +48,19 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
4848 with TickerProviderStateMixin {
4949 static final _planeTween = CurveTween (curve: Curves .easeInOut);
5050 late AnimationController _planeController;
51+ late AnimationController _planeOutInController;
5152
5253 @override
5354 void initState () {
5455 _planeController = AnimationController (
5556 vsync: this ,
5657 duration: const Duration (milliseconds: 500 ),
5758 );
59+ _planeOutInController = AnimationController (
60+ vsync: this ,
61+ duration: _completeDuration,
62+ value: 0.0 ,
63+ );
5864
5965 _setupCloudsAnimationControllers ();
6066 WidgetsBinding .instance.addPostFrameCallback ((_) => _precacheImages ());
@@ -71,15 +77,15 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
7177 _Cloud (
7278 color: _Cloud ._dark,
7379 initialValue: 0.6 ,
74- dy: 10 .0 ,
80+ dy: 5 .0 ,
7581 image: AssetImage (_Cloud ._assets[1 ]),
7682 width: 100 ,
7783 duration: const Duration (milliseconds: 1600 ),
7884 ),
7985 _Cloud (
8086 color: _Cloud ._light,
8187 initialValue: 0.15 ,
82- dy: 25 .0 ,
88+ dy: 18 .0 ,
8389 image: AssetImage (_Cloud ._assets[3 ]),
8490 width: 40 ,
8591 duration: const Duration (milliseconds: 1600 ),
@@ -151,10 +157,12 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
151157 @override
152158 void dispose () {
153159 _planeController.dispose ();
160+ _planeOutInController.dispose ();
154161 _disposeCloudsControllers ();
155162 super .dispose ();
156163 }
157164
165+ static const _completeDuration = Duration (milliseconds: 300 );
158166 static const _offsetToArmed = 150.0 ;
159167
160168 @override
@@ -178,63 +186,90 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
178186 );
179187 },
180188 );
181- return CustomRefreshIndicator (
182- offsetToArmed: _offsetToArmed,
183- autoRebuild: false ,
184- onStateChanged: (change) {
185- if (change.didChange (
186- from: IndicatorState .armed,
187- to: IndicatorState .settling,
188- )) {
189- _startCloudAnimation ();
190- _startPlaneAnimation ();
191- }
192- if (change.didChange (
193- from: IndicatorState .loading,
194- )) {
195- _stopPlaneAnimation ();
196- }
197- if (change.didChange (
198- to: IndicatorState .idle,
199- )) {
200- _stopCloudAnimation ();
201- }
202- },
203- onRefresh: () => Future .delayed (const Duration (seconds: 3 )),
204- builder: (BuildContext context, Widget child,
205- IndicatorController controller) {
206- return AnimatedBuilder (
207- animation: controller,
208- child: child,
209- builder: (context, child) {
210- return Stack (
211- clipBehavior: Clip .hardEdge,
212- children: < Widget > [
213- if (! controller.side.isNone)
214- Container (
215- height: _offsetToArmed * controller.value,
216- color: const Color (0xFFFDFEFF ),
217- width: double .infinity,
218- child: AnimatedBuilder (
189+ return ClipRect (
190+ child: CustomRefreshIndicator (
191+ offsetToArmed: _offsetToArmed,
192+ autoRebuild: false ,
193+ durations: const RefreshIndicatorDurations (
194+ finalizeDuration: Duration (milliseconds: 200 ),
195+ completeDuration: _completeDuration,
196+ ),
197+ onStateChanged: (change) {
198+ if (change.didChange (
199+ from: IndicatorState .armed,
200+ to: IndicatorState .settling,
201+ )) {
202+ _startCloudAnimation ();
203+ _startPlaneAnimation ();
204+ }
205+ if (change.didChange (
206+ from: IndicatorState .loading,
207+ )) {
208+ _stopPlaneAnimation ();
209+ }
210+ if (change.didChange (
211+ to: IndicatorState .idle,
212+ )) {
213+ _stopCloudAnimation ();
214+ _planeOutInController.value = 0.0 ;
215+ }
216+ if (change.didChange (to: IndicatorState .complete)) {
217+ _planeOutInController.animateTo (1.0 ,
218+ duration:
219+ _completeDuration - const Duration (milliseconds: 100 ));
220+ }
221+ },
222+ onRefresh: () => Future .delayed (const Duration (seconds: 3 )),
223+ builder: (BuildContext context, Widget child,
224+ IndicatorController controller) {
225+ final clamped = controller.clamp (0 , 1 );
226+
227+ return AnimatedBuilder (
228+ animation: controller,
229+ child: child,
230+ builder: (context, child) {
231+ return Stack (
232+ clipBehavior: Clip .hardEdge,
233+ children: < Widget > [
234+ if (child != null ) child,
235+ if (! controller.side.isNone)
236+ AnimatedBuilder (
219237 animation: _clouds.first.controller! ,
220238 builder: (BuildContext context, Widget ? child) {
221239 return Stack (
222240 clipBehavior: Clip .hardEdge,
241+ alignment: Alignment .topLeft,
223242 children: < Widget > [
243+ Opacity (
244+ opacity: clamped.value,
245+ child: Container (
246+ height: 200 ,
247+ decoration: BoxDecoration (
248+ gradient: LinearGradient (
249+ begin: Alignment .topCenter,
250+ end: Alignment .bottomCenter,
251+ colors: [
252+ Colors .blue[100 ]! .withOpacity (.2 ),
253+ Colors .blue[100 ]! .withOpacity (0.0 )
254+ ],
255+ ),
256+ ),
257+ ),
258+ ),
259+
224260 for (final cloud in _clouds)
225261 Transform .translate (
226262 offset: Offset (
227263 ((screenWidth + cloud.width) *
228264 cloud.controller! .value) -
229265 cloud.width,
230- cloud.dy * controller.value,
266+ - cloud.width +
267+ ((cloud.width + cloud.dy) *
268+ controller.value),
231269 ),
232- child: OverflowBox (
233- minWidth: cloud.width,
234- minHeight: cloud.width,
235- maxHeight: cloud.width,
236- maxWidth: cloud.width,
237- alignment: Alignment .topLeft,
270+ child: SizedBox (
271+ width: cloud.width,
272+ height: cloud.width,
238273 child: Image (
239274 color: cloud.color,
240275 image: cloud.image,
@@ -244,31 +279,39 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
244279 ),
245280
246281 /// plane
247- Center (
248- child: OverflowBox (
249- maxWidth: 172 ,
250- minWidth: 172 ,
251- maxHeight: 50 ,
252- minHeight: 50 ,
253- alignment: Alignment .center,
254- child: plane,
282+ Transform .translate (
283+ offset: Offset (
284+ controller.state.isComplete ||
285+ controller.state.isFinalizing
286+ ? constraints.maxWidth *
287+ (0.5 -
288+ Curves .easeInCirc.transform (
289+ _planeOutInController
290+ .value))
291+ : constraints.maxWidth *
292+ (1.0 - (controller.value * 0.5 )),
293+ - 52 + (100 * controller.value),
294+ ),
295+ child: Align (
296+ alignment: Alignment .topLeft,
297+ child: SizedBox (
298+ width: 172 ,
299+ height: 50 ,
300+ child: plane,
301+ ),
255302 ),
256303 ),
257304 ],
258305 );
259306 },
260307 ),
261- ),
262- Transform .translate (
263- offset: Offset (0.0 , _offsetToArmed * controller.value),
264- child: child,
265- ),
266- ],
267- );
268- },
269- );
270- },
271- child: widget.child,
308+ ],
309+ );
310+ },
311+ );
312+ },
313+ child: widget.child,
314+ ),
272315 );
273316 },
274317 );
0 commit comments