Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add a gallery of the images selected and uploaded for a product (…
…#2801) * feat: image list gallery * feat: display every image in gallery view * feat: shimmering effect in `smooth_list_view.dart` * docs: add documentation to `product_image_gallery_view.dart` * refactor: extract SmoothBackButton Co-authored-by: monsieurtanuki <fabrice_fontaine@hotmail.com>
- Loading branch information
1 parent
d1b9020
commit c706839
Showing
25 changed files
with
818 additions
and
485 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import 'package:flutter/widgets.dart'; | ||
|
||
/// A [SliverChildBuilderDelegate] that can show progress by displaying | ||
/// [loadingWidget]s. | ||
/// | ||
/// When [loading] is `true`, [loadingCount] of [loadingWidget] will be | ||
/// displayed. | ||
class LoadingSliverChildBuilderDelegate extends SliverChildBuilderDelegate { | ||
LoadingSliverChildBuilderDelegate({ | ||
required IndexedWidgetBuilder childBuilder, | ||
required int childCount, | ||
Widget? loadingWidget, | ||
int loadingCount = 4, | ||
bool loading = false, | ||
}) : assert(loading == false || loadingWidget != null), | ||
super( | ||
(BuildContext context, int index) => | ||
loading ? loadingWidget : childBuilder(context, index), | ||
childCount: loading ? loadingCount : childCount, | ||
); | ||
} |
65 changes: 65 additions & 0 deletions
65
packages/smooth_app/lib/generic_lib/widgets/images/smooth_image.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:smooth_app/generic_lib/design_constants.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/picture_not_found.dart'; | ||
|
||
/// Container to display a product image on a product card. | ||
/// | ||
/// If [imageProvider] is null, [PictureNotFound] is displayed. | ||
class SmoothImage extends StatelessWidget { | ||
const SmoothImage({ | ||
this.imageProvider, | ||
this.height, | ||
this.width, | ||
this.color, | ||
this.decoration, | ||
this.fit, | ||
}); | ||
|
||
final ImageProvider? imageProvider; | ||
final double? height; | ||
final double? width; | ||
final Color? color; | ||
final Decoration? decoration; | ||
final BoxFit? fit; | ||
|
||
@override | ||
Widget build(BuildContext context) => ClipRRect( | ||
borderRadius: ROUNDED_BORDER_RADIUS, | ||
child: Container( | ||
decoration: decoration, | ||
width: width, | ||
height: height, | ||
color: color, | ||
child: imageProvider == null | ||
? const PictureNotFound() | ||
: Image( | ||
image: imageProvider!, | ||
fit: fit ?? BoxFit.cover, | ||
loadingBuilder: _loadingBuilder, | ||
), | ||
), | ||
); | ||
|
||
Widget _loadingBuilder( | ||
BuildContext _, | ||
Widget child, | ||
ImageChunkEvent? progress, | ||
) { | ||
if (progress == null) { | ||
return child; | ||
} | ||
|
||
final double progressValue = | ||
progress.cumulativeBytesLoaded / progress.expectedTotalBytes!; | ||
|
||
return Center( | ||
child: CircularProgressIndicator( | ||
strokeWidth: 2.5, | ||
valueColor: const AlwaysStoppedAnimation<Color>( | ||
Colors.white, | ||
), | ||
value: progressValue, | ||
), | ||
); | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
packages/smooth_app/lib/generic_lib/widgets/images/smooth_images_sliver_grid.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:shimmer/shimmer.dart'; | ||
import 'package:smooth_app/data_models/product_image_data.dart'; | ||
import 'package:smooth_app/generic_lib/design_constants.dart'; | ||
import 'package:smooth_app/generic_lib/loading_sliver.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/images/smooth_images_view.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/picture_not_found.dart'; | ||
|
||
/// Displays a [SliverGrid] with tiles showing the images passed | ||
/// via [imagesData] | ||
class SmoothImagesSliverGrid extends SmoothImagesView { | ||
const SmoothImagesSliverGrid({ | ||
required super.imagesData, | ||
super.onTap, | ||
super.loading = false, | ||
this.loadingCount = 6, | ||
this.maxTileWidth = VERY_LARGE_SPACE * 7, | ||
this.childAspectRatio = 1.5, | ||
}); | ||
|
||
/// The number of shimmering tiles to display while [loading] is true | ||
final int loadingCount; | ||
|
||
/// The maximum width of a tile | ||
final double maxTileWidth; | ||
|
||
final double childAspectRatio; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final List<MapEntry<ProductImageData, ImageProvider?>> imageList = | ||
imagesData.entries.toList(); | ||
|
||
return SliverPadding( | ||
padding: const EdgeInsets.all(MEDIUM_SPACE), | ||
sliver: SliverGrid( | ||
delegate: LoadingSliverChildBuilderDelegate( | ||
loading: loading, | ||
childCount: imageList.length, | ||
loadingWidget: _buildShimmer(), | ||
loadingCount: loadingCount, | ||
childBuilder: (BuildContext context, int index) { | ||
final MapEntry<ProductImageData, ImageProvider<Object>?> entry = | ||
imageList[index]; | ||
final ImageProvider? imageProvider = entry.value; | ||
|
||
return imageProvider == null | ||
? const PictureNotFound() | ||
: Hero( | ||
tag: entry.key.imageUrl!, | ||
child: _ImageTile( | ||
image: imageProvider, | ||
onTap: onTap == null | ||
? null | ||
: () => onTap!(entry.key, entry.value), | ||
), | ||
); | ||
}), | ||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( | ||
maxCrossAxisExtent: maxTileWidth, | ||
childAspectRatio: childAspectRatio, | ||
mainAxisSpacing: MEDIUM_SPACE, | ||
crossAxisSpacing: MEDIUM_SPACE, | ||
), | ||
), | ||
); | ||
} | ||
|
||
Widget _buildShimmer() => Shimmer.fromColors( | ||
baseColor: WHITE_COLOR, | ||
highlightColor: GREY_COLOR, | ||
child: const SmoothImage( | ||
width: VERY_LARGE_SPACE * 5, | ||
height: MEDIUM_SPACE * 5, | ||
color: WHITE_COLOR, | ||
), | ||
); | ||
} | ||
|
||
class _ImageTile extends StatelessWidget { | ||
const _ImageTile({ | ||
required this.image, | ||
this.onTap, | ||
}); | ||
|
||
final ImageProvider image; | ||
final void Function()? onTap; | ||
|
||
@override | ||
Widget build(BuildContext context) => Ink( | ||
decoration: BoxDecoration( | ||
borderRadius: ROUNDED_BORDER_RADIUS, | ||
image: DecorationImage( | ||
image: image, | ||
fit: BoxFit.cover, | ||
)), | ||
child: InkWell( | ||
borderRadius: ROUNDED_BORDER_RADIUS, | ||
onTap: onTap, | ||
), | ||
); | ||
} |
44 changes: 44 additions & 0 deletions
44
packages/smooth_app/lib/generic_lib/widgets/images/smooth_images_sliver_list.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:smooth_app/data_models/product_image_data.dart'; | ||
import 'package:smooth_app/generic_lib/loading_sliver.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/images/smooth_images_view.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/smooth_list_tile_card.dart'; | ||
|
||
/// Displays a [SliverList] by using [SmoothListTileCard] for showing images | ||
/// passed via [imagesData]. | ||
/// | ||
/// If [loading] is set to `true`, the list shows instead | ||
/// loading [SmoothListTileCard]s. | ||
class SmoothImagesSliverList extends SmoothImagesView { | ||
const SmoothImagesSliverList({ | ||
required super.imagesData, | ||
super.onTap, | ||
super.loading = false, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final ThemeData themeData = Theme.of(context); | ||
final List<MapEntry<ProductImageData, ImageProvider?>> imageList = | ||
imagesData.entries.toList(); | ||
final int count = imageList.length; | ||
|
||
return SliverList( | ||
delegate: LoadingSliverChildBuilderDelegate( | ||
loading: loading, | ||
childCount: count, | ||
loadingWidget: SmoothListTileCard.loading(), | ||
childBuilder: (_, int index) => SmoothListTileCard.image( | ||
imageProvider: imageList[index].value, | ||
title: Text( | ||
imageList[index].key.title, | ||
style: themeData.textTheme.headline4, | ||
), | ||
onTap: onTap == null | ||
? null | ||
: () => onTap!(imageList[index].key, imageList[index].value), | ||
), | ||
), | ||
); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
packages/smooth_app/lib/generic_lib/widgets/images/smooth_images_view.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import 'package:flutter/widgets.dart'; | ||
import 'package:smooth_app/data_models/product_image_data.dart'; | ||
|
||
/// Base class for classes that display a collection of images. | ||
abstract class SmoothImagesView extends StatelessWidget { | ||
const SmoothImagesView({ | ||
required this.imagesData, | ||
this.onTap, | ||
this.loading = false, | ||
}); | ||
|
||
final Map<ProductImageData, ImageProvider?> imagesData; | ||
final void Function(ProductImageData, ImageProvider?)? onTap; | ||
final bool loading; | ||
} |
15 changes: 15 additions & 0 deletions
15
packages/smooth_app/lib/generic_lib/widgets/picture_not_found.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import 'package:flutter/widgets.dart'; | ||
import 'package:flutter_svg/svg.dart'; | ||
|
||
/// Displays a default asset as a _picture not found_ image. | ||
class PictureNotFound extends StatelessWidget { | ||
const PictureNotFound(); | ||
|
||
static const String NOT_FOUND_ASSET = 'assets/product/product_not_found.svg'; | ||
|
||
@override | ||
Widget build(BuildContext context) => SvgPicture.asset( | ||
NOT_FOUND_ASSET, | ||
fit: BoxFit.cover, | ||
); | ||
} |
19 changes: 19 additions & 0 deletions
19
packages/smooth_app/lib/generic_lib/widgets/smooth_back_button.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:smooth_app/themes/constant_icons.dart'; | ||
|
||
/// Displays an [IconButton] containing the platform-specific default | ||
/// back button icon. | ||
class SmoothBackButton extends StatelessWidget { | ||
const SmoothBackButton({ | ||
this.onPressed, | ||
}); | ||
|
||
final void Function()? onPressed; | ||
|
||
@override | ||
Widget build(BuildContext context) => IconButton( | ||
icon: Icon(ConstantIcons.instance.getBackIcon()), | ||
tooltip: MaterialLocalizations.of(context).backButtonTooltip, | ||
onPressed: onPressed ?? () => Navigator.maybePop(context), | ||
); | ||
} |
Oops, something went wrong.