diff --git a/lib/features/home/api/unsplash_images_api.dart b/lib/features/home/api/unsplash_images_api.dart index e563367..82b8f84 100644 --- a/lib/features/home/api/unsplash_images_api.dart +++ b/lib/features/home/api/unsplash_images_api.dart @@ -25,7 +25,7 @@ class UnsplashImagesApi with LoggingService implements ImagesApi { return ImageModel( imageIndex: imageIndex, uri: imageUri, - imageName: '${Strings.current.image} $imageIndex: size=$imageSide', + imageName: '${Strings.current.image} ${imageIndex + 1}: size=$imageSide', ); }); } on Exception catch (ex, stackTrace) { diff --git a/lib/features/home/services/images_service.dart b/lib/features/home/services/images_service.dart index d26e5c1..ce04a3a 100644 --- a/lib/features/home/services/images_service.dart +++ b/lib/features/home/services/images_service.dart @@ -36,6 +36,9 @@ class ImagesService { int get firstAvailableImageIndex => 0; int get lastAvailableImageIndex => _imageModels.length - 1; + int get numberOfImages => _imageModels.length; + + ImageModel imageModelAt({required int index}) => _imageModels.elementAt(index); static ImagesService get locate => Locator.locate(); } diff --git a/lib/features/home/views/gallery/gallery_view.dart b/lib/features/home/views/gallery/gallery_view.dart index fb436cb..cde03b3 100644 --- a/lib/features/home/views/gallery/gallery_view.dart +++ b/lib/features/home/views/gallery/gallery_view.dart @@ -46,15 +46,12 @@ class GalleryView extends StatelessWidget { context, imageModel: imageModel, ), - child: Hero( - tag: imageModel.imageIndex.toString(), - child: CachedNetworkImage( - imageUrl: imageModel.uri.toString(), - cacheKey: imageModel.imageIndex.toString(), - progressIndicatorBuilder: (_, __, final progress) => - CircularProgressIndicator( - value: model.downloadProgressValue(progress: progress), - ), + child: CachedNetworkImage( + imageUrl: imageModel.uri.toString(), + cacheKey: imageModel.imageIndex.toString(), + progressIndicatorBuilder: (_, __, final progress) => + CircularProgressIndicator( + value: model.downloadProgressValue(progress: progress), ), ), ), diff --git a/lib/features/home/views/image_carousel/image_carousel_view.dart b/lib/features/home/views/image_carousel/image_carousel_view.dart index 13dae54..8b1d9b7 100644 --- a/lib/features/home/views/image_carousel/image_carousel_view.dart +++ b/lib/features/home/views/image_carousel/image_carousel_view.dart @@ -1,9 +1,12 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:mc_gallery/features/core/data/constants/const_colors.dart'; -import 'package:mc_gallery/features/core/data/constants/const_text.dart'; +import 'package:mc_gallery/features/home/data/models/image_model.dart'; +import '/features/core/data/constants/const_colors.dart'; +import '/features/core/data/constants/const_text.dart'; import '/features/core/widgets/gap.dart'; import '/features/core/widgets/mcg_scaffold.dart'; import '/features/core/widgets/view_model_builder.dart'; @@ -28,6 +31,7 @@ class ImageCarouselView extends StatelessWidget { viewModelBuilder: () => ImageCarouselViewModel.locate, argumentBuilder: () => imageCarouselViewArguments, builder: (context, final model) => McgScaffold( + bodyBuilderWaiter: model.isInitialised, appBar: AppBar( title: Text(model.strings.imageCarousel), ), @@ -36,41 +40,59 @@ class ImageCarouselView extends StatelessWidget { Expanded( child: Card( elevation: 8, - child: Stack( - fit: StackFit.expand, - children: [ - Hero( - tag: model.currentImageKey, - child: CachedNetworkImage( - imageUrl: model.currentImageUrl, - cacheKey: model.currentImageKey, - fit: BoxFit.fill, - progressIndicatorBuilder: (_, __, final progress) => - CircularProgressIndicator( - value: model.downloadProgressValue(progress: progress), + child: CarouselSlider.builder( + itemCount: model.numberOfImages, + options: CarouselOptions( + enlargeFactor: 1, + enlargeCenterPage: true, + enlargeStrategy: CenterPageEnlargeStrategy.scale, + disableCenter: true, + aspectRatio: 1, + initialPage: model.currentImageIndex, + enableInfiniteScroll: false, + onPageChanged: (final index, _) => model.swipedTo(newIndex: index), + ), + itemBuilder: (context, _, __) => Stack( + fit: StackFit.expand, + children: [ + ValueListenableBuilder( + valueListenable: model.currentImageModelListenable, + builder: (context, _, __) => CachedNetworkImage( + imageUrl: model.currentImageUrl, + cacheKey: model.currentImageKey, + fit: BoxFit.fill, + progressIndicatorBuilder: (_, __, final progress) => + CircularProgressIndicator( + value: model.downloadProgressValue(progress: progress), + ), ), ), - ), - Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Icon( - Icons.chevron_left, - color: model.hasPreviousImage ? ConstColours.white : ConstColours.black, + Center( + child: ValueListenableBuilder( + valueListenable: model.currentImageModelListenable, + builder: (context, _, __) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon( + Icons.chevron_left, + color: model.hasPreviousImage + ? ConstColours.white + : ConstColours.black, + ), + AutoSizeText( + model.currentImageName, + style: ConstText.imageOverlayTextStyle(context), + ), + Icon( + Icons.chevron_right, + color: model.hasNextImage ? ConstColours.white : ConstColours.black, + ), + ], ), - Text( - model.currentImageName, - style: ConstText.imageOverlayTextStyle(context), - ), - Icon( - Icons.chevron_right, - color: model.hasNextImage ? ConstColours.white : ConstColours.black, - ), - ], + ), ), - ), - ], + ], + ), ), ), ), diff --git a/lib/features/home/views/image_carousel/image_carousel_view_model.dart b/lib/features/home/views/image_carousel/image_carousel_view_model.dart index a9579bc..3381ec1 100644 --- a/lib/features/home/views/image_carousel/image_carousel_view_model.dart +++ b/lib/features/home/views/image_carousel/image_carousel_view_model.dart @@ -22,13 +22,14 @@ class ImageCarouselViewModel extends BaseViewModel { final NavigationService _navigationService; final LoggingService _loggingService; - late final ValueNotifier _currentImageModel; - ValueListenable get currentImageModel => _currentImageModel; + late final ValueNotifier _currentImageModelNotifier; + ValueListenable get currentImageModelListenable => _currentImageModelNotifier; @override Future initialise(bool Function() mounted, [arguments]) async { - _currentImageModel = ValueNotifier(_imagesService.imageModels + _currentImageModelNotifier = ValueNotifier(_imagesService.imageModels .elementAt((arguments! as ImageCarouselViewArguments).imageIndexKey)); + _loggingService.info('Initialized with image: ${_currentImageModelNotifier.value.imageIndex}'); super.initialise(mounted, arguments); } @@ -38,17 +39,25 @@ class ImageCarouselViewModel extends BaseViewModel { super.dispose(); } - String get currentImageUrl => currentImageModel.value.uri.toString(); - String get currentImageKey => currentImageModel.value.imageIndex.toString(); - String get currentImageName => currentImageModel.value.imageName; + void swipedTo({required int newIndex}) { + _currentImageModelNotifier.value = _imagesService.imageModelAt(index: newIndex); + _loggingService.info('Swiped to image: ${_currentImageModelNotifier.value.imageIndex}'); + } + + String get currentImageUrl => currentImageModelListenable.value.uri.toString(); + String get currentImageKey => currentImageModelListenable.value.imageIndex.toString(); + String get currentImageName => currentImageModelListenable.value.imageName; + int get currentImageIndex => currentImageModelListenable.value.imageIndex; + + int get numberOfImages => _imagesService.numberOfImages; double? downloadProgressValue({required DownloadProgress progress}) => progress.totalSize != null ? progress.downloaded / progress.totalSize! : null; bool get hasPreviousImage => - currentImageModel.value.imageIndex > _imagesService.firstAvailableImageIndex; + currentImageModelListenable.value.imageIndex > _imagesService.firstAvailableImageIndex; bool get hasNextImage => - currentImageModel.value.imageIndex < _imagesService.lastAvailableImageIndex; + currentImageModelListenable.value.imageIndex < _imagesService.lastAvailableImageIndex; static ImageCarouselViewModel get locate => Locator.locate(); } diff --git a/pubspec.lock b/pubspec.lock index 9cbd01a..ca2f9cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -43,6 +43,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.9.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -71,6 +78,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + carousel_slider: + dependency: "direct main" + description: + name: carousel_slider + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.1" characters: dependency: transitive description: @@ -545,7 +559,7 @@ packages: name: talker url: "https://pub.dartlang.org" source: hosted - version: "2.1.0+1" + version: "2.2.0" talker_dio_logger: dependency: "direct main" description: @@ -559,7 +573,7 @@ packages: name: talker_logger url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 637ddb8..1a242bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,9 +32,11 @@ dependencies: # Util frontend flutter_markdown: ^0.6.13 + auto_size_text: ^3.0.0 + carousel_slider: ^4.2.1 # Logging - talker: ^2.1.0+1 + talker: ^2.2.0 talker_dio_logger: ^1.0.0 # Assets