better scrolling

This commit is contained in:
mertalev 2025-08-13 18:01:47 -04:00
parent c988342de1
commit 3100702e93
No known key found for this signature in database
GPG key ID: DF6ABC77AAD98C95
8 changed files with 233 additions and 88 deletions

View file

@ -1,30 +1,43 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/widgets/common/transparent_image.dart';
class FadeInPlaceholderImage extends StatelessWidget {
final Widget placeholder;
final ImageProvider image;
final Duration duration;
final BoxFit fit;
final double width;
final double height;
const FadeInPlaceholderImage({
super.key,
required this.placeholder,
required this.image,
required this.width,
required this.height,
this.duration = const Duration(milliseconds: 100),
this.fit = BoxFit.cover,
});
@override
Widget build(BuildContext context) {
return SizedBox.expand(
child: Stack(
fit: StackFit.expand,
children: [
placeholder,
FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)),
],
),
final stopwatch = Stopwatch()..start();
return Image(
image: image,
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
if (frame == null) {
return AnimatedSwitcher(duration: duration, child: placeholder);
}
stopwatch.stop();
if (stopwatch.elapsedMilliseconds < 32) {
return child;
}
return AnimatedSwitcher(duration: duration, child: child);
},
filterQuality: FilterQuality.low,
fit: fit,
width: width,
height: height,
);
}
}

View file

@ -61,7 +61,7 @@ class ImmichThumbnail extends HookConsumerWidget {
customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) {
thumbnailProviderInstance.evict();
final originalErrorWidgetBuilder = blurHashErrorBuilder(blurhash, fit: fit);
final originalErrorWidgetBuilder = blurHashErrorBuilder(blurhash, width: width, height: height, fit: fit);
return originalErrorWidgetBuilder(ctx, error, stackTrace);
}
@ -72,7 +72,7 @@ class ImmichThumbnail extends HookConsumerWidget {
fadeInDuration: Duration.zero,
fadeOutDuration: const Duration(milliseconds: 100),
octoSet: OctoSet(
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, width: width, height: height, fit: fit),
errorBuilder: customErrorBuilder,
),
image: thumbnailProviderInstance,

View file

@ -6,25 +6,40 @@ import 'package:octo_image/octo_image.dart';
/// Simple set to show [OctoPlaceholder.circularProgressIndicator] as
/// placeholder and [OctoError.icon] as error.
OctoSet blurHashOrPlaceholder(Uint8List? blurhash, {BoxFit? fit, Text? errorMessage}) {
OctoSet blurHashOrPlaceholder(
Uint8List? blurhash, {
required double width,
required double height,
BoxFit? fit,
Text? errorMessage,
}) {
return OctoSet(
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage),
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, width: width, height: height, fit: fit),
errorBuilder: blurHashErrorBuilder(blurhash, width: width, height: height, fit: fit, message: errorMessage),
);
}
OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) {
OctoPlaceholderBuilder blurHashPlaceholderBuilder(
Uint8List? blurhash, {
required double width,
required double height,
BoxFit? fit,
}) {
return (context) => blurhash == null
? const ThumbnailPlaceholder()
: FadeInPlaceholderImage(
placeholder: const ThumbnailPlaceholder(),
image: MemoryImage(blurhash),
fit: fit ?? BoxFit.cover,
width: width,
height: height,
);
}
OctoErrorBuilder blurHashErrorBuilder(
Uint8List? blurhash, {
required double width,
required double height,
BoxFit? fit,
Text? message,
IconData? icon,
@ -32,7 +47,7 @@ OctoErrorBuilder blurHashErrorBuilder(
double? iconSize,
}) {
return OctoError.placeholderWithErrorIcon(
blurHashPlaceholderBuilder(blurhash, fit: fit),
blurHashPlaceholderBuilder(blurhash, width: width, height: width, fit: fit),
message: message,
icon: icon,
iconColor: iconColor,