diff --git a/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart b/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart index 4c4a217c088..26197d46234 100644 --- a/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart +++ b/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart @@ -219,23 +219,7 @@ class _PhotoRow extends StatelessWidget { textDirection: Directionality.of(context), bottom: VERY_SMALL_SPACE, end: VERY_SMALL_SPACE, - width: 30.0, - height: 30.0, - child: DecoratedBox( - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.5), - shape: BoxShape.circle, - ), - child: const Padding( - padding: EdgeInsetsDirectional.only( - start: SMALL_SPACE, - end: SMALL_SPACE, - top: SMALL_SPACE, - bottom: SMALL_SPACE - 1.0, - ), - child: CloudUploadAnimation(), - ), - ), + child: const CloudUploadAnimation.circle(size: 30.0), ), ], ), diff --git a/packages/smooth_app/lib/pages/product/product_image_viewer.dart b/packages/smooth_app/lib/pages/product/product_image_viewer.dart index 1f43d5cfd76..f92e8ece55c 100644 --- a/packages/smooth_app/lib/pages/product/product_image_viewer.dart +++ b/packages/smooth_app/lib/pages/product/product_image_viewer.dart @@ -8,11 +8,13 @@ import 'package:smooth_app/data_models/up_to_date_mixin.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/transient_file.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/duration_constants.dart'; import 'package:smooth_app/generic_lib/widgets/language_selector.dart'; import 'package:smooth_app/generic_lib/widgets/picture_not_found.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/product/product_image_button.dart'; +import 'package:smooth_app/resources/app_animations.dart'; /// Displays a full-screen image with an "edit" floating button. class ProductImageViewer extends StatefulWidget { @@ -69,8 +71,11 @@ class _ProductImageViewerState extends State widget.imageField, widget.language, ); - final ImageProvider? imageProvider = _getTransientFile().getImageProvider(); + final TransientFile transientFile = _getTransientFile(); + final ImageProvider? imageProvider = transientFile.getImageProvider(); final bool imageExists = imageProvider != null; + final bool isLoading = + transientFile.isImageAvailable() && !transientFile.isServerImage(); final Iterable selectedLanguages = getProductImageLanguages( upToDateProduct, @@ -124,40 +129,59 @@ class _ProductImageViewerState extends State ), ], ) - : PhotoView( - minScale: 0.2, - imageProvider: imageProvider, - heroAttributes: PhotoViewHeroAttributes( - tag: 'photo_${widget.imageField.offTag}', - flightShuttleBuilder: ( - _, - Animation animation, - HeroFlightDirection flightDirection, - BuildContext fromHeroContext, - BuildContext toHeroContext, - ) { - return AnimatedBuilder( - animation: animation, - builder: (_, __) { - Widget widget; - if (flightDirection == - HeroFlightDirection.push) { - widget = fromHeroContext.widget; - } else { - widget = toHeroContext.widget; - } + : SizedBox.expand( + child: Stack( + children: [ + Positioned.fill( + child: AnimatedOpacity( + opacity: isLoading ? 0.5 : 1.0, + duration: SmoothAnimationsDuration.short, + child: PhotoView( + minScale: 0.2, + imageProvider: imageProvider, + heroAttributes: PhotoViewHeroAttributes( + tag: 'photo_${widget.imageField.offTag}', + flightShuttleBuilder: ( + _, + Animation animation, + HeroFlightDirection flightDirection, + BuildContext fromHeroContext, + BuildContext toHeroContext, + ) { + return AnimatedBuilder( + animation: animation, + builder: (_, __) { + Widget widget; + if (flightDirection == + HeroFlightDirection.push) { + widget = fromHeroContext.widget; + } else { + widget = toHeroContext.widget; + } - return ClipRRect( - borderRadius: BorderRadius.circular( - 1 - animation.value) * - ROUNDED_RADIUS.x, - child: widget, - ); - }, - ); - }), - backgroundDecoration: const BoxDecoration( - color: Colors.black, + return ClipRRect( + borderRadius: BorderRadius.circular( + 1 - animation.value) * + ROUNDED_RADIUS.x, + child: widget, + ); + }, + ); + }), + backgroundDecoration: const BoxDecoration( + color: Colors.black, + ), + ), + ), + ), + if (isLoading) + Center( + child: CloudUploadAnimation.circle( + size: MediaQuery.sizeOf(context).longestSide * + 0.2, + ), + ), + ], ), ), ), diff --git a/packages/smooth_app/lib/resources/app_animations.dart b/packages/smooth_app/lib/resources/app_animations.dart index d128b99a2e2..3086c71208b 100644 --- a/packages/smooth_app/lib/resources/app_animations.dart +++ b/packages/smooth_app/lib/resources/app_animations.dart @@ -75,15 +75,51 @@ class BarcodeAnimation extends StatelessWidget { class CloudUploadAnimation extends StatelessWidget { const CloudUploadAnimation({ + required this.size, super.key, - }); + }) : _circleColor = null; + + const CloudUploadAnimation.circle({ + required this.size, + Color? circleColor, + super.key, + }) : _circleColor = circleColor ?? Colors.black54; + + final double size; + final Color? _circleColor; @override Widget build(BuildContext context) { - return RiveAnimation.direct( - AnimationsLoader.of(context), - artboard: 'Cloud upload', - animations: const ['Animation'], + Widget widget = SizedBox.square( + dimension: size, + child: RiveAnimation.direct( + AnimationsLoader.of(context), + artboard: 'Cloud upload', + animations: const ['Animation'], + ), + ); + + if (_circleColor != null) { + widget = DecoratedBox( + decoration: BoxDecoration( + color: _circleColor, + shape: BoxShape.circle, + ), + child: Padding( + padding: EdgeInsetsDirectional.only( + top: size * 0.2, + start: size * 0.2, + end: size * 0.2, + bottom: size * 0.13, + ), + child: widget, + ), + ); + } + + return SizedBox.square( + dimension: size, + child: widget, ); } }