mirror of
https://github.com/immich-app/immich.git
synced 2025-02-04 01:09:14 -05:00
blurhash fade to thumb
This commit is contained in:
parent
ef2f605635
commit
45d4984fde
4 changed files with 109 additions and 63 deletions
60
mobile/lib/modules/home/ui/asset_grid/blurhash_thumb.dart
Normal file
60
mobile/lib/modules/home/ui/asset_grid/blurhash_thumb.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
|
import 'package:immich_mobile/shared/ui/hooks/blurhash_hook.dart';
|
||||||
|
|
||||||
|
class BlurhashThumb extends HookWidget {
|
||||||
|
final double height;
|
||||||
|
final double width;
|
||||||
|
final Asset asset;
|
||||||
|
final EdgeInsets margin;
|
||||||
|
|
||||||
|
const BlurhashThumb({
|
||||||
|
super.key,
|
||||||
|
required this.height,
|
||||||
|
required this.width,
|
||||||
|
required this.asset,
|
||||||
|
required this.margin,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final blurhash = useBlurHashRef(asset).value;
|
||||||
|
if (blurhash == null) {
|
||||||
|
return SizedBox(
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Padding(
|
||||||
|
padding: margin,
|
||||||
|
child: Image.memory(
|
||||||
|
blurhash,
|
||||||
|
gaplessPlayback: true,
|
||||||
|
frameBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Widget child,
|
||||||
|
int? frame,
|
||||||
|
bool wasSynchronouslyLoaded,
|
||||||
|
) {
|
||||||
|
if (wasSynchronouslyLoaded) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
child: frame != null
|
||||||
|
? child
|
||||||
|
: SizedBox(
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
@ -6,12 +7,15 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/collection_extensions.dart';
|
import 'package:immich_mobile/extensions/collection_extensions.dart';
|
||||||
import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart';
|
import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/ui/asset_grid/blurhash_thumb.dart';
|
||||||
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
|
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
|
||||||
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.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/models/asset.dart';
|
||||||
|
import 'package:immich_mobile/shared/ui/hooks/blurhash_hook.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
|
||||||
import 'asset_grid_data_structure.dart';
|
import 'asset_grid_data_structure.dart';
|
||||||
|
@ -325,32 +329,33 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single row of all placeholder widgets
|
/// A single row of all placeholder widgets
|
||||||
class _PlaceholderRow extends StatelessWidget {
|
class _PlaceholderRow extends HookWidget {
|
||||||
final int number;
|
|
||||||
final double width;
|
final double width;
|
||||||
final double height;
|
final double height;
|
||||||
final double margin;
|
final double margin;
|
||||||
|
final List<Asset> assets;
|
||||||
|
|
||||||
const _PlaceholderRow({
|
const _PlaceholderRow({
|
||||||
super.key,
|
super.key,
|
||||||
required this.number,
|
|
||||||
required this.width,
|
required this.width,
|
||||||
required this.height,
|
required this.height,
|
||||||
required this.margin,
|
required this.margin,
|
||||||
|
required this.assets,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
for (int i = 0; i < number; i++)
|
for (int i = 0; i < assets.length; i++)
|
||||||
ThumbnailPlaceholder(
|
BlurhashThumb(
|
||||||
key: ValueKey(i),
|
key: ValueKey(i),
|
||||||
|
asset: assets[i],
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
bottom: margin,
|
bottom: margin,
|
||||||
right: i + 1 == number ? 0.0 : margin,
|
right: i + 1 == assets.length ? 0.0 : margin,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -401,9 +406,9 @@ class _Section extends StatelessWidget {
|
||||||
final width = constraints.maxWidth / assetsPerRow -
|
final width = constraints.maxWidth / assetsPerRow -
|
||||||
margin * (assetsPerRow - 1) / assetsPerRow;
|
margin * (assetsPerRow - 1) / assetsPerRow;
|
||||||
final rows = (section.count + assetsPerRow - 1) ~/ assetsPerRow;
|
final rows = (section.count + assetsPerRow - 1) ~/ assetsPerRow;
|
||||||
final List<Asset> assetsToRender = scrolling
|
final List<Asset> assetsToRender = //scrolling
|
||||||
? []
|
//? []
|
||||||
: renderList.loadAssets(section.offset, section.count);
|
renderList.loadAssets(section.offset, section.count);
|
||||||
return Column(
|
return Column(
|
||||||
key: ValueKey(section.offset),
|
key: ValueKey(section.offset),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -422,18 +427,22 @@ class _Section extends StatelessWidget {
|
||||||
selectAssets: selectAssets,
|
selectAssets: selectAssets,
|
||||||
deselectAssets: deselectAssets,
|
deselectAssets: deselectAssets,
|
||||||
),
|
),
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
for (int i = 0; i < rows; i++)
|
for (int i = 0; i < rows; i++)
|
||||||
scrolling
|
_PlaceholderRow(
|
||||||
? _PlaceholderRow(
|
key: ValueKey('placeholder-$i'),
|
||||||
key: ValueKey(i),
|
assets: assetsToRender.nestedSlice(
|
||||||
number: i + 1 == rows
|
i * assetsPerRow,
|
||||||
? section.count - i * assetsPerRow
|
min((i + 1) * assetsPerRow, section.count),
|
||||||
: assetsPerRow,
|
),
|
||||||
width: width,
|
width: width,
|
||||||
height: width,
|
height: width,
|
||||||
margin: margin,
|
margin: margin,
|
||||||
)
|
),
|
||||||
: _AssetRow(
|
if (!scrolling)
|
||||||
|
for (int i = 0; i < rows; i++)
|
||||||
|
_AssetRow(
|
||||||
key: ValueKey(i),
|
key: ValueKey(i),
|
||||||
assets: assetsToRender.nestedSlice(
|
assets: assetsToRender.nestedSlice(
|
||||||
i * assetsPerRow,
|
i * assetsPerRow,
|
||||||
|
@ -455,6 +464,8 @@ class _Section extends StatelessWidget {
|
||||||
onDeselect: (asset) => deselectAssets([asset]),
|
onDeselect: (asset) => deselectAssets([asset]),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,8 +23,7 @@ OctoPlaceholderBuilder blurHashPlaceholderBuilder(
|
||||||
}) {
|
}) {
|
||||||
return (context) => blurhash == null
|
return (context) => blurhash == null
|
||||||
? const ThumbnailPlaceholder()
|
? const ThumbnailPlaceholder()
|
||||||
: FadeInPlaceholderImage(
|
: Image(
|
||||||
placeholder: const ThumbnailPlaceholder(),
|
|
||||||
image: MemoryImage(blurhash),
|
image: MemoryImage(blurhash),
|
||||||
fit: fit ?? BoxFit.cover,
|
fit: fit ?? BoxFit.cover,
|
||||||
);
|
);
|
||||||
|
|
|
@ -413,10 +413,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file
|
name: file
|
||||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "6.1.4"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -860,30 +860,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.1"
|
version: "4.8.1"
|
||||||
leak_tracker:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: leak_tracker
|
|
||||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "10.0.0"
|
|
||||||
leak_tracker_flutter_testing:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: leak_tracker_flutter_testing
|
|
||||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
leak_tracker_testing:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: leak_tracker_testing
|
|
||||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -931,18 +907,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.16+1"
|
version: "0.12.16"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.0"
|
version: "0.5.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
|
@ -1026,10 +1002,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.8.3"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1162,10 +1138,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "3.1.2"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1194,10 +1170,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: process
|
name: process
|
||||||
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
|
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.2"
|
version: "4.2.4"
|
||||||
provider:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1663,10 +1639,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "13.0.0"
|
version: "11.10.0"
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1711,10 +1687,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webdriver
|
name: webdriver
|
||||||
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
|
sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.2"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
Loading…
Add table
Reference in a new issue