-
-
Notifications
You must be signed in to change notification settings - Fork 259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: image list gallery #2724
feat: image list gallery #2724
Changes from all commits
d748bc6
27e780b
be68b27
50f1e1d
7b70d61
3e3968c
5d53934
8339953
86af408
ae461e4
cb34353
5a1a91c
2318810
5236428
3693d95
dfcd7b0
862241a
efa58c2
dbcd334
0b580a3
c21aafc
a57a485
7a25112
d44439e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import 'package:flutter/widgets.dart'; | ||
import 'package:flutter_svg/svg.dart'; | ||
|
||
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, | ||
); | ||
} |
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), | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:smooth_app/data_models/product_image_data.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/smooth_list_tile_card.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/smooth_list_view.dart'; | ||
|
||
/// Displays a list of [ProductImageData] | ||
class SmoothImageList extends StatelessWidget { | ||
const SmoothImageList({ | ||
required this.imagesData, | ||
this.onTap, | ||
this.loading = false, | ||
}); | ||
|
||
final Map<ProductImageData, ImageProvider?> imagesData; | ||
final void Function(ProductImageData, ImageProvider?)? onTap; | ||
final bool loading; | ||
|
||
@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 Scrollbar( | ||
child: SmoothListView.builder( | ||
loading: loading, | ||
itemCount: count, | ||
loadingWidget: (_, __) => SmoothListTileCard.loading(), | ||
itemBuilder: (_, 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), | ||
), | ||
), | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:shimmer/shimmer.dart'; | ||
import 'package:smooth_app/generic_lib/design_constants.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/picture_not_found.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/smooth_product_image_container.dart'; | ||
import 'package:smooth_app/themes/constant_icons.dart'; | ||
|
||
class SmoothListTileCard extends StatelessWidget { | ||
const SmoothListTileCard({ | ||
required final this.title, | ||
this.subtitle, | ||
this.onTap, | ||
this.leading, | ||
Key? key, | ||
}) : super(key: key); | ||
|
||
/// Displays a [ListTile] inside a [SmoothCard] with a leading [Column] | ||
/// containing the specified [imageProvider] | ||
SmoothListTileCard.image({ | ||
required ImageProvider? imageProvider, | ||
Widget? title, | ||
GestureTapCallback? onTap, | ||
}) : this( | ||
title: title, | ||
onTap: onTap, | ||
leading: SmoothProductImageContainer( | ||
width: VERY_LARGE_SPACE * 5, | ||
height: MEDIUM_SPACE * 5, | ||
child: imageProvider != null | ||
? Image( | ||
image: imageProvider, | ||
fit: BoxFit.cover, | ||
) | ||
: const PictureNotFound(), | ||
), | ||
); | ||
|
||
/// Displays a [ListTile] inside a [SmoothCard] with a leading [Column] | ||
/// containing the specified [icon] | ||
SmoothListTileCard.icon({ | ||
Widget? icon, | ||
Widget? title, | ||
Widget? subtitle, | ||
GestureTapCallback? onTap, | ||
Key? key, | ||
}) : this( | ||
title: title, | ||
subtitle: subtitle, | ||
key: key, | ||
onTap: onTap, | ||
// we use a Column to have the icon centered vertically | ||
leading: Column( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
children: <Widget>[icon ?? const Icon(Icons.edit)], | ||
), | ||
); | ||
|
||
/// Displays a loading card with a shimmering effect | ||
SmoothListTileCard.loading() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand: is that only a "loading" effect for "pictures"? If so call it something like "imageLoading". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a widget that displays the shimmer effect |
||
: this( | ||
title: Shimmer.fromColors( | ||
baseColor: GREY_COLOR, | ||
highlightColor: WHITE_COLOR, | ||
child: Container( | ||
width: VERY_LARGE_SPACE * 8, | ||
height: 10, | ||
decoration: const BoxDecoration( | ||
color: GREY_COLOR, | ||
borderRadius: CIRCULAR_BORDER_RADIUS, | ||
)), | ||
), | ||
leading: Shimmer.fromColors( | ||
baseColor: GREY_COLOR, | ||
highlightColor: WHITE_COLOR, | ||
child: const SmoothProductImageContainer( | ||
width: VERY_LARGE_SPACE * 5, | ||
height: MEDIUM_SPACE * 5, | ||
color: GREY_COLOR, | ||
), | ||
), | ||
); | ||
|
||
final Widget? title; | ||
final Widget? subtitle; | ||
final Widget? leading; | ||
final GestureTapCallback? onTap; | ||
|
||
@override | ||
Widget build(BuildContext context) => SmoothCard( | ||
padding: EdgeInsets.zero, | ||
child: InkWell( | ||
borderRadius: ROUNDED_BORDER_RADIUS, | ||
onTap: onTap, | ||
child: Padding( | ||
padding: const EdgeInsets.all(5.0), | ||
child: ListTile( | ||
onTap: onTap, | ||
title: title, | ||
subtitle: subtitle, | ||
leading: leading, | ||
trailing: Icon(ConstantIcons.instance.getForwardIcon()), | ||
), | ||
), | ||
), | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import 'package:flutter/widgets.dart'; | ||
|
||
class SmoothListView extends ListView { | ||
/// Represents a ListView that can be show progress by displaying a | ||
/// [loadingWidget] at the end of the list. | ||
/// | ||
/// [loading] controls the display of the [loadingWidget] at the end of | ||
/// the list. | ||
SmoothListView.builder({ | ||
required IndexedWidgetBuilder itemBuilder, | ||
required int itemCount, | ||
IndexedWidgetBuilder? loadingWidget, | ||
bool loading = false, | ||
}) : assert(loading == false || loadingWidget != null), | ||
super.builder( | ||
itemCount: loading ? itemCount + 1 : itemCount, | ||
itemBuilder: (BuildContext context, int index) => | ||
loading && (index == itemCount) | ||
// Render a loading card as the last card | ||
? loadingWidget!(context, index) | ||
: itemBuilder(context, index), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused: is that a list card, like a
Card(child: ListTile(...))
, or is that for pictures?You would probably be better off with 2 distinct classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is like a
Card(child: ListTile(...))
but I added some constructor to facilitate the creation of an inner ListTile with a leading image or a leading icon. Should I create different classes? I don't really know when to create a different class vs just create a delegating constructor.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The big misunderstanding is that I never imagined that you would display an image inside a "normal"
ListTile
. I expected something similar to the website: tons of pictures in a grid.I don't know if there were explicit expectations regarding UX/UI, but I must say I'm not a big fan of:
We should start from there: how is it supposed to look? @teolemon Are you OK with the latest screenshots?