Adds blurhash

format
This commit is contained in:
Marty Fuhry
2024-02-21 10:37:12 -05:00
parent 84cd91bbbe
commit b05d4fa7d3
8 changed files with 343 additions and 54 deletions
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/shared/ui/transparent_image.dart';
class FadeInPlaceholderImage extends StatelessWidget {
final Widget placeholder;
final ImageProvider image;
final Duration duration;
final BoxFit fit;
const FadeInPlaceholderImage({
super.key,
required this.placeholder,
required this.image,
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: const Duration(milliseconds: 100),
image: image,
fit: fit,
placeholder: MemoryImage(kTransparentImage),
),
],
),
);
}
}
-4
View File
@@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@@ -41,7 +40,6 @@ class ImmichImage extends StatelessWidget {
}
if (asset == null) {
print('using remote for $assetId');
return ImmichRemoteImageProvider(
assetId: assetId!,
isThumbnail: false,
@@ -49,12 +47,10 @@ class ImmichImage extends StatelessWidget {
}
if (useLocal(asset)) {
print('using local for ${asset.localId}');
return ImmichLocalImageProvider(
asset: asset,
);
} else {
print('using remote for ${asset.localId}');
return ImmichRemoteImageProvider(
assetId: asset.remoteId!,
isThumbnail: false,
+33 -38
View File
@@ -1,25 +1,24 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_local_thumbnail_provider.dart';
import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_remote_image_provider.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/ui/thumbhash_placeholder.dart';
import 'package:octo_image/octo_image.dart';
import 'package:thumbhash/thumbhash.dart' as thumbhash;
class ImmichThumbnail extends StatelessWidget {
class ImmichThumbnail extends StatefulWidget {
const ImmichThumbnail({
this.asset,
this.width = 250,
this.height = 250,
this.fit = BoxFit.cover,
this.placeholder,
super.key,
});
final Asset? asset;
final Widget? placeholder;
final double width;
final double height;
final BoxFit fit;
@@ -63,51 +62,47 @@ class ImmichThumbnail extends StatelessWidget {
static bool useLocal(Asset asset) => !asset.isRemote || asset.isLocal;
@override
State<ImmichThumbnail> createState() => _ImmichThumbnailState();
}
class _ImmichThumbnailState extends State<ImmichThumbnail> {
Uint8List? _decodedBlurHash;
@override
void initState() {
if (widget.asset?.thumbhash != null) {
final rgbaImage =
thumbhash.thumbHashToRGBA(base64Decode(widget.asset!.thumbhash!));
_decodedBlurHash = thumbhash.rgbaToBmp(rgbaImage);
}
super.initState();
}
@override
Widget build(BuildContext context) {
if (asset == null) {
if (widget.asset == null) {
return Container(
color: Colors.grey,
width: width,
height: height,
width: widget.width,
height: widget.height,
child: const Center(
child: Icon(Icons.no_photography),
),
);
}
return OctoImage(
fadeInDuration: const Duration(milliseconds: 0),
return OctoImage.fromSet(
placeholderFadeInDuration: Duration.zero,
fadeInDuration: Duration.zero,
fadeOutDuration: const Duration(milliseconds: 100),
placeholderBuilder: (context) {
return placeholder ??
ThumbnailPlaceholder(
height: height,
width: width,
);
},
octoSet: blurHashOrPlaceholder(_decodedBlurHash),
image: ImmichThumbnail.imageProvider(
asset: asset,
asset: widget.asset,
),
width: width,
height: height,
fit: fit,
errorBuilder: (context, error, stackTrace) {
if (error is PlatformException &&
error.code == "The asset not found!") {
debugPrint(
"Asset ${asset?.localId} does not exist anymore on device!",
);
} else {
debugPrint(
"Error getting thumb for assetId=${asset?.localId}: $error",
);
}
return Icon(
Icons.image_not_supported_outlined,
color: context.primaryColor,
);
},
width: widget.width,
height: widget.height,
fit: widget.fit,
);
}
}
@@ -0,0 +1,48 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/shared/ui/fade_in_placeholder_image.dart';
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,
}) {
return OctoSet(
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
errorBuilder: blurHashErrorBuilder(blurhash, fit: fit),
);
}
OctoPlaceholderBuilder blurHashPlaceholderBuilder(
Uint8List? blurhash, {
BoxFit? fit,
}) {
return (context) => blurhash == null
? const ThumbnailPlaceholder()
: FadeInPlaceholderImage(
placeholder: const ThumbnailPlaceholder(),
image: MemoryImage(blurhash),
fit: fit ?? BoxFit.cover,
);
}
OctoErrorBuilder blurHashErrorBuilder(
Uint8List? blurhash, {
BoxFit? fit,
Text? message,
IconData? icon,
Color? iconColor,
double? iconSize,
}) {
return OctoError.placeholderWithErrorIcon(
blurHashPlaceholderBuilder(blurhash, fit: fit),
message: message,
icon: icon,
iconColor: iconColor,
iconSize: iconSize,
);
}