mirror of
https://github.com/immich-app/immich.git
synced 2025-07-31 15:08:44 -04:00
refactor
This commit is contained in:
parent
2a1e914245
commit
bd199f8985
@ -85,3 +85,13 @@ extension DateRangeFormatting on DateTime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension IsSameExtension on DateTime {
|
||||||
|
bool isSameDay(DateTime other) {
|
||||||
|
return day == other.day && month == other.month && year == other.year;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSameMonth(DateTime other) {
|
||||||
|
return month == other.month && year == other.year;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.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/image_provider.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
import 'package:octo_image/octo_image.dart';
|
import 'package:octo_image/octo_image.dart';
|
||||||
|
|
||||||
class FullImage extends StatelessWidget {
|
class FullImage extends StatelessWidget {
|
||||||
@ -9,7 +9,7 @@ class FullImage extends StatelessWidget {
|
|||||||
this.asset, {
|
this.asset, {
|
||||||
required this.size,
|
required this.size,
|
||||||
this.fit = BoxFit.cover,
|
this.fit = BoxFit.cover,
|
||||||
this.placeholder = const ThumbnailPlaceholder(),
|
this.placeholder = const Thumbhash(),
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail_tile.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail_tile.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart';
|
||||||
@ -75,6 +76,21 @@ class FixedSegment extends Segment {
|
|||||||
spacing: spacing,
|
spacing: spacing,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FixedSegment.empty()
|
||||||
|
: this(
|
||||||
|
firstIndex: 0,
|
||||||
|
lastIndex: 0,
|
||||||
|
startOffset: 0,
|
||||||
|
endOffset: 0,
|
||||||
|
firstAssetIndex: 0,
|
||||||
|
bucket: const Bucket(assetCount: 0),
|
||||||
|
tileHeight: 1,
|
||||||
|
columnCount: 0,
|
||||||
|
headerExtent: 0,
|
||||||
|
spacing: 0,
|
||||||
|
header: HeaderType.none,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FixedSegmentRow extends ConsumerWidget {
|
class _FixedSegmentRow extends ConsumerWidget {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||||
|
import 'package:immich_mobile/extensions/datetime_extensions.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment.model.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment.model.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart';
|
||||||
@ -6,6 +7,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart
|
|||||||
class FixedSegmentBuilder extends SegmentBuilder {
|
class FixedSegmentBuilder extends SegmentBuilder {
|
||||||
final double tileHeight;
|
final double tileHeight;
|
||||||
final int columnCount;
|
final int columnCount;
|
||||||
|
static final DateTime _dummyDate = DateTime.fromMicrosecondsSinceEpoch(0);
|
||||||
|
|
||||||
const FixedSegmentBuilder({
|
const FixedSegmentBuilder({
|
||||||
required super.buckets,
|
required super.buckets,
|
||||||
@ -16,12 +18,11 @@ class FixedSegmentBuilder extends SegmentBuilder {
|
|||||||
});
|
});
|
||||||
|
|
||||||
List<Segment> generate() {
|
List<Segment> generate() {
|
||||||
final segments = <Segment>[];
|
final segments = List.filled(buckets.length, const FixedSegment.empty());
|
||||||
int firstIndex = 0;
|
int firstIndex = 0;
|
||||||
double startOffset = 0;
|
double startOffset = 0;
|
||||||
int assetIndex = 0;
|
int assetIndex = 0;
|
||||||
DateTime? previousDate;
|
DateTime previousDate = _dummyDate;
|
||||||
|
|
||||||
for (int i = 0; i < buckets.length; i++) {
|
for (int i = 0; i < buckets.length; i++) {
|
||||||
final bucket = buckets[i];
|
final bucket = buckets[i];
|
||||||
|
|
||||||
@ -32,11 +33,10 @@ class FixedSegmentBuilder extends SegmentBuilder {
|
|||||||
final segmentFirstIndex = firstIndex;
|
final segmentFirstIndex = firstIndex;
|
||||||
firstIndex += segmentCount;
|
firstIndex += segmentCount;
|
||||||
final segmentLastIndex = firstIndex - 1;
|
final segmentLastIndex = firstIndex - 1;
|
||||||
|
|
||||||
final timelineHeader = switch (groupBy) {
|
final timelineHeader = switch (groupBy) {
|
||||||
GroupAssetsBy.month => HeaderType.month,
|
GroupAssetsBy.month => HeaderType.month,
|
||||||
GroupAssetsBy.day || GroupAssetsBy.auto =>
|
GroupAssetsBy.day || GroupAssetsBy.auto =>
|
||||||
bucket is TimeBucket && bucket.date.month != previousDate?.month ? HeaderType.monthAndDay : HeaderType.day,
|
bucket is TimeBucket && !previousDate.isSameMonth(bucket.date) ? HeaderType.monthAndDay : HeaderType.day,
|
||||||
GroupAssetsBy.none => HeaderType.none,
|
GroupAssetsBy.none => HeaderType.none,
|
||||||
};
|
};
|
||||||
final headerExtent = SegmentBuilder.headerExtent(timelineHeader);
|
final headerExtent = SegmentBuilder.headerExtent(timelineHeader);
|
||||||
@ -45,8 +45,7 @@ class FixedSegmentBuilder extends SegmentBuilder {
|
|||||||
startOffset += headerExtent + (tileHeight * numberOfRows) + spacing * (numberOfRows - 1);
|
startOffset += headerExtent + (tileHeight * numberOfRows) + spacing * (numberOfRows - 1);
|
||||||
final segmentEndOffset = startOffset;
|
final segmentEndOffset = startOffset;
|
||||||
|
|
||||||
segments.add(
|
segments[i] = FixedSegment(
|
||||||
FixedSegment(
|
|
||||||
firstIndex: segmentFirstIndex,
|
firstIndex: segmentFirstIndex,
|
||||||
lastIndex: segmentLastIndex,
|
lastIndex: segmentLastIndex,
|
||||||
startOffset: segmentStartOffset,
|
startOffset: segmentStartOffset,
|
||||||
@ -58,7 +57,6 @@ class FixedSegmentBuilder extends SegmentBuilder {
|
|||||||
headerExtent: headerExtent,
|
headerExtent: headerExtent,
|
||||||
spacing: spacing,
|
spacing: spacing,
|
||||||
header: timelineHeader,
|
header: timelineHeader,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assetIndex += assetCount;
|
assetIndex += assetCount;
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
|
|
||||||
abstract class SegmentBuilder {
|
abstract class SegmentBuilder {
|
||||||
final List<Bucket> buckets;
|
final List<Bucket> buckets;
|
||||||
@ -23,12 +23,13 @@ abstract class SegmentBuilder {
|
|||||||
int count, {
|
int count, {
|
||||||
Size size = const Size.square(kTimelineFixedTileExtent),
|
Size size = const Size.square(kTimelineFixedTileExtent),
|
||||||
double spacing = kTimelineSpacing,
|
double spacing = kTimelineSpacing,
|
||||||
}) => RepaintBoundary(
|
}) =>
|
||||||
|
RepaintBoundary(
|
||||||
child: FixedTimelineRow(
|
child: FixedTimelineRow(
|
||||||
dimension: size.height,
|
dimension: size.height,
|
||||||
spacing: spacing,
|
spacing: spacing,
|
||||||
textDirection: Directionality.of(context),
|
textDirection: Directionality.of(context),
|
||||||
children: List.generate(count, (_) => ThumbnailPlaceholder(width: size.width, height: size.height)),
|
children: List.filled(count, const Thumbhash()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/image/immich_local_image_provider.dart';
|
import 'package:immich_mobile/providers/image/immich_local_image_provider.dart';
|
||||||
import 'package:immich_mobile/providers/image/immich_remote_image_provider.dart';
|
import 'package:immich_mobile/providers/image/immich_remote_image_provider.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
import 'package:octo_image/octo_image.dart';
|
import 'package:octo_image/octo_image.dart';
|
||||||
|
|
||||||
class ImmichImage extends StatelessWidget {
|
class ImmichImage extends StatelessWidget {
|
||||||
@ -14,7 +14,7 @@ class ImmichImage extends StatelessWidget {
|
|||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
this.fit = BoxFit.cover,
|
this.fit = BoxFit.cover,
|
||||||
this.placeholder = const ThumbnailPlaceholder(),
|
this.placeholder = const Thumbhash(),
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,107 +4,18 @@ import 'dart:ui' as ui;
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:thumbhash/thumbhash.dart' as thumbhash;
|
import 'package:thumbhash/thumbhash.dart' as thumbhash;
|
||||||
|
|
||||||
class ThumbhashImage extends RenderBox {
|
|
||||||
Color _placeholderColor;
|
|
||||||
ui.Image? _image;
|
|
||||||
BoxFit _fit;
|
|
||||||
|
|
||||||
ThumbhashImage({
|
|
||||||
required ui.Image? image,
|
|
||||||
required BoxFit fit,
|
|
||||||
required Color placeholderColor,
|
|
||||||
}) : _image = image,
|
|
||||||
_fit = fit,
|
|
||||||
_placeholderColor = placeholderColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(PaintingContext context, Offset offset) {
|
|
||||||
final image = _image;
|
|
||||||
final rect = offset & size;
|
|
||||||
if (image == null) {
|
|
||||||
final paint = Paint();
|
|
||||||
paint.color = _placeholderColor;
|
|
||||||
context.canvas.drawRect(rect, paint);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
paintImage(
|
|
||||||
canvas: context.canvas,
|
|
||||||
rect: rect,
|
|
||||||
image: image,
|
|
||||||
fit: _fit,
|
|
||||||
filterQuality: FilterQuality.low,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void performLayout() {
|
|
||||||
size = constraints.biggest;
|
|
||||||
}
|
|
||||||
|
|
||||||
set image(ui.Image? value) {
|
|
||||||
if (_image != value) {
|
|
||||||
_image = value;
|
|
||||||
markNeedsPaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set fit(BoxFit value) {
|
|
||||||
if (_fit != value) {
|
|
||||||
_fit = value;
|
|
||||||
markNeedsPaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set placeholderColor(Color value) {
|
|
||||||
if (_placeholderColor != value) {
|
|
||||||
_placeholderColor = value;
|
|
||||||
markNeedsPaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ThumbhashLeaf extends LeafRenderObjectWidget {
|
|
||||||
final ui.Image? image;
|
|
||||||
final BoxFit fit;
|
|
||||||
final Color placeholderColor;
|
|
||||||
|
|
||||||
const ThumbhashLeaf({
|
|
||||||
super.key,
|
|
||||||
required this.image,
|
|
||||||
required this.fit,
|
|
||||||
required this.placeholderColor,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
RenderObject createRenderObject(BuildContext context) {
|
|
||||||
return ThumbhashImage(
|
|
||||||
image: image,
|
|
||||||
fit: fit,
|
|
||||||
placeholderColor: placeholderColor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void updateRenderObject(BuildContext context, ThumbhashImage renderObject) {
|
|
||||||
renderObject.fit = fit;
|
|
||||||
renderObject.image = image;
|
|
||||||
renderObject.placeholderColor = placeholderColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Thumbhash extends StatefulWidget {
|
class Thumbhash extends StatefulWidget {
|
||||||
final String? blurhash;
|
final String? blurhash;
|
||||||
final BoxFit fit;
|
final BoxFit fit;
|
||||||
final Color placeholderColor;
|
|
||||||
|
|
||||||
const Thumbhash({
|
const Thumbhash({
|
||||||
required this.blurhash,
|
this.blurhash,
|
||||||
this.fit = BoxFit.cover,
|
this.fit = BoxFit.cover,
|
||||||
this.placeholderColor = const Color.fromRGBO(0, 0, 0, 0.2),
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -112,18 +23,19 @@ class Thumbhash extends StatefulWidget {
|
|||||||
State<Thumbhash> createState() => _ThumbhashState();
|
State<Thumbhash> createState() => _ThumbhashState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _ThumbhashState extends State<Thumbhash> {
|
class _ThumbhashState extends State<Thumbhash> {
|
||||||
String? blurhash;
|
String? blurhash;
|
||||||
BoxFit? fit;
|
BoxFit? fit;
|
||||||
ui.Image? _image;
|
ui.Image? _image;
|
||||||
Color? placeholderColor;
|
|
||||||
|
static final _gradientCache = <ColorScheme, Gradient>{};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final blurhash_ = blurhash = widget.blurhash;
|
final blurhash_ = blurhash = widget.blurhash;
|
||||||
fit = widget.fit;
|
fit = widget.fit;
|
||||||
placeholderColor = widget.placeholderColor;
|
|
||||||
if (blurhash_ == null) {
|
if (blurhash_ == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -179,10 +91,20 @@ class _ThumbhashState extends State<Thumbhash> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ThumbhashLeaf(
|
final colorScheme = context.colorScheme;
|
||||||
|
final gradient = _gradientCache[colorScheme] ??= LinearGradient(
|
||||||
|
colors: [
|
||||||
|
colorScheme.surfaceContainer,
|
||||||
|
colorScheme.surfaceContainer.darken(amount: .1),
|
||||||
|
],
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
);
|
||||||
|
|
||||||
|
return _ThumbhashLeaf(
|
||||||
image: _image,
|
image: _image,
|
||||||
fit: fit!,
|
fit: fit!,
|
||||||
placeholderColor: placeholderColor!,
|
placeholderGradient: gradient,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,3 +114,94 @@ class _ThumbhashState extends State<Thumbhash> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ThumbhashLeaf extends LeafRenderObjectWidget {
|
||||||
|
final ui.Image? image;
|
||||||
|
final BoxFit fit;
|
||||||
|
final Gradient placeholderGradient;
|
||||||
|
|
||||||
|
const _ThumbhashLeaf({
|
||||||
|
required this.image,
|
||||||
|
required this.fit,
|
||||||
|
required this.placeholderGradient,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
|
return _ThumbhashRenderBox(
|
||||||
|
image: image,
|
||||||
|
fit: fit,
|
||||||
|
placeholderGradient: placeholderGradient,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateRenderObject(
|
||||||
|
BuildContext context,
|
||||||
|
_ThumbhashRenderBox renderObject,
|
||||||
|
) {
|
||||||
|
renderObject.fit = fit;
|
||||||
|
renderObject.image = image;
|
||||||
|
renderObject.placeholderGradient = placeholderGradient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ThumbhashRenderBox extends RenderBox {
|
||||||
|
ui.Image? _image;
|
||||||
|
BoxFit _fit;
|
||||||
|
Gradient _placeholderGradient;
|
||||||
|
|
||||||
|
_ThumbhashRenderBox({
|
||||||
|
required ui.Image? image,
|
||||||
|
required BoxFit fit,
|
||||||
|
required Gradient placeholderGradient,
|
||||||
|
}) : _image = image,
|
||||||
|
_fit = fit,
|
||||||
|
_placeholderGradient = placeholderGradient;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) {
|
||||||
|
final image = _image;
|
||||||
|
final rect = offset & size;
|
||||||
|
if (image == null) {
|
||||||
|
final paint = Paint();
|
||||||
|
paint.shader = _placeholderGradient.createShader(rect);
|
||||||
|
context.canvas.drawRect(rect, paint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paintImage(
|
||||||
|
canvas: context.canvas,
|
||||||
|
rect: rect,
|
||||||
|
image: image,
|
||||||
|
fit: _fit,
|
||||||
|
filterQuality: FilterQuality.low,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout() {
|
||||||
|
size = constraints.biggest;
|
||||||
|
}
|
||||||
|
|
||||||
|
set image(ui.Image? value) {
|
||||||
|
if (_image != value) {
|
||||||
|
_image = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set fit(BoxFit value) {
|
||||||
|
if (_fit != value) {
|
||||||
|
_fit = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set placeholderGradient(Gradient value) {
|
||||||
|
if (_placeholderGradient != value) {
|
||||||
|
_placeholderGradient = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,15 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
import 'package:octo_image/octo_image.dart';
|
import 'package:octo_image/octo_image.dart';
|
||||||
|
|
||||||
/// Simple set to show [OctoPlaceholder.circularProgressIndicator] as
|
|
||||||
/// placeholder and [OctoError.icon] as error.
|
|
||||||
OctoSet blurHashOrPlaceholder(String? blurhash, {BoxFit fit = BoxFit.cover, Text? errorMessage}) {
|
|
||||||
return OctoSet(
|
|
||||||
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
|
|
||||||
errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
OctoPlaceholderBuilder blurHashPlaceholderBuilder(String? blurhash, {required BoxFit fit}) {
|
OctoPlaceholderBuilder blurHashPlaceholderBuilder(String? blurhash, {required BoxFit fit}) {
|
||||||
return (context) => Thumbhash(blurhash: blurhash, fit: fit);
|
return (context) => Thumbhash(blurhash: blurhash, fit: fit);
|
||||||
}
|
}
|
||||||
|
@ -90,12 +90,7 @@ class _BlurredBackdrop extends HookWidget {
|
|||||||
final blurhash = asset.thumbhash;
|
final blurhash = asset.thumbhash;
|
||||||
if (blurhash != null) {
|
if (blurhash != null) {
|
||||||
// Use a nice cheap blur hash image decoration
|
// Use a nice cheap blur hash image decoration
|
||||||
return Stack(
|
return Thumbhash(blurhash: blurhash, fit: BoxFit.cover);
|
||||||
children: [
|
|
||||||
const ColoredBox(color: Color.fromRGBO(0, 0, 0, 0.2)),
|
|
||||||
Thumbhash(blurhash: blurhash, fit: BoxFit.cover),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to using a more expensive image filtered
|
// Fall back to using a more expensive image filtered
|
||||||
|
Loading…
x
Reference in New Issue
Block a user