@@ -48,13 +48,19 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
48
48
with TickerProviderStateMixin {
49
49
static final _planeTween = CurveTween (curve: Curves .easeInOut);
50
50
late AnimationController _planeController;
51
+ late AnimationController _planeOutInController;
51
52
52
53
@override
53
54
void initState () {
54
55
_planeController = AnimationController (
55
56
vsync: this ,
56
57
duration: const Duration (milliseconds: 500 ),
57
58
);
59
+ _planeOutInController = AnimationController (
60
+ vsync: this ,
61
+ duration: _completeDuration,
62
+ value: 0.0 ,
63
+ );
58
64
59
65
_setupCloudsAnimationControllers ();
60
66
WidgetsBinding .instance.addPostFrameCallback ((_) => _precacheImages ());
@@ -71,15 +77,15 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
71
77
_Cloud (
72
78
color: _Cloud ._dark,
73
79
initialValue: 0.6 ,
74
- dy: 10 .0 ,
80
+ dy: 5 .0 ,
75
81
image: AssetImage (_Cloud ._assets[1 ]),
76
82
width: 100 ,
77
83
duration: const Duration (milliseconds: 1600 ),
78
84
),
79
85
_Cloud (
80
86
color: _Cloud ._light,
81
87
initialValue: 0.15 ,
82
- dy: 25 .0 ,
88
+ dy: 18 .0 ,
83
89
image: AssetImage (_Cloud ._assets[3 ]),
84
90
width: 40 ,
85
91
duration: const Duration (milliseconds: 1600 ),
@@ -151,10 +157,12 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
151
157
@override
152
158
void dispose () {
153
159
_planeController.dispose ();
160
+ _planeOutInController.dispose ();
154
161
_disposeCloudsControllers ();
155
162
super .dispose ();
156
163
}
157
164
165
+ static const _completeDuration = Duration (milliseconds: 300 );
158
166
static const _offsetToArmed = 150.0 ;
159
167
160
168
@override
@@ -178,63 +186,90 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
178
186
);
179
187
},
180
188
);
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 (
219
237
animation: _clouds.first.controller! ,
220
238
builder: (BuildContext context, Widget ? child) {
221
239
return Stack (
222
240
clipBehavior: Clip .hardEdge,
241
+ alignment: Alignment .topLeft,
223
242
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
+
224
260
for (final cloud in _clouds)
225
261
Transform .translate (
226
262
offset: Offset (
227
263
((screenWidth + cloud.width) *
228
264
cloud.controller! .value) -
229
265
cloud.width,
230
- cloud.dy * controller.value,
266
+ - cloud.width +
267
+ ((cloud.width + cloud.dy) *
268
+ controller.value),
231
269
),
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,
238
273
child: Image (
239
274
color: cloud.color,
240
275
image: cloud.image,
@@ -244,31 +279,39 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
244
279
),
245
280
246
281
/// 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
+ ),
255
302
),
256
303
),
257
304
],
258
305
);
259
306
},
260
307
),
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
+ ),
272
315
);
273
316
},
274
317
);
0 commit comments