Favourites
This commit is contained in:
parent
6a84a9bef0
commit
1747ab0245
23 changed files with 469 additions and 67 deletions
|
@ -15,30 +15,108 @@ class _DownloadedGalleryView extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
// Using Wrap instead of GridView, to make use of different image sizes
|
||||
child: Wrap(
|
||||
runSpacing: 24,
|
||||
spacing: 8,
|
||||
alignment: WrapAlignment.center,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
for (final imageModel in galleryViewModel.imageModels)
|
||||
GestureDetector(
|
||||
onTap: () => galleryViewModel.pushImageCarouselView(
|
||||
context,
|
||||
imageModel: imageModel,
|
||||
child: ValueListenableBuilder<bool>(
|
||||
valueListenable: galleryViewModel.isViewingFavouriteListenable,
|
||||
builder: (context, final isViewingFavourites, _) => !isViewingFavourites
|
||||
? Wrap(
|
||||
runSpacing: 24,
|
||||
spacing: 8,
|
||||
alignment: WrapAlignment.center,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
for (final imageModel in galleryViewModel.imageModels)
|
||||
_StarrableImage(
|
||||
key: ValueKey(imageModel.imageIndex),
|
||||
imageModel: imageModel,
|
||||
galleryViewModel: galleryViewModel,
|
||||
),
|
||||
],
|
||||
)
|
||||
: Wrap(
|
||||
runSpacing: 24,
|
||||
spacing: 8,
|
||||
alignment: WrapAlignment.center,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
for (final favouriteImageModel in galleryViewModel.favouriteImageModels)
|
||||
_StarrableImage(
|
||||
key: ValueKey(favouriteImageModel.imageIndex),
|
||||
imageModel: favouriteImageModel,
|
||||
galleryViewModel: galleryViewModel,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: imageModel.uri.toString(),
|
||||
cacheKey: imageModel.imageIndex.toString(),
|
||||
progressIndicatorBuilder: (_, __, final progress) => CircularProgressIndicator(
|
||||
value: galleryViewModel.downloadProgressValue(progress: progress),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StarrableImage extends StatefulWidget {
|
||||
const _StarrableImage({
|
||||
required this.galleryViewModel,
|
||||
required this.imageModel,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final GalleryViewModel galleryViewModel;
|
||||
final ImageModel imageModel;
|
||||
|
||||
@override
|
||||
State<_StarrableImage> createState() => _StarrableImageState();
|
||||
}
|
||||
|
||||
class _StarrableImageState extends State<_StarrableImage> {
|
||||
late bool isMarkedFavourite;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
isMarkedFavourite = widget.imageModel.isFavourite;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
alignment: Alignment.topRight.add(const Alignment(0.2, -0.2)),
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => widget.galleryViewModel.pushImageCarouselView(
|
||||
context,
|
||||
imageModel: widget.imageModel,
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: widget.imageModel.uri.toString(),
|
||||
cacheKey: widget.imageModel.imageIndex.toString(),
|
||||
progressIndicatorBuilder: (_, __, final progress) => CircularProgressIndicator(
|
||||
value: widget.galleryViewModel.downloadProgressValue(progress: progress),
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
child: isMarkedFavourite
|
||||
? ConstMedia.buildIcon(
|
||||
ConstMedia.favStarFilled,
|
||||
width: 16,
|
||||
height: 16,
|
||||
)
|
||||
: ConstMedia.buildIcon(
|
||||
ConstMedia.favStarOutline,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
onTap: () {
|
||||
widget.galleryViewModel.updateImageFavouriteStatus(
|
||||
imageModel: widget.imageModel,
|
||||
newFavouriteStatus: !isMarkedFavourite,
|
||||
);
|
||||
setState(() => isMarkedFavourite = !isMarkedFavourite);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mc_gallery/features/core/data/constants/const_media.dart';
|
||||
|
||||
import '/features/core/data/constants/const_colors.dart';
|
||||
import '/features/core/data/constants/const_durations.dart';
|
||||
|
@ -80,9 +81,21 @@ class GalleryView extends StatelessWidget {
|
|||
valueListenable: model.isSearchingListenable,
|
||||
builder: (context, final isSearching, _) => AnimatedSwitcher(
|
||||
duration: ConstDurations.oneAndHalfDefaultAnimationDuration,
|
||||
child: !isSearching
|
||||
? _DownloadedGalleryView(galleryViewModel: model)
|
||||
: _SearchGalleryView(galleryViewModel: model),
|
||||
child: Column(
|
||||
children: [
|
||||
ValueListenableBuilder<bool>(
|
||||
valueListenable: model.isViewingFavouriteListenable,
|
||||
builder: (context, final isViewingFavourites, child) =>
|
||||
Switch(
|
||||
value: isViewingFavourites,
|
||||
onChanged: model.onFavouriteViewChange,
|
||||
),
|
||||
),
|
||||
!isSearching
|
||||
? _DownloadedGalleryView(galleryViewModel: model)
|
||||
: _SearchGalleryView(galleryViewModel: model),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:mc_gallery/features/core/data/extensions/value_notifier_extensions.dart';
|
||||
|
||||
import '/features/core/abstracts/base_view_model.dart';
|
||||
import '/features/core/services/logging_service.dart';
|
||||
|
@ -27,6 +28,7 @@ class GalleryViewModel extends BaseViewModel {
|
|||
|
||||
final ImagesService _imagesService;
|
||||
final NavigationService _navigationService;
|
||||
//todo(mehul): Use to implement pull-to-refresh or an extra widget
|
||||
final ImageCacheManagerService _imageCacheManagerService;
|
||||
final LoggingService _loggingService;
|
||||
|
||||
|
@ -40,6 +42,9 @@ class GalleryViewModel extends BaseViewModel {
|
|||
final ValueNotifier<List<ImageModel>> _imageSearchResultsNotifier = ValueNotifier([]);
|
||||
ValueListenable<List<ImageModel>> get imageSearchResultsListenable => _imageSearchResultsNotifier;
|
||||
|
||||
final ValueNotifier<bool> _isViewingFavouriteNotifier = ValueNotifier(false);
|
||||
ValueListenable<bool> get isViewingFavouriteListenable => _isViewingFavouriteNotifier;
|
||||
|
||||
@override
|
||||
Future<void> initialise(bool Function() mounted, [arguments]) async {
|
||||
super.initialise(mounted, arguments);
|
||||
|
@ -80,7 +85,7 @@ class GalleryViewModel extends BaseViewModel {
|
|||
_loggingService.info('Clearing of results on view mode change');
|
||||
}
|
||||
|
||||
_isSearchingNotifier.value = !_isSearchingNotifier.value;
|
||||
_isSearchingNotifier.flipValue();
|
||||
}
|
||||
|
||||
Future<void> get lastQueryResultDone => _imagesService.lastQueryIsCompleted;
|
||||
|
@ -92,9 +97,24 @@ class GalleryViewModel extends BaseViewModel {
|
|||
_imageSearchResultsNotifier.value = [];
|
||||
_loggingService.info('Cleared resultsw from view');
|
||||
|
||||
//todo(mehul): Either redo search or force user to type in by clearing field
|
||||
//todo(mehul): Either redo search or force user to type in new (trigger) by clearing field
|
||||
}
|
||||
|
||||
void onFavouriteViewChange(bool newValue) => _isViewingFavouriteNotifier.value = newValue;
|
||||
|
||||
void updateImageFavouriteStatus({
|
||||
required ImageModel imageModel,
|
||||
required bool newFavouriteStatus,
|
||||
}) {
|
||||
_imagesService.updateImageFavouriteStatus(
|
||||
imageModel: imageModel,
|
||||
newFavouriteStatus: newFavouriteStatus,
|
||||
);
|
||||
}
|
||||
|
||||
Iterable<ImageModel> get favouriteImageModels =>
|
||||
imageModels.where((final imageModel) => imageModel.isFavourite);
|
||||
|
||||
void onPromptPressed() => _isDisplayingPressingPrompt.value = false;
|
||||
|
||||
Iterable<ImageModel> get imageModels => _imagesService.imageModels;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue