4
4
5
5
import 'dart:async' ;
6
6
import 'dart:io' ;
7
+ import 'dart:math' as math;
7
8
8
9
import 'package:flutter/material.dart' ;
9
10
import 'package:flutter/semantics.dart' ;
@@ -127,7 +128,9 @@ class CameraPickerViewerState extends State<CameraPickerViewer> {
127
128
if (isSavingEntity) {
128
129
return ;
129
130
}
130
- isSavingEntity = true ;
131
+ setState (() {
132
+ isSavingEntity = true ;
133
+ });
131
134
final CameraPickerViewType viewType = widget.viewType;
132
135
if (widget.pickerConfig.onEntitySaving != null ) {
133
136
try {
@@ -138,8 +141,10 @@ class CameraPickerViewerState extends State<CameraPickerViewer> {
138
141
);
139
142
} catch (e, s) {
140
143
handleErrorWithHandler (e, widget.pickerConfig.onError, s: s);
141
- } finally {
142
- isSavingEntity = false ;
144
+ }
145
+ isSavingEntity = false ;
146
+ if (mounted) {
147
+ setState (() {});
143
148
}
144
149
return ;
145
150
}
@@ -194,6 +199,9 @@ class CameraPickerViewerState extends State<CameraPickerViewer> {
194
199
padding: const EdgeInsets .all (10 ),
195
200
child: IconButton (
196
201
onPressed: () {
202
+ if (isSavingEntity) {
203
+ return ;
204
+ }
197
205
if (previewFile.existsSync ()) {
198
206
previewFile.delete ();
199
207
}
@@ -209,7 +217,10 @@ class CameraPickerViewerState extends State<CameraPickerViewer> {
209
217
color: Colors .white,
210
218
shape: BoxShape .circle,
211
219
),
212
- child: const Icon (Icons .keyboard_return_rounded, color: Colors .black,),
220
+ child: const Icon (
221
+ Icons .keyboard_return_rounded,
222
+ color: Colors .black,
223
+ ),
213
224
),
214
225
),
215
226
),
@@ -329,6 +340,16 @@ class CameraPickerViewerState extends State<CameraPickerViewer> {
329
340
);
330
341
}
331
342
343
+ Widget buildLoading (BuildContext context) {
344
+ return IgnorePointer (
345
+ child: AnimatedOpacity (
346
+ duration: kThemeAnimationDuration,
347
+ opacity: isSavingEntity ? 1 : 0 ,
348
+ child: _WechatLoading (tip: Constants .textDelegate.saving),
349
+ ),
350
+ );
351
+ }
352
+
332
353
@override
333
354
Widget build (BuildContext context) {
334
355
if (hasErrorWhenInitializing) {
@@ -349,8 +370,115 @@ class CameraPickerViewerState extends State<CameraPickerViewer> {
349
370
children: < Widget > [
350
371
buildPreview (context),
351
372
buildForeground (context),
373
+ buildLoading (context),
352
374
],
353
375
),
354
376
);
355
377
}
356
378
}
379
+
380
+ class _WechatLoading extends StatefulWidget {
381
+ const _WechatLoading ({Key ? key, required this .tip}) : super (key: key);
382
+
383
+ final String tip;
384
+
385
+ @override
386
+ State <_WechatLoading > createState () => _WechatLoadingState ();
387
+ }
388
+
389
+ class _WechatLoadingState extends State <_WechatLoading >
390
+ with SingleTickerProviderStateMixin {
391
+ late final AnimationController _controller = AnimationController (
392
+ duration: const Duration (seconds: 2 ),
393
+ vsync: this ,
394
+ );
395
+
396
+ @override
397
+ void initState () {
398
+ super .initState ();
399
+ _controller.repeat ();
400
+ }
401
+
402
+ @override
403
+ void dispose () {
404
+ _controller.dispose ();
405
+ super .dispose ();
406
+ }
407
+
408
+ Widget _buildContent (BuildContext context, double minWidth) {
409
+ return Column (
410
+ mainAxisSize: MainAxisSize .min,
411
+ children: < Widget > [
412
+ SizedBox .fromSize (
413
+ size: Size .square (minWidth / 3 ),
414
+ child: AnimatedBuilder (
415
+ animation: _controller,
416
+ builder: (_, Widget ? child) => Transform .rotate (
417
+ angle: math.pi * 2 * _controller.value,
418
+ child: child,
419
+ ),
420
+ child: CustomPaint (
421
+ painter: _LoadingPainter (
422
+ Theme .of (context).textTheme.bodyMedium? .color,
423
+ ),
424
+ ),
425
+ ),
426
+ ),
427
+ SizedBox (height: minWidth / 10 ),
428
+ Text (
429
+ widget.tip,
430
+ style: const TextStyle (fontSize: 14 ),
431
+ textScaleFactor: 1 ,
432
+ ),
433
+ ],
434
+ );
435
+ }
436
+
437
+ @override
438
+ Widget build (BuildContext context) {
439
+ final double minWidth = MediaQuery .of (context).size.shortestSide / 3 ;
440
+ return Container (
441
+ color: Colors .black38,
442
+ alignment: Alignment .center,
443
+ child: RepaintBoundary (
444
+ child: Container (
445
+ constraints: BoxConstraints (minWidth: minWidth),
446
+ padding: EdgeInsets .all (minWidth / 5 ),
447
+ decoration: BoxDecoration (
448
+ borderRadius: BorderRadius .circular (10 ),
449
+ color: Theme .of (context).canvasColor,
450
+ ),
451
+ child: _buildContent (context, minWidth),
452
+ ),
453
+ ),
454
+ );
455
+ }
456
+ }
457
+
458
+ class _LoadingPainter extends CustomPainter {
459
+ const _LoadingPainter (this .activeColor);
460
+
461
+ final Color ? activeColor;
462
+
463
+ @override
464
+ void paint (Canvas canvas, Size size) {
465
+ final Color color = activeColor ?? Colors .white;
466
+ final Offset center = Offset (size.width / 2 , size.height / 2 );
467
+ final Rect rect = Rect .fromCenter (
468
+ center: center,
469
+ width: size.width,
470
+ height: size.height,
471
+ );
472
+ final Paint paint = Paint ()
473
+ ..style = PaintingStyle .stroke
474
+ ..strokeCap = StrokeCap .round
475
+ ..strokeWidth = 4
476
+ ..shader = SweepGradient (
477
+ colors: < Color > [color.withOpacity (0 ), color],
478
+ ).createShader (rect);
479
+ canvas.drawArc (rect, 0.1 , math.pi * 2 * 0.9 , false , paint);
480
+ }
481
+
482
+ @override
483
+ bool shouldRepaint (_LoadingPainter oldDelegate) => false ;
484
+ }
0 commit comments