-
-
Notifications
You must be signed in to change notification settings - Fork 314
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: 3129 Allow to swipe between product images on the full screen image view #3325
Changes from all commits
512284c
2772e17
1288068
f4e14cf
3460fe4
5df856b
8a3a2f1
9866c91
b589c3c
2d5136e
693d477
5ff8425
20fffcb
739b51d
9e1ec8b
bcb3135
7b1fbaa
3bdad42
bde4737
2aa8f9e
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 |
---|---|---|
@@ -1,17 +1,19 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||
import 'package:openfoodfacts/openfoodfacts.dart'; | ||
import 'package:provider/provider.dart'; | ||
import 'package:smooth_app/data_models/product_image_data.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/widgets/images/smooth_images_sliver_list.dart'; | ||
import 'package:smooth_app/generic_lib/widgets/smooth_back_button.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/common/product_refresher.dart'; | ||
import 'package:smooth_app/pages/product/product_image_viewer.dart'; | ||
import 'package:smooth_app/pages/product/confirm_and_upload_picture.dart'; | ||
import 'package:smooth_app/pages/product/product_image_swipeable_view.dart'; | ||
import 'package:smooth_app/widgets/smooth_app_bar.dart'; | ||
import 'package:smooth_app/widgets/smooth_scaffold.dart'; | ||
|
||
|
@@ -32,14 +34,11 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> { | |
late final LocalDatabase _localDatabase; | ||
late final Product _initialProduct; | ||
late Product _product; | ||
|
||
late Map<ProductImageData, ImageProvider?> _selectedImages; | ||
|
||
bool _isRefreshed = false; | ||
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. No |
||
ImageProvider? _provideImage(ProductImageData imageData) => | ||
TransientFile.getImageProvider(imageData, _barcode); | ||
|
||
imageData.imageUrl == null ? null : NetworkImage(imageData.imageUrl!); | ||
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. No |
||
String get _barcode => _initialProduct.barcode!; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
|
@@ -80,7 +79,7 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> { | |
) | ||
: null, | ||
leading: SmoothBackButton( | ||
onPressed: () => Navigator.maybePop(context), | ||
onPressed: () => Navigator.maybePop(context, _isRefreshed), | ||
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. No |
||
), | ||
), | ||
body: RefreshIndicator( | ||
|
@@ -97,9 +96,16 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> { | |
), | ||
SmoothImagesSliverList( | ||
imagesData: _selectedImages, | ||
onTap: (ProductImageData data, _) => | ||
TransientFile.isImageAvailable(data, _barcode) | ||
? _openImage(data) | ||
onTap: ( | ||
ProductImageData data, | ||
_, | ||
int? currentProductImageDataIndex, | ||
) => | ||
data.imageUrl != null | ||
? _openImage( | ||
selectedImages: _selectedImages, | ||
index: currentProductImageDataIndex ?? 0, | ||
) | ||
: _newImage(data), | ||
), | ||
], | ||
|
@@ -119,22 +125,54 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> { | |
), | ||
), | ||
); | ||
|
||
Future<void> _openImage(ProductImageData imageData) async => | ||
Navigator.push<void>( | ||
Future<void> _openImage({ | ||
required int index, | ||
required Map<ProductImageData, ImageProvider?> selectedImages, | ||
}) async => | ||
Navigator.push( | ||
context, | ||
MaterialPageRoute<void>( | ||
builder: (_) => ProductImageViewer( | ||
product: _product, | ||
imageField: imageData.imageField, | ||
), | ||
builder: (_) { | ||
return ProductImageSwipeableView( | ||
barcode: _barcode, | ||
initialProductImageDataIndex: index, | ||
selectedImages: _selectedImages, | ||
); | ||
}, | ||
), | ||
); | ||
Future<void> _newImage(ProductImageData data) async { | ||
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. No |
||
final File? croppedImageFile = await startNewImageCropping(this); | ||
if (croppedImageFile == null) { | ||
return; | ||
} | ||
if (!mounted) { | ||
return; | ||
} | ||
setState(() { | ||
final FileImage fileImage = FileImage(croppedImageFile); | ||
final ImageField imageField = data.imageField; | ||
for (final ProductImageData productImageData in _selectedImages.keys) { | ||
if (productImageData.imageField == imageField) { | ||
_selectedImages[productImageData] = fileImage; | ||
return; | ||
} | ||
} | ||
}); | ||
final File? uploaded = await Navigator.push<File>( | ||
context, | ||
MaterialPageRoute<File>( | ||
builder: (BuildContext context) => ConfirmAndUploadPicture( | ||
barcode: _barcode, | ||
imageField: data.imageField, | ||
initialPhoto: croppedImageFile, | ||
), | ||
), | ||
); | ||
final bool isUploaded = uploaded != null; | ||
|
||
Future<void> _newImage(ProductImageData data) async => | ||
confirmAndUploadNewPicture( | ||
this, | ||
barcode: _barcode, | ||
imageField: data.imageField, | ||
); | ||
if (isUploaded) { | ||
_isRefreshed = true; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import 'package:flutter/material.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/widgets/smooth_back_button.dart'; | ||
import 'package:smooth_app/pages/product/product_image_viewer.dart'; | ||
|
||
///Widget to display swipeable product images, | ||
///Opens product image with [initialProductImageDataIndex] from list of images Typecasted from [selectedImages] | ||
class ProductImageSwipeableView extends StatefulWidget { | ||
const ProductImageSwipeableView({ | ||
super.key, | ||
required this.selectedImages, | ||
required this.initialProductImageDataIndex, | ||
required this.barcode, | ||
}); | ||
|
||
final Map<ProductImageData, ImageProvider?> selectedImages; | ||
final int initialProductImageDataIndex; | ||
final String barcode; | ||
|
||
@override | ||
State<ProductImageSwipeableView> createState() => | ||
_ProductImageSwipeableViewState(); | ||
} | ||
|
||
class _ProductImageSwipeableViewState extends State<ProductImageSwipeableView> { | ||
final ValueNotifier<int> _currentImageDataIndex = ValueNotifier<int>(0); | ||
late List<ProductImageData> _imageDataList; | ||
late PageController _controller; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
_currentImageDataIndex.value = widget.initialProductImageDataIndex; | ||
_imageDataList = List<ProductImageData>.from(widget.selectedImages.keys); | ||
_controller = PageController( | ||
initialPage: widget.initialProductImageDataIndex, | ||
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. Let's say |
||
); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
backgroundColor: Colors.black, | ||
foregroundColor: WHITE_COLOR, | ||
elevation: 0, | ||
title: ValueListenableBuilder<int>( | ||
valueListenable: _currentImageDataIndex, | ||
builder: (_, int index, __) { | ||
return Text( | ||
_imageDataList[index].title, | ||
); | ||
}, | ||
), | ||
leading: SmoothBackButton( | ||
iconColor: Colors.white, | ||
onPressed: () => Navigator.maybePop(context), | ||
), | ||
), | ||
body: PageView.builder( | ||
onPageChanged: (int index) { | ||
_currentImageDataIndex.value = index; | ||
}, | ||
controller: _controller, | ||
itemCount: widget.selectedImages.keys.length, | ||
itemBuilder: (BuildContext context, int index) { | ||
return ProductImageViewer( | ||
monsieurtanuki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
barcode: widget.barcode, | ||
imageData: _imageDataList[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.
No