mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
better scrolling
This commit is contained in:
parent
c988342de1
commit
3100702e93
8 changed files with 233 additions and 88 deletions
|
|
@ -2,13 +2,14 @@ import 'package:flutter/material.dart';
|
|||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
||||
import 'package:immich_mobile/widgets/common/fade_in_placeholder_image.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:octo_image/octo_image.dart';
|
||||
|
||||
class Thumbnail extends StatelessWidget {
|
||||
const Thumbnail({this.asset, this.remoteId, this.size = const Size.square(256), this.fit = BoxFit.cover, super.key})
|
||||
class Thumbnail extends StatefulWidget {
|
||||
const Thumbnail({this.asset, this.remoteId, this.size = kTimelineFixedTileExtent, this.fit = BoxFit.cover, super.key})
|
||||
: assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided');
|
||||
|
||||
final BaseAsset? asset;
|
||||
|
|
@ -16,46 +17,79 @@ class Thumbnail extends StatelessWidget {
|
|||
final Size size;
|
||||
final BoxFit fit;
|
||||
|
||||
@override
|
||||
createState() => _ThumbnailState();
|
||||
}
|
||||
|
||||
class _ThumbnailState extends State<Thumbnail> {
|
||||
ImageProvider? provider;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
provider = getThumbnailImageProvider(asset: widget.asset, remoteId: widget.remoteId);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant Thumbnail oldWidget) {
|
||||
if (oldWidget.asset == widget.asset && oldWidget.remoteId == widget.remoteId) {
|
||||
return;
|
||||
}
|
||||
if (provider is CancellableImageProvider) {
|
||||
(provider as CancellableImageProvider).cancel();
|
||||
}
|
||||
provider = getThumbnailImageProvider(asset: widget.asset, remoteId: widget.remoteId);
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final thumbHash = asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null;
|
||||
final provider = getThumbnailImageProvider(asset: asset, remoteId: remoteId);
|
||||
final thumbHash = widget.asset is RemoteAsset ? (widget.asset as RemoteAsset).thumbHash : null;
|
||||
|
||||
return OctoImage.fromSet(
|
||||
image: provider,
|
||||
image: provider!,
|
||||
octoSet: OctoSet(
|
||||
placeholderBuilder: _blurHashPlaceholderBuilder(thumbHash, fit: fit),
|
||||
errorBuilder: _blurHashErrorBuilder(thumbHash, provider: provider, fit: fit, asset: asset),
|
||||
placeholderBuilder: _blurHashPlaceholderBuilder(thumbHash),
|
||||
errorBuilder: _blurHashErrorBuilder(thumbHash, provider: provider, asset: widget.asset),
|
||||
),
|
||||
fadeOutDuration: const Duration(milliseconds: 100),
|
||||
fadeInDuration: Duration.zero,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
fit: fit,
|
||||
width: widget.size.width,
|
||||
height: widget.size.height,
|
||||
fit: widget.fit,
|
||||
placeholderFadeInDuration: Duration.zero,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
OctoPlaceholderBuilder _blurHashPlaceholderBuilder(String? thumbHash, {BoxFit? fit}) {
|
||||
return (context) => thumbHash == null
|
||||
? const ThumbnailPlaceholder()
|
||||
: FadeInPlaceholderImage(
|
||||
placeholder: const ThumbnailPlaceholder(),
|
||||
image: ThumbHashProvider(thumbHash: thumbHash),
|
||||
fit: fit ?? BoxFit.cover,
|
||||
@override
|
||||
void dispose() {
|
||||
if (provider is CancellableImageProvider) {
|
||||
(provider as CancellableImageProvider).cancel();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
OctoPlaceholderBuilder _blurHashPlaceholderBuilder(String? thumbHash) {
|
||||
return (context) => thumbHash == null
|
||||
? const ThumbnailPlaceholder()
|
||||
: FadeInPlaceholderImage(
|
||||
placeholder: const ThumbnailPlaceholder(),
|
||||
image: ThumbHashProvider(thumbHash: thumbHash),
|
||||
fit: widget.fit,
|
||||
width: widget.size.width,
|
||||
height: widget.size.height,
|
||||
);
|
||||
}
|
||||
|
||||
OctoErrorBuilder _blurHashErrorBuilder(String? blurhash, {BaseAsset? asset, ImageProvider? provider}) =>
|
||||
(context, e, s) {
|
||||
Logger("ImThumbnail").warning("Error loading thumbnail for ${asset?.name}", e, s);
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
_blurHashPlaceholderBuilder(blurhash)(context),
|
||||
const Opacity(opacity: 0.75, child: Icon(Icons.error_outline_rounded)),
|
||||
],
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
OctoErrorBuilder _blurHashErrorBuilder(String? blurhash, {BaseAsset? asset, ImageProvider? provider, BoxFit? fit}) =>
|
||||
(context, e, s) {
|
||||
Logger("ImThumbnail").warning("Error loading thumbnail for ${asset?.name}", e, s);
|
||||
provider?.evict();
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
_blurHashPlaceholderBuilder(blurhash, fit: fit)(context),
|
||||
const Opacity(opacity: 0.75, child: Icon(Icons.error_outline_rounded)),
|
||||
],
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import 'package:immich_mobile/domain/models/timeline.model.dart';
|
|||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/scroll_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
||||
|
|
@ -106,7 +107,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
bool _dragging = false;
|
||||
TimelineAssetIndex? _dragAnchorIndex;
|
||||
final Set<BaseAsset> _draggedAssets = HashSet();
|
||||
ScrollPhysics? _scrollPhysics;
|
||||
ScrollPhysics _scrollPhysics = const ScrollUnawareScrollPhysics();
|
||||
|
||||
int _perRow = 4;
|
||||
double _scaleFactor = 3.0;
|
||||
|
|
@ -188,7 +189,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
// Drag selection methods
|
||||
void _setDragStartIndex(TimelineAssetIndex index) {
|
||||
setState(() {
|
||||
_scrollPhysics = const ClampingScrollPhysics();
|
||||
_scrollPhysics = const ScrollUnawareClampingScrollPhysics();
|
||||
_dragAnchorIndex = index;
|
||||
_dragging = true;
|
||||
});
|
||||
|
|
@ -198,7 +199,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// Update the physics post frame to prevent sudden change in physics on iOS.
|
||||
setState(() {
|
||||
_scrollPhysics = null;
|
||||
_scrollPhysics = const ScrollUnawareScrollPhysics();
|
||||
});
|
||||
});
|
||||
setState(() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue