Skip to content

Commit

Permalink
feat: Better product image view (#1122)
Browse files Browse the repository at this point in the history
* feat: Better product image view

* Refactor

* Update product_image_gallery_view.dart
  • Loading branch information
M123-dev authored Feb 12, 2022
1 parent 3ccbd29 commit aa976c7
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 86 deletions.
40 changes: 17 additions & 23 deletions packages/smooth_app/lib/cards/data_cards/image_upload_card.dart
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/data_models/product_image_data.dart';
import 'package:smooth_app/helpers/picture_capture_helper.dart';
import 'package:smooth_app/pages/image_crop_page.dart';
import 'package:smooth_app/pages/product/product_image_page.dart';
import 'package:smooth_app/pages/product/product_image_gallery_view.dart';

class ImageUploadCard extends StatefulWidget {
const ImageUploadCard({
required this.product,
required this.imageField,
this.imageUrl,
this.title,
required this.buttonText,
required this.productImageData,
required this.allProductImagesData,
required this.onUpload,
});

final Product product;
final ImageField imageField;
final String? imageUrl;
final String? title;
final String buttonText;
final ProductImageData productImageData;
final List<ProductImageData> allProductImagesData;
final Function(BuildContext) onUpload;

@override
Expand Down Expand Up @@ -53,7 +49,7 @@ class _ImageUploadCardState extends State<ImageUploadCard> {
context,
barcode: widget.product
.barcode!, //Probably throws an error, but this is not a big problem when we got a product without a barcode
imageField: widget.imageField,
imageField: widget.productImageData.imageField,
imageUri: croppedImageFile.uri,
);
croppedImageFile.delete();
Expand All @@ -68,10 +64,9 @@ class _ImageUploadCardState extends State<ImageUploadCard> {
// We can already have an _imageProvider for a file that is going to be uploaded
// or an imageUrl for a network image
// or no image yet
final AppLocalizations appLocalizations = AppLocalizations.of(context)!;

if ((_imageProvider == null) && (widget.imageUrl != null)) {
_imageProvider = NetworkImage(widget.imageUrl!);
if ((_imageProvider == null) &&
(widget.productImageData.imageUrl != null)) {
_imageProvider = NetworkImage(widget.productImageData.imageUrl!);
}

if (_imageProvider != null) {
Expand All @@ -85,19 +80,18 @@ class _ImageUploadCardState extends State<ImageUploadCard> {

if (_imageFullProvider == null) {
final String _imageFullUrl =
widget.imageUrl!.replaceAll('.400.', '.full.');
widget.productImageData.imageUrl!.replaceAll('.400.', '.full.');
_imageFullProvider = NetworkImage(_imageFullUrl);
}

Navigator.push<Widget>(
context,
MaterialPageRoute<Widget>(
builder: (BuildContext context) => ProductImagePage(
product: widget.product,
imageField: widget.imageField,
imageProvider: _imageFullProvider!,
title: widget.title ?? appLocalizations.image,
buttonText: widget.buttonText),
builder: (BuildContext context) => ProductImageGalleryView(
productImageData: widget.productImageData,
allProductImagesData: widget.allProductImagesData,
title: widget.productImageData.title,
),
),
);
},
Expand All @@ -106,7 +100,7 @@ class _ImageUploadCardState extends State<ImageUploadCard> {
return ElevatedButton.icon(
onPressed: _getImage,
icon: const Icon(Icons.add_a_photo),
label: Text(widget.buttonText),
label: Text(widget.productImageData.buttonText),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/model/Product.dart';
import 'package:openfoodfacts/model/ProductImage.dart';
import 'package:smooth_app/cards/data_cards/image_upload_card.dart';
import 'package:smooth_app/data_models/product_image_data.dart';

class ProductImageCarousel extends StatelessWidget {
const ProductImageCarousel(
Expand All @@ -18,46 +19,36 @@ class ProductImageCarousel extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context)!;
final List<ImageUploadCard> carouselItems = <ImageUploadCard>[
ImageUploadCard(
product: product,
final List<ProductImageData> allProductImagesData = <ProductImageData>[
ProductImageData(
imageField: ImageField.FRONT,
imageUrl: product.imageFrontUrl,
title: appLocalizations.product,
buttonText: appLocalizations.front_photo,
onUpload: onUpload,
),
ImageUploadCard(
product: product,
ProductImageData(
imageField: ImageField.INGREDIENTS,
imageUrl: product.imageIngredientsUrl,
title: appLocalizations.ingredients,
buttonText: appLocalizations.ingredients_photo,
onUpload: onUpload,
),
ImageUploadCard(
product: product,
ProductImageData(
imageField: ImageField.NUTRITION,
imageUrl: product.imageNutritionUrl,
title: appLocalizations.nutrition,
buttonText: appLocalizations.nutrition_facts_photo,
onUpload: onUpload,
),
ImageUploadCard(
product: product,
ProductImageData(
imageField: ImageField.PACKAGING,
imageUrl: product.imagePackagingUrl,
title: appLocalizations.packaging_information,
buttonText: appLocalizations.packaging_information_photo,
onUpload: onUpload,
),
ImageUploadCard(
product: product,
ProductImageData(
imageField: ImageField.OTHER,
imageUrl: null,
title: appLocalizations.more_photos,
buttonText: appLocalizations.more_photos,
onUpload: onUpload,
),
];

Expand All @@ -66,12 +57,17 @@ class ProductImageCarousel extends StatelessWidget {
child: ListView(
// This next line does the trick.
scrollDirection: Axis.horizontal,
children: carouselItems
children: allProductImagesData
.map(
(ImageUploadCard item) => Container(
(ProductImageData item) => Container(
margin: const EdgeInsets.fromLTRB(0, 0, 5, 0),
decoration: const BoxDecoration(color: Colors.black12),
child: item,
child: ImageUploadCard(
product: product,
productImageData: item,
allProductImagesData: allProductImagesData,
onUpload: onUpload,
),
),
)
.toList(),
Expand Down
15 changes: 15 additions & 0 deletions packages/smooth_app/lib/data_models/product_image_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:openfoodfacts/model/ProductImage.dart';

class ProductImageData {
const ProductImageData({
required this.imageField,
required this.title,
required this.buttonText,
this.imageUrl,
});

final ImageField imageField;
final String title;
final String buttonText;
final String? imageUrl;
}
106 changes: 106 additions & 0 deletions packages/smooth_app/lib/pages/product/product_image_gallery_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:smooth_app/data_models/product_image_data.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_gauge.dart';

class ProductImageGalleryView extends StatefulWidget {
const ProductImageGalleryView({
required this.title,
required this.productImageData,
required this.allProductImagesData,
});

final String title;
final ProductImageData productImageData;
final List<ProductImageData> allProductImagesData;

@override
State<ProductImageGalleryView> createState() =>
_ProductImageGalleryViewState();
}

class _ProductImageGalleryViewState extends State<ProductImageGalleryView> {
late final PageController _controller;
late final List<ProductImageData> images = <ProductImageData>[];
late String title;

@override
void initState() {
title = widget.title;

for (final ProductImageData element in widget.allProductImagesData) {
if (element.imageUrl != null) {
images.add(element);
}
}

_controller = PageController(
initialPage: widget.allProductImagesData.indexOf(
images.firstWhere((ProductImageData element) =>
element.imageUrl == widget.productImageData.imageUrl),
),
);
super.initState();
}

@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context)!;

//When all are empty there shouldn't be a way to access this page
if (images.isEmpty) {
return Scaffold(
body: Center(
child: Text(appLocalizations.error),
),
);
}
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text(title),
),
backgroundColor: Colors.black,
body: PhotoViewGallery.builder(
pageController: _controller,
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(images[index].imageUrl!),
initialScale: PhotoViewComputedScale.contained * 0.8,
minScale: PhotoViewComputedScale.contained * 0.8,
maxScale: PhotoViewComputedScale.covered * 1.1,
heroAttributes:
PhotoViewHeroAttributes(tag: images[index].imageUrl!),
);
},
itemCount: images.length,
loadingBuilder:
(final BuildContext context, final ImageChunkEvent? event) {
return Center(
child: SmoothGauge(
color: Theme.of(context).colorScheme.onBackground,
value: event == null ||
event.expectedTotalBytes == null ||
event.expectedTotalBytes == 0
? 0
: event.cumulativeBytesLoaded / event.expectedTotalBytes!,
),
);
},
backgroundDecoration: const BoxDecoration(
color: Colors.black,
),
onPageChanged: (int index) {
setState(() {
title = images[index].title;
});
},
),
);
}
}
44 changes: 0 additions & 44 deletions packages/smooth_app/lib/pages/product/product_image_page.dart

This file was deleted.

0 comments on commit aa976c7

Please sign in to comment.