Skip to content
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

Feature/swipe between photos 2 #561

Open
wants to merge 8 commits into
base: feature/NO-ISSUE/fix-pipelines-and-linting
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions lib/src/features/gallery/api/gallery_image_provider.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
// ignore_for_file: prefer-extracting-function-callbacks

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ksrvnjord_main_app/src/features/gallery/api/gallery_storage.dart';

// ignore: prefer-static-class
final galleryImageCacheProvider = Provider<Map<String, MemoryImage>>((ref) {
return {}; // Create a map to store cached images.
});

// ignore: prefer-static-class
final galleryImageProvider =
FutureProvider.autoDispose.family<MemoryImage, String>((ref, path) async {
final cache = ref.read(galleryImageCacheProvider); // Access the cache.

// If the image is already cached, return it directly.
if (cache.containsKey(path)) {
return cache[path]!; // Return the cached image.
}

// If not cached, fetch the image from the network.
final url = await ref.watch(galleryFileUrlProvider(path).future);

// Download image from URL and return the image.
final res = await Dio().get(
url,
options: Options(responseType: ResponseType.bytes),
);

return MemoryImage(res.data);
final image = MemoryImage(res.data);

// Cache the image manually.
cache[path] = image;

return image;
});
85 changes: 62 additions & 23 deletions lib/src/features/gallery/pages/gallery_file_page_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ksrvnjord_main_app/src/features/gallery/api/gallery_image_provider.dart';
import 'package:ksrvnjord_main_app/src/features/gallery/api/gallery_image_provider.dart';
import 'package:share_plus/share_plus.dart';

class GalleryFilePageView extends ConsumerStatefulWidget {
Expand All @@ -15,6 +16,7 @@ class GalleryFilePageView extends ConsumerStatefulWidget {
required this.initialIndex,
required this.paths,
});

final int initialIndex;
final List<Reference> paths;

Expand All @@ -31,6 +33,29 @@ class _GalleryFilePageViewState extends ConsumerState<GalleryFilePageView> {
super.initState();
_currentPage = widget.initialIndex;
_pageController = PageController(initialPage: widget.initialIndex);
_preloadImage(ref, _currentPage);
for (int i = 1; i < 4; i += 1) {
_preloadImage(ref, _currentPage + i);
_preloadImage(ref, _currentPage - i);
}
}

void _preloadImage(WidgetRef ref, int index) {
if (index < 0 || index >= widget.paths.length) {
return;
}
final path = widget.paths[index];

// Read from the provider and cache it if needed.
final memoryImageFuture =
ref.read(galleryImageProvider(path.fullPath).future);

memoryImageFuture.then((image) {
// Precache the image only if it's not in the cache already.
if (!imageCache.containsKey(MemoryImage(image.bytes))) {
precacheImage(image, context).ignore();
}
});
}

@override
Expand All @@ -45,7 +70,6 @@ class _GalleryFilePageViewState extends ConsumerState<GalleryFilePageView> {
appBar: AppBar(
title: const Text("Gallery"),
actions: [
// Share Button.
IconButton(
onPressed: () {
final path = widget.paths.elementAtOrNull(_currentPage);
Expand All @@ -54,20 +78,17 @@ class _GalleryFilePageViewState extends ConsumerState<GalleryFilePageView> {

imageVal.when(
data: (image) {
if (kIsWeb) {
// Handle sharing on web or other platforms if needed.
} else {
Share.shareXFiles(
[
XFile.fromData(
image.bytes,
mimeType: "image/jpeg",
name: "foto_$_currentPage.jpg",
),
],
subject: "Foto",
).ignore();
}
// Handle sharing functionality here.
Share.shareXFiles(
[
XFile.fromData(
image.bytes,
mimeType: "image/jpeg",
name: "$path.jpg",
),
],
subject: "Foto",
).ignore();
},
error: (err, _) {},
loading: () {},
Expand All @@ -83,32 +104,50 @@ class _GalleryFilePageViewState extends ConsumerState<GalleryFilePageView> {
controller: _pageController,
onPageChanged: (index) {
setState(() {
bool isForward = _currentPage < index;
_currentPage = index;
_preloadImage(ref, isForward ? index + 3 : index - 3);
});
},
itemBuilder: (context, index) {
final path = widget.paths[index];
final imageVal = ref.watch(galleryImageProvider(path.fullPath));

return imageVal.when(
data: (image) => Image.memory(image.bytes),
error: (err, _) => Center(child: Text('Error: $err')),
loading: () => const Center(child: CircularProgressIndicator()),
);
// Access the manual cache.
final cache = ref.read(galleryImageCacheProvider);

// Check if the image is in the cache.
final image = cache[path];

if (image == null) {
// Fallback: Use galleryImageProvider to load and cache the image.
final imageVal = ref.watch(galleryImageProvider(path.fullPath));

return imageVal.when(
data: (loadedImage) {
// Add the newly loaded image to the cache.
cache[path.fullPath] = loadedImage;

return Image.memory(loadedImage.bytes);
},
error: (err, _) => Center(child: Text('Error: $err')),
loading: () => const Center(child: CircularProgressIndicator()),
);
}

// Render the cached image.
return Image.memory(image.bytes);
},
itemCount: widget.paths.length,
),
onHorizontalDragUpdate: (details) {
if (details.primaryDelta! > 0) {
// Swiping in right direction.
if (_currentPage > 0) {
_pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
} else if (details.primaryDelta! < 0) {
// Swiping in left direction.
if (_currentPage < widget.paths.length - 1) {
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
Expand Down
9 changes: 7 additions & 2 deletions lib/src/features/gallery/widgets/file_button.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ksrvnjord_main_app/src/features/gallery/utils/gallery_view_provider.dart';
import 'package:ksrvnjord_main_app/src/features/gallery/utils/get_thumbnail_reference.dart';
import 'package:ksrvnjord_main_app/src/features/shared/model/thumbnail.dart';
import 'package:styled_widget/styled_widget.dart';
Expand All @@ -19,8 +20,12 @@ class FileButton extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final image = getThumbnailReference(item, Thumbnail.x400)
.getDownloadURL(); // TODO: We should get 800x800 thumbnail if we are in LIST mode, IF we are in GRID mode, we should get 400x400 thumbnail.
// ignore: prefer-boolean-prefixes
final gridOrList = ref.watch(gridOrListViewProvider);
final image = getThumbnailReference(
item,
gridOrList ? Thumbnail.x800 : Thumbnail.x400,
).getDownloadURL();
final navigator = Navigator.of(context);

const padding = 12.0;
Expand Down
3 changes: 2 additions & 1 deletion lib/src/features/shared/model/thumbnail.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:flutter/foundation.dart';

@immutable
class Thumbnail {
abstract final class Thumbnail {
static const String x200 = '_200x200';
static const String x400 = '_400x400';
static const String x800 = '_800x800';
}
Loading
Loading