chore: bump line length to 120 (#20191)

This commit is contained in:
shenlong 2025-07-25 08:07:22 +05:30 committed by GitHub
parent 977c9b96ba
commit ad65e9011a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
517 changed files with 4520 additions and 9514 deletions

View file

@ -35,9 +35,7 @@ class ArchiveActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -25,12 +25,10 @@ class BaseActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final miniWidth =
minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0);
final miniWidth = minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0);
final iconTheme = IconTheme.of(context);
final iconSize = iconTheme.size ?? 24.0;
final iconColor =
this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color;
final iconColor = this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color;
final textColor = context.themeData.textTheme.labelLarge?.color;
if (menuItem) {

View file

@ -17,8 +17,7 @@ class CastActionButton extends ConsumerWidget {
return BaseActionButton(
iconData: isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded,
iconColor:
isCasting ? context.primaryColor : null, // null = default color
iconColor: isCasting ? context.primaryColor : null, // null = default color
label: "cast".t(context: context),
onPressed: () {
showDialog(

View file

@ -35,9 +35,7 @@ class DeleteLocalActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -20,8 +20,7 @@ class DeletePermanentActionButton extends ConsumerWidget {
return;
}
final result =
await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
ref.read(multiSelectProvider.notifier).reset();
if (source == ActionSource.viewer) {
@ -36,9 +35,7 @@ class DeletePermanentActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -17,8 +17,7 @@ class DeleteTrashActionButton extends ConsumerWidget {
return;
}
final result =
await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source);
ref.read(multiSelectProvider.notifier).reset();
final successMessage = 'assets_permanently_deleted_count'.t(
@ -29,9 +28,7 @@ class DeleteTrashActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -35,8 +35,7 @@ class DownloadActionButton extends ConsumerWidget {
} else if (result.count > 0) {
ImmichToast.show(
context: context,
msg: 'download_action_prompt'
.t(context: context, args: {'count': result.count.toString()}),
msg: 'download_action_prompt'.t(context: context, args: {'count': result.count.toString()}),
gravity: ToastGravity.BOTTOM,
toastType: ToastType.success,
);

View file

@ -18,8 +18,7 @@ class EditLocationActionButton extends ConsumerWidget {
return;
}
final result =
await ref.read(actionProvider.notifier).editLocation(source, context);
final result = await ref.read(actionProvider.notifier).editLocation(source, context);
if (result == null) {
return;
}
@ -34,9 +33,7 @@ class EditLocationActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -39,9 +39,7 @@ class FavoriteActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -14,9 +14,7 @@ class MotionPhotoActionButton extends ConsumerWidget {
final isPlaying = ref.watch(isPlayingMotionVideoProvider);
return BaseActionButton(
iconData: isPlaying
? Icons.motion_photos_pause_outlined
: Icons.play_circle_outline_rounded,
iconData: isPlaying ? Icons.motion_photos_pause_outlined : Icons.play_circle_outline_rounded,
label: "play_motion_photo".t(context: context),
onPressed: ref.read(isPlayingMotionVideoProvider.notifier).toggle,
menuItem: menuItem,

View file

@ -20,8 +20,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget {
return;
}
final result =
await ref.read(actionProvider.notifier).moveToLockFolder(source);
final result = await ref.read(actionProvider.notifier).moveToLockFolder(source);
ref.read(multiSelectProvider.notifier).reset();
if (source == ActionSource.viewer) {
@ -36,9 +35,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -23,9 +23,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget {
return;
}
final result = await ref
.read(actionProvider.notifier)
.removeFromAlbum(source, albumId);
final result = await ref.read(actionProvider.notifier).removeFromAlbum(source, albumId);
ref.read(multiSelectProvider.notifier).reset();
final successMessage = 'remove_from_album_action_prompt'.t(
@ -36,9 +34,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -18,8 +18,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget {
return;
}
final result =
await ref.read(actionProvider.notifier).removeFromLockFolder(source);
final result = await ref.read(actionProvider.notifier).removeFromLockFolder(source);
ref.read(multiSelectProvider.notifier).reset();
final successMessage = 'remove_from_lock_folder_action_prompt'.t(
@ -30,9 +29,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -28,9 +28,7 @@ class RestoreTrashActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -37,8 +37,7 @@ class ShareActionButton extends ConsumerWidget {
} else if (result.count > 0) {
ImmichToast.show(
context: context,
msg: 'share_action_prompt'
.t(context: context, args: {'count': result.count.toString()}),
msg: 'share_action_prompt'.t(context: context, args: {'count': result.count.toString()}),
gravity: ToastGravity.BOTTOM,
toastType: ToastType.success,
);
@ -48,8 +47,7 @@ class ShareActionButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return BaseActionButton(
iconData:
Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded,
iconData: Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded,
label: 'share'.t(context: context),
onPressed: () => _onTap(context, ref),
);

View file

@ -24,8 +24,7 @@ class StackActionButton extends ConsumerWidget {
throw Exception('User must be logged in to access stack action');
}
final result =
await ref.read(actionProvider.notifier).stack(user.id, source);
final result = await ref.read(actionProvider.notifier).stack(user.id, source);
ref.read(multiSelectProvider.notifier).reset();
final successMessage = 'stack_action_prompt'.t(
@ -36,9 +35,7 @@ class StackActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -35,9 +35,7 @@ class TrashActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -29,9 +29,7 @@ class UnArchiveActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -39,9 +39,7 @@ class UnFavoriteActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -29,9 +29,7 @@ class UnStackActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -28,9 +28,7 @@ class UploadActionButton extends ConsumerWidget {
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);

View file

@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
class StackChildrenNotifier
extends AutoDisposeFamilyAsyncNotifier<List<RemoteAsset>, BaseAsset?> {
class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier<List<RemoteAsset>, BaseAsset?> {
@override
Future<List<RemoteAsset>> build(BaseAsset? asset) async {
if (asset == null || asset is! RemoteAsset || asset.stackId == null) {
@ -14,7 +13,7 @@ class StackChildrenNotifier
}
}
final stackChildrenNotifier = AsyncNotifierProvider.autoDispose
.family<StackChildrenNotifier, List<RemoteAsset>, BaseAsset?>(
final stackChildrenNotifier =
AsyncNotifierProvider.autoDispose.family<StackChildrenNotifier, List<RemoteAsset>, BaseAsset?>(
StackChildrenNotifier.new,
);

View file

@ -14,8 +14,7 @@ class AssetStackRow extends ConsumerWidget {
int opacity = ref.watch(
assetViewerProvider.select((state) => state.backgroundOpacity),
);
final showControls =
ref.watch(assetViewerProvider.select((s) => s.showingControls));
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
if (!showControls) {
opacity = 0;
@ -68,8 +67,7 @@ class _StackList extends ConsumerWidget {
child: Container(
height: 60,
width: 60,
decoration: index ==
ref.watch(assetViewerProvider.select((s) => s.stackIndex))
decoration: index == ref.watch(assetViewerProvider.select((s) => s.stackIndex))
? const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(6)),

View file

@ -120,12 +120,10 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
super.dispose();
}
bool get showingBottomSheet =>
ref.read(assetViewerProvider.select((s) => s.showingBottomSheet));
bool get showingBottomSheet => ref.read(assetViewerProvider.select((s) => s.showingBottomSheet));
Color get backgroundColor {
final opacity =
ref.read(assetViewerProvider.select((s) => s.backgroundOpacity));
final opacity = ref.read(assetViewerProvider.select((s) => s.backgroundOpacity));
return Colors.black.withAlpha(opacity);
}
@ -139,9 +137,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
// This is used to calculate the scale of the asset when the bottom sheet is showing.
// It is a small increment to ensure that the asset is slightly zoomed in when the
// bottom sheet is showing, which emulates the zoom effect.
double get _getScaleForBottomSheet =>
(viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) +
0.01;
double get _getScaleForBottomSheet => (viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) + 0.01;
double _getVerticalOffsetForBottomSheet(double extent) =>
(context.height * extent) - (context.height * _kBottomSheetMinimumExtent);
@ -235,8 +231,8 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
void _onPageBuild(PhotoViewControllerBase controller) {
viewController ??= controller;
if (showingBottomSheet) {
final verticalOffset = (context.height * bottomSheetController.size) -
(context.height * _kBottomSheetMinimumExtent);
final verticalOffset =
(context.height * bottomSheetController.size) - (context.height * _kBottomSheetMinimumExtent);
controller.position = Offset(0, -verticalOffset);
}
}
@ -262,9 +258,8 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
viewController = controller;
dragDownPosition = details.localPosition;
initialPhotoViewState = controller.value;
final isZoomed =
scaleStateController.scaleState == PhotoViewScaleState.zoomedIn ||
scaleStateController.scaleState == PhotoViewScaleState.covering;
final isZoomed = scaleStateController.scaleState == PhotoViewScaleState.zoomedIn ||
scaleStateController.scaleState == PhotoViewScaleState.covering;
if (!showingBottomSheet && isZoomed) {
blockGestures = true;
}
@ -349,8 +344,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
updatedScale = initialPhotoViewState.scale! * (1.0 - scaleReduction);
}
final backgroundOpacity =
(255 * (1.0 - (scaleReduction / dragRatio))).round();
final backgroundOpacity = (255 * (1.0 - (scaleReduction / dragRatio))).round();
viewController?.updateMultiple(
position: initialPhotoViewState.position + delta,
@ -494,8 +488,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
}
void _snapBottomSheet() {
if (bottomSheetController.size > _kBottomSheetSnapExtent ||
bottomSheetController.size < 0.4) {
if (bottomSheetController.size > _kBottomSheetSnapExtent || bottomSheetController.size < 0.4) {
return;
}
isSnapping = true;
@ -514,8 +507,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
if (stackChildren != null && stackChildren.isNotEmpty) {
asset = stackChildren
.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
}
return Container(
width: double.infinity,
@ -547,8 +539,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
if (stackChildren != null && stackChildren.isNotEmpty) {
asset = stackChildren
.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
}
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
@ -564,8 +555,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
return PhotoViewGalleryPageOptions(
key: ValueKey(asset.heroTag),
imageProvider: getFullImageProvider(asset, size: size),
heroAttributes:
PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
filterQuality: FilterQuality.high,
tightMode: true,
initialScale: PhotoViewComputedScale.contained * 0.999,
@ -600,8 +590,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
onDragUpdate: _onDragUpdate,
onDragEnd: _onDragEnd,
onTapDown: _onTapDown,
heroAttributes:
PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'),
filterQuality: FilterQuality.high,
initialScale: PhotoViewComputedScale.contained * 0.99,
maxScale: 1.0,
@ -615,8 +604,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
asset: asset,
image: Image(
key: ValueKey(asset),
image:
getFullImageProvider(asset, size: Size(ctx.width, ctx.height)),
image: getFullImageProvider(asset, size: Size(ctx.width, ctx.height)),
fit: BoxFit.contain,
height: ctx.height,
width: ctx.width,
@ -641,8 +629,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
ref.watch(isPlayingMotionVideoProvider);
// Listen for casting changes and send initial asset to the cast provider
ref.listen(castProvider.select((value) => value.isCasting),
(_, isCasting) async {
ref.listen(castProvider.select((value) => value.isCasting), (_, isCasting) async {
if (!isCasting) return;
final asset = ref.read(currentAssetNotifier);

View file

@ -108,7 +108,6 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
}
}
final assetViewerProvider =
AutoDisposeNotifierProvider<AssetViewerStateNotifier, AssetViewerState>(
final assetViewerProvider = AutoDisposeNotifierProvider<AssetViewerStateNotifier, AssetViewerState>(
AssetViewerStateNotifier.new,
);

View file

@ -29,8 +29,7 @@ class ViewerBottomBar extends ConsumerWidget {
int opacity = ref.watch(
assetViewerProvider.select((state) => state.backgroundOpacity),
);
final showControls =
ref.watch(assetViewerProvider.select((s) => s.showingControls));
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
if (!showControls) {
opacity = 0;
@ -38,10 +37,8 @@ class ViewerBottomBar extends ConsumerWidget {
final actions = <Widget>[
const ShareActionButton(source: ActionSource.viewer),
if (asset.isLocalOnly)
const UploadActionButton(source: ActionSource.viewer),
if (asset.hasRemote && isOwner)
const ArchiveActionButton(source: ActionSource.viewer),
if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer),
if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer),
];
return IgnorePointer(
@ -55,11 +52,9 @@ class ViewerBottomBar extends ConsumerWidget {
? const SizedBox.shrink()
: Theme(
data: context.themeData.copyWith(
iconTheme:
const IconThemeData(size: 22, color: Colors.white),
iconTheme: const IconThemeData(size: 22, color: Colors.white),
textTheme: context.themeData.textTheme.copyWith(
labelLarge:
context.themeData.textTheme.labelLarge?.copyWith(
labelLarge: context.themeData.textTheme.labelLarge?.copyWith(
color: Colors.white,
),
),

View file

@ -52,8 +52,7 @@ class AssetDetailBottomSheet extends ConsumerWidget {
if (asset.hasRemote) ...[
const ShareLinkActionButton(source: ActionSource.viewer),
const ArchiveActionButton(source: ActionSource.viewer),
if (!asset.hasLocal)
const DownloadActionButton(source: ActionSource.viewer),
if (!asset.hasLocal) const DownloadActionButton(source: ActionSource.viewer),
isTrashEnable
? const TrashActionButton(source: ActionSource.viewer)
: const DeletePermanentActionButton(source: ActionSource.viewer),
@ -100,18 +99,14 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
String _getFileInfo(BaseAsset asset, ExifInfo? exifInfo) {
final height = asset.height ?? exifInfo?.height;
final width = asset.width ?? exifInfo?.width;
final resolution = (width != null && height != null)
? "${width.toInt()} x ${height.toInt()}"
: null;
final fileSize =
exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null;
final resolution = (width != null && height != null) ? "${width.toInt()} x ${height.toInt()}" : null;
final fileSize = exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null;
return switch ((fileSize, resolution)) {
(null, null) => '',
(String fileSize, null) => fileSize,
(null, String resolution) => resolution,
(String fileSize, String resolution) =>
'$fileSize$_kSeparator$resolution',
(String fileSize, String resolution) => '$fileSize$_kSeparator$resolution',
};
}
@ -133,17 +128,12 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
return null;
}
final fNumber =
exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null;
final exposureTime =
exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null;
final focalLength =
exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null;
final fNumber = exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null;
final exposureTime = exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null;
final focalLength = exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null;
final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null;
return [fNumber, exposureTime, focalLength, iso]
.where((spec) => spec != null && spec.isNotEmpty)
.join(_kSeparator);
return [fNumber, exposureTime, focalLength, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator);
}
@override

View file

@ -71,17 +71,13 @@ class _SheetLocationDetailsState extends ConsumerState<SheetLocationDetails> {
final hasCoordinates = exifInfo?.hasCoordinates ?? false;
// Guard no lat/lng
if (!hasCoordinates ||
(asset != null && asset is LocalAsset && asset!.hasRemote)) {
if (!hasCoordinates || (asset != null && asset is LocalAsset && asset!.hasRemote)) {
return const SizedBox.shrink();
}
final remoteId = asset is LocalAsset
? (asset as LocalAsset).remoteId
: (asset as RemoteAsset).id;
final remoteId = asset is LocalAsset ? (asset as LocalAsset).remoteId : (asset as RemoteAsset).id;
final locationName = _getLocationName(exifInfo);
final coordinates =
"${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}";
final coordinates = "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}";
return Padding(
padding: EdgeInsets.symmetric(

View file

@ -30,13 +30,11 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
final isInLockedView = ref.watch(inLockedViewProvider);
final isShowingSheet = ref
.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
int opacity = ref.watch(
assetViewerProvider.select((state) => state.backgroundOpacity),
);
final showControls =
ref.watch(assetViewerProvider.select((s) => s.showingControls));
final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
if (!showControls) {
opacity = 0;
@ -45,8 +43,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
final isCasting = ref.watch(
castProvider.select((c) => c.isCasting),
);
final websocketConnected =
ref.watch(websocketProvider.select((c) => c.isConnected));
final websocketConnected = ref.watch(websocketProvider.select((c) => c.isConnected));
final actions = <Widget>[
if (isCasting || (asset.hasRemote && websocketConnected))
@ -78,8 +75,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
opacity: opacity / 255,
duration: Durations.short2,
child: AppBar(
backgroundColor:
isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125),
backgroundColor: isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125),
leading: const _AppBarBackButton(),
iconTheme: const IconThemeData(size: 22, color: Colors.white),
actionsIconTheme: const IconThemeData(size: 22, color: Colors.white),
@ -117,12 +113,9 @@ class _AppBarBackButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final isShowingSheet = ref
.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
final backgroundColor =
isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black;
final foregroundColor =
isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white;
final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet));
final backgroundColor = isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black;
final foregroundColor = isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white;
return Padding(
padding: const EdgeInsets.only(left: 12.0),

View file

@ -92,9 +92,7 @@ class NativeVideoViewer extends HookConsumerWidget {
try {
if (asset.hasLocal && asset.livePhotoVideoId == null) {
final id = asset is LocalAsset
? (asset as LocalAsset).id
: (asset as RemoteAsset).localId!;
final id = asset is LocalAsset ? (asset as LocalAsset).id : (asset as RemoteAsset).localId!;
final file = await const StorageRepository().getFileForAsset(id);
if (file == null) {
throw Exception('No file found for the video');
@ -111,10 +109,8 @@ class NativeVideoViewer extends HookConsumerWidget {
// Use a network URL for the video player controller
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
final isOriginalVideo =
ref.read(settingsProvider).get<bool>(Setting.loadOriginalVideo);
final String postfixUrl =
isOriginalVideo ? 'original' : 'video/playback';
final isOriginalVideo = ref.read(settingsProvider).get<bool>(Setting.loadOriginalVideo);
final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback';
final String videoUrl = asset.livePhotoVideoId != null
? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl'
: '$serverEndpoint/assets/$remoteId/$postfixUrl';
@ -142,8 +138,7 @@ class NativeVideoViewer extends HookConsumerWidget {
}
try {
aspectRatio.value =
await ref.read(assetServiceProvider).getAspectRatio(asset);
aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset);
} catch (error) {
log.severe(
'Error getting aspect ratio for asset ${asset.name}: $error',
@ -159,8 +154,7 @@ class NativeVideoViewer extends HookConsumerWidget {
}
final videoPlayback = ref.read(videoPlaybackValueProvider);
if ((isBuffering.value ||
videoPlayback.state == VideoPlaybackState.initializing) &&
if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) &&
videoPlayback.state != VideoPlaybackState.buffering) {
ref.read(videoPlaybackValueProvider.notifier).value =
videoPlayback.copyWith(state: VideoPlaybackState.buffering);
@ -219,8 +213,7 @@ class NativeVideoViewer extends HookConsumerWidget {
return;
}
final videoPlayback =
VideoPlaybackValue.fromNativeController(videoController);
final videoPlayback = VideoPlaybackValue.fromNativeController(videoController);
ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback;
if (ref.read(assetViewerProvider.select((s) => s.showingBottomSheet))) {
@ -241,8 +234,7 @@ class NativeVideoViewer extends HookConsumerWidget {
return;
}
final videoPlayback =
VideoPlaybackValue.fromNativeController(videoController);
final videoPlayback = VideoPlaybackValue.fromNativeController(videoController);
if (videoPlayback.state == VideoPlaybackState.playing) {
// Sync with the controls playing
WakelockPlus.enable();
@ -251,8 +243,7 @@ class NativeVideoViewer extends HookConsumerWidget {
WakelockPlus.disable();
}
ref.read(videoPlaybackValueProvider.notifier).status =
videoPlayback.state;
ref.read(videoPlaybackValueProvider.notifier).status = videoPlayback.state;
}
void onPlaybackPositionChanged() {
@ -271,8 +262,7 @@ class NativeVideoViewer extends HookConsumerWidget {
return;
}
ref.read(videoPlaybackValueProvider.notifier).position =
Duration(seconds: playbackInfo.position);
ref.read(videoPlaybackValueProvider.notifier).position = Duration(seconds: playbackInfo.position);
// Check if the video is buffering
if (playbackInfo.status == PlaybackStatus.playing) {
@ -296,10 +286,8 @@ class NativeVideoViewer extends HookConsumerWidget {
}
void removeListeners(NativeVideoPlayerController controller) {
controller.onPlaybackPositionChanged
.removeListener(onPlaybackPositionChanged);
controller.onPlaybackStatusChanged
.removeListener(onPlaybackStatusChanged);
controller.onPlaybackPositionChanged.removeListener(onPlaybackPositionChanged);
controller.onPlaybackStatusChanged.removeListener(onPlaybackStatusChanged);
controller.onPlaybackReady.removeListener(onPlaybackReady);
controller.onPlaybackEnded.removeListener(onPlaybackEnded);
}
@ -324,9 +312,7 @@ class NativeVideoViewer extends HookConsumerWidget {
nc.loadVideoSource(source).catchError((error) {
log.severe('Error loading video source: $error');
});
final loopVideo = ref
.read(appSettingsServiceProvider)
.getSetting<bool>(AppSettingsEnum.loopVideo);
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
nc.setLoop(!asset.isMotionPhoto && loopVideo);
controller.value = nc;

View file

@ -23,15 +23,12 @@ class VideoViewerControls extends HookConsumerWidget {
final assetIsVideo = ref.watch(
currentAssetNotifier.select((asset) => asset != null && asset.isVideo),
);
bool showControls =
ref.watch(assetViewerProvider.select((s) => s.showingControls));
final showBottomSheet =
ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
bool showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
final showBottomSheet = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
if (showBottomSheet) {
showControls = false;
}
final VideoPlaybackState state =
ref.watch(videoPlaybackValueProvider.select((value) => value.state));
final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state));
final cast = ref.watch(castProvider);
@ -45,15 +42,12 @@ class VideoViewerControls extends HookConsumerWidget {
final state = ref.read(videoPlaybackValueProvider).state;
// Do not hide on paused
if (state != VideoPlaybackState.paused &&
state != VideoPlaybackState.completed &&
assetIsVideo) {
if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) {
ref.read(assetViewerProvider.notifier).setControls(false);
}
},
);
final showBuffering =
state == VideoPlaybackState.buffering && !cast.isCasting;
final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting;
/// Shows the controls and starts the timer to hide them
void showControlsAndStartHideTimer() {
@ -62,8 +56,7 @@ class VideoViewerControls extends HookConsumerWidget {
}
// When we change position, show or hide timer
ref.listen(videoPlayerControlsProvider.select((v) => v.position),
(previous, next) {
ref.listen(videoPlayerControlsProvider.select((v) => v.position), (previous, next) {
showControlsAndStartHideTimer();
});
@ -111,14 +104,13 @@ class VideoViewerControls extends HookConsumerWidget {
)
else
GestureDetector(
onTap: () =>
ref.read(assetViewerProvider.notifier).setControls(false),
onTap: () => ref.read(assetViewerProvider.notifier).setControls(false),
child: CenterPlayButton(
backgroundColor: Colors.black54,
iconColor: Colors.white,
isFinished: state == VideoPlaybackState.completed,
isPlaying: state == VideoPlaybackState.playing ||
(cast.isCasting && cast.castState == CastState.playing),
isPlaying:
state == VideoPlaybackState.playing || (cast.isCasting && cast.castState == CastState.playing),
show: assetIsVideo && showControls,
onPressed: togglePlay,
),

View file

@ -21,8 +21,7 @@ class BackupToggleButton extends ConsumerStatefulWidget {
ConsumerState<BackupToggleButton> createState() => BackupToggleButtonState();
}
class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
with SingleTickerProviderStateMixin {
class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _gradientAnimation;
bool _isEnabled = false;
@ -42,9 +41,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
),
);
_isEnabled = ref
.read(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.enableBackup);
_isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
}
@override
@ -54,9 +51,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
}
Future<void> _onToggle(bool value) async {
await ref
.read(appSettingsServiceProvider)
.setSetting(AppSettingsEnum.enableBackup, value);
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.enableBackup, value);
setState(() {
_isEnabled = value;
@ -141,8 +136,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
borderRadius: const BorderRadius.all(Radius.circular(20.5)),
onTap: () => isCanceling ? null : _onToggle(!_isEnabled),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Container(
@ -180,8 +174,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
children: [
Text(
"enable_backup".t(context: context),
style:
context.textTheme.titleMedium?.copyWith(
style: context.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
color: context.primaryColor,
),
@ -214,9 +207,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
backgroundColor: context
.colorScheme.onSurface
.withValues(alpha: 0.2),
backgroundColor: context.colorScheme.onSurface.withValues(alpha: 0.2),
),
),
],
@ -226,8 +217,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton>
),
Switch.adaptive(
value: _isEnabled,
onChanged: (value) =>
isCanceling ? null : _onToggle(value),
onChanged: (value) => isCanceling ? null : _onToggle(value),
),
],
),

View file

@ -31,12 +31,10 @@ class BaseBottomSheet extends ConsumerStatefulWidget {
});
@override
ConsumerState<BaseBottomSheet> createState() =>
_BaseDraggableScrollableSheetState();
ConsumerState<BaseBottomSheet> createState() => _BaseDraggableScrollableSheetState();
}
class _BaseDraggableScrollableSheetState
extends ConsumerState<BaseBottomSheet> {
class _BaseDraggableScrollableSheetState extends ConsumerState<BaseBottomSheet> {
late DraggableScrollableController _controller;
@override
@ -71,8 +69,7 @@ class _BaseDraggableScrollableSheetState
shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent,
builder: (BuildContext context, ScrollController scrollController) {
return Card(
color: widget.backgroundColor ??
context.colorScheme.surfaceContainerHigh,
color: widget.backgroundColor ?? context.colorScheme.surfaceContainerHigh,
borderOnForeground: false,
clipBehavior: Clip.antiAlias,
elevation: 6.0,

View file

@ -71,5 +71,4 @@ ImageProvider getThumbnailImageProvider({
}
bool _shouldUseLocalAsset(BaseAsset asset) =>
asset.hasLocal &&
(!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage));
asset.hasLocal && (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage));

View file

@ -17,8 +17,7 @@ import 'package:immich_mobile/providers/image/exceptions/image_loading_exception
import 'package:logging/logging.dart';
class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
final AssetMediaRepository _assetMediaRepository =
const AssetMediaRepository();
final AssetMediaRepository _assetMediaRepository = const AssetMediaRepository();
final CacheManager? cacheManager;
final String id;
@ -63,20 +62,17 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
CacheManager cache,
ImageDecoderCallback decode,
) async {
final cacheKey =
'${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}';
final cacheKey = '${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}';
final fileFromCache = await cache.getFileFromCache(cacheKey);
if (fileFromCache != null) {
try {
final buffer =
await ImmutableBuffer.fromFilePath(fileFromCache.file.path);
final buffer = await ImmutableBuffer.fromFilePath(fileFromCache.file.path);
return decode(buffer);
} catch (_) {}
}
final thumbnailBytes =
await _assetMediaRepository.getThumbnail(key.id, size: key.size);
final thumbnailBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size);
if (thumbnailBytes == null) {
PaintingBinding.instance.imageCache.evict(key);
throw StateError(
@ -103,8 +99,7 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
}
class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
final AssetMediaRepository _assetMediaRepository =
const AssetMediaRepository();
final AssetMediaRepository _assetMediaRepository = const AssetMediaRepository();
final StorageRepository _storageRepository = const StorageRepository();
final String id;
@ -160,8 +155,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
throw StateError('Unsupported asset type ${key.type}');
}
} catch (error, stack) {
Logger('ImmichLocalImageProvider')
.severe('Error loading local image ${key.name}', error, stack);
Logger('ImmichLocalImageProvider').severe('Error loading local image ${key.name}', error, stack);
throw const ImageLoadingException(
'Could not load image from local storage',
);
@ -172,8 +166,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
LocalFullImageProvider key,
ImageDecoderCallback decode,
) async {
final thumbBytes =
await _assetMediaRepository.getThumbnail(key.id, size: key.size);
final thumbBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size);
if (thumbBytes == null) {
return null;
}
@ -191,8 +184,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
}
final fileSize = await file.length();
final devicePixelRatio =
PlatformDispatcher.instance.views.first.devicePixelRatio;
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
final isLargeFile = fileSize > 20 * 1024 * 1024; // 20MB
final isHEIC = file.path.toLowerCase().contains(RegExp(r'\.(heic|heif)$'));
final isProgressive = isLargeFile || (isHEIC && !Platform.isIOS);
@ -204,8 +196,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
(key.size.width * progressiveMultiplier).clamp(256, 1024),
(key.size.height * progressiveMultiplier).clamp(256, 1024),
);
final mediumThumb =
await _assetMediaRepository.getThumbnail(key.id, size: size);
final mediumThumb = await _assetMediaRepository.getThumbnail(key.id, size: size);
if (mediumThumb != null) {
final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb);
yield await decode(mediumBuffer);
@ -221,8 +212,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
(key.size.width * progressiveMultiplier).clamp(512, 2048),
(key.size.height * progressiveMultiplier).clamp(512, 2048),
);
final highThumb =
await _assetMediaRepository.getThumbnail(key.id, size: size);
final highThumb = await _assetMediaRepository.getThumbnail(key.id, size: size);
if (highThumb != null) {
final highBuffer = await ImmutableBuffer.fromUint8List(highThumb);
yield await decode(highBuffer);
@ -238,15 +228,11 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is LocalFullImageProvider) {
return id == other.id &&
size == other.size &&
type == other.type &&
name == other.name;
return id == other.id && size == other.size && type == other.type && name == other.name;
}
return false;
}
@override
int get hashCode =>
id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode;
int get hashCode => id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode;
}

View file

@ -26,10 +26,8 @@ class Thumbnail extends StatelessWidget {
@override
Widget build(BuildContext context) {
final thumbHash =
asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null;
final provider =
getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size);
final thumbHash = asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null;
final provider = getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size);
return OctoImage.fromSet(
image: provider,
@ -72,8 +70,7 @@ OctoErrorBuilder _blurHashErrorBuilder(
BoxFit? fit,
}) =>
(context, e, s) {
Logger("ImThumbnail")
.warning("Error loading thumbnail for ${asset?.name}", e, s);
Logger("ImThumbnail").warning("Error loading thumbnail for ${asset?.name}", e, s);
provider?.evict();
return Stack(
alignment: Alignment.center,

View file

@ -28,9 +28,8 @@ class ThumbnailTile extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0;
final assetContainerColor = context.isDarkTheme
? context.primaryColor.darken(amount: 0.4)
: context.primaryColor.lighten(amount: 0.75);
final assetContainerColor =
context.isDarkTheme ? context.primaryColor.darken(amount: 0.4) : context.primaryColor.lighten(amount: 0.75);
final isSelected = ref.watch(
multiSelectProvider.select(
@ -53,8 +52,7 @@ class ThumbnailTile extends ConsumerWidget {
)
: const BoxDecoration();
final hasStack =
asset is RemoteAsset && (asset as RemoteAsset).stackId != null;
final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null;
return Stack(
children: [
@ -63,9 +61,8 @@ class ThumbnailTile extends ConsumerWidget {
curve: Curves.decelerate,
decoration: borderStyle,
child: ClipRRect(
borderRadius: isSelected || lockSelection
? const BorderRadius.all(Radius.circular(15.0))
: BorderRadius.zero,
borderRadius:
isSelected || lockSelection ? const BorderRadius.all(Radius.circular(15.0)) : BorderRadius.zero,
child: Stack(
children: [
Positioned.fill(
@ -141,9 +138,7 @@ class ThumbnailTile extends ConsumerWidget {
child: _SelectionIndicator(
isSelected: isSelected,
isLocked: lockSelection,
color: lockSelection
? context.colorScheme.surfaceContainerHighest
: assetContainerColor,
color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor,
),
),
),

View file

@ -46,11 +46,9 @@ class DriftMemoryCard extends StatelessWidget {
BoxFit fit = BoxFit.contain;
if (asset.width != null && asset.height != null) {
final aspectRatio = asset.width! / asset.height!;
final phoneAspectRatio =
constraints.maxWidth / constraints.maxHeight;
final phoneAspectRatio = constraints.maxWidth / constraints.maxHeight;
// Look for a 25% difference in either direction
if (phoneAspectRatio * .75 < aspectRatio &&
phoneAspectRatio * 1.25 > aspectRatio) {
if (phoneAspectRatio * .75 < aspectRatio && phoneAspectRatio * 1.25 > aspectRatio) {
// Cover to look nice if we have nearly the same aspect ratio
fit = BoxFit.cover;
}

View file

@ -47,8 +47,7 @@ class DriftMemoryLane extends ConsumerWidget {
),
);
},
children:
memories.map((memory) => DriftMemoryCard(memory: memory)).toList(),
children: memories.map((memory) => DriftMemoryCard(memory: memory)).toList(),
),
);
}

View file

@ -13,8 +13,7 @@ class PartnerUserAvatar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final url =
"${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image";
final url = "${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image";
final nameFirstLetter = partner.name.isNotEmpty ? partner.name[0] : "";
return CircleAvatar(
radius: 16,

View file

@ -97,14 +97,12 @@ class DriftRemoteAlbumOption extends ConsumerWidget {
ListTile(
leading: Icon(
Icons.delete,
color:
context.isDarkTheme ? Colors.red[400] : Colors.red[800],
color: context.isDarkTheme ? Colors.red[400] : Colors.red[800],
),
title: Text(
'delete_album'.t(context: context),
style: textStyle.copyWith(
color:
context.isDarkTheme ? Colors.red[400] : Colors.red[800],
color: context.isDarkTheme ? Colors.red[400] : Colors.red[800],
),
),
onTap: onDeleteAlbum,

View file

@ -90,8 +90,7 @@ class RenderFixedRow extends RenderBox
}
}
double get intrinsicWidth =>
dimension * childCount + spacing * (childCount - 1);
double get intrinsicWidth => dimension * childCount + spacing * (childCount - 1);
@override
double computeMinIntrinsicWidth(double height) => intrinsicWidth;

View file

@ -39,9 +39,7 @@ class FixedSegment extends Segment {
@override
double indexToLayoutOffset(int index) {
final relativeIndex = index - gridIndex;
return relativeIndex < 0
? startOffset
: gridOffset + (mainAxisExtend * relativeIndex);
return relativeIndex < 0 ? startOffset : gridOffset + (mainAxisExtend * relativeIndex);
}
@override
@ -98,8 +96,7 @@ class _FixedSegmentRow extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final isScrubbing =
ref.watch(timelineStateProvider.select((s) => s.isScrubbing));
final isScrubbing = ref.watch(timelineStateProvider.select((s) => s.isScrubbing));
final timelineService = ref.read(timelineServiceProvider);
if (isScrubbing) {
@ -215,11 +212,8 @@ class _AssetTileWidget extends ConsumerWidget {
return RepaintBoundary(
child: GestureDetector(
onTap: () => lockSelection
? null
: _handleOnTap(context, ref, assetIndex, asset),
onLongPress: () =>
lockSelection ? null : _handleOnLongPress(ref, asset),
onTap: () => lockSelection ? null : _handleOnTap(context, ref, assetIndex, asset),
onLongPress: () => lockSelection ? null : _handleOnLongPress(ref, asset),
child: ThumbnailTile(
asset,
lockSelection: lockSelection,

View file

@ -37,17 +37,13 @@ class FixedSegmentBuilder extends SegmentBuilder {
GroupAssetsBy.month => HeaderType.month,
GroupAssetsBy.day ||
GroupAssetsBy.auto =>
bucket is TimeBucket && bucket.date.month != previousDate?.month
? HeaderType.monthAndDay
: HeaderType.day,
bucket is TimeBucket && bucket.date.month != previousDate?.month ? HeaderType.monthAndDay : HeaderType.day,
GroupAssetsBy.none => HeaderType.none,
};
final headerExtent = SegmentBuilder.headerExtent(timelineHeader);
final segmentStartOffset = startOffset;
startOffset += headerExtent +
(tileHeight * numberOfRows) +
spacing * (numberOfRows - 1);
startOffset += headerExtent + (tileHeight * numberOfRows) + spacing * (numberOfRows - 1);
final segmentEndOffset = startOffset;
segments.add(

View file

@ -43,10 +43,8 @@ class TimelineHeader extends StatelessWidget {
final date = (bucket as TimeBucket).date;
final isMonthHeader =
header == HeaderType.month || header == HeaderType.monthAndDay;
final isDayHeader =
header == HeaderType.day || header == HeaderType.monthAndDay;
final isMonthHeader = header == HeaderType.month || header == HeaderType.monthAndDay;
final isDayHeader = header == HeaderType.day || header == HeaderType.monthAndDay;
return Padding(
padding: EdgeInsets.only(
@ -111,9 +109,7 @@ class _BulkSelectIconButton extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
List<BaseAsset> bucketAssets;
try {
bucketAssets = ref
.watch(timelineServiceProvider)
.getAssets(assetOffset, bucket.assetCount);
bucketAssets = ref.watch(timelineServiceProvider).getAssets(assetOffset, bucket.assetCount);
} catch (e) {
bucketAssets = <BaseAsset>[];
}

View file

@ -58,15 +58,13 @@ List<_Segment> _buildSegments({
DateTime? lastDate;
double lastOffset = -offsetThreshold;
for (final layoutSegment in layoutSegments) {
final scrollPercentage =
layoutSegment.startOffset / layoutSegments.last.endOffset;
final scrollPercentage = layoutSegment.startOffset / layoutSegments.last.endOffset;
final startOffset = scrollPercentage * timelineHeight;
final date = (layoutSegment.bucket as TimeBucket).date;
final label = formatter.format(date);
final showSegment = lastOffset + offsetThreshold <= startOffset &&
(lastDate == null || date.year != lastDate.year);
final showSegment = lastOffset + offsetThreshold <= startOffset && (lastDate == null || date.year != lastDate.year);
segments.add(
_Segment(
@ -85,8 +83,7 @@ List<_Segment> _buildSegments({
return segments;
}
class ScrubberState extends ConsumerState<Scrubber>
with TickerProviderStateMixin {
class ScrubberState extends ConsumerState<Scrubber> with TickerProviderStateMixin {
double _thumbTopOffset = 0.0;
bool _isDragging = false;
List<_Segment> _segments = [];
@ -98,17 +95,14 @@ class ScrubberState extends ConsumerState<Scrubber>
late AnimationController _labelAnimationController;
late Animation<double> _labelAnimation;
double get _scrubberHeight =>
widget.timelineHeight - widget.topPadding - widget.bottomPadding;
double get _scrubberHeight => widget.timelineHeight - widget.topPadding - widget.bottomPadding;
late ScrollController _scrollController;
double get _currentOffset {
if (_scrollController.hasClients != true) return 0.0;
return _scrollController.offset *
_scrubberHeight /
_scrollController.position.maxScrollExtent;
return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent;
}
@override
@ -148,8 +142,7 @@ class ScrubberState extends ConsumerState<Scrubber>
void didUpdateWidget(covariant Scrubber oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.layoutSegments.lastOrNull?.endOffset !=
widget.layoutSegments.lastOrNull?.endOffset) {
if (oldWidget.layoutSegments.lastOrNull?.endOffset != widget.layoutSegments.lastOrNull?.endOffset) {
_segments = _buildSegments(
layoutSegments: widget.layoutSegments,
timelineHeight: _scrubberHeight,
@ -179,8 +172,7 @@ class ScrubberState extends ConsumerState<Scrubber>
return false;
}
if (notification is ScrollStartNotification ||
notification is ScrollUpdateNotification) {
if (notification is ScrollStartNotification || notification is ScrollUpdateNotification) {
ref.read(timelineStateProvider.notifier).setScrolling(true);
} else if (notification is ScrollEndNotification) {
ref.read(timelineStateProvider.notifier).setScrolling(false);
@ -287,8 +279,7 @@ class ScrubberState extends ConsumerState<Scrubber>
return widget.layoutSegments.indexWhere(
(layoutSegment) {
final bucket = layoutSegment.bucket as TimeBucket;
return bucket.date.year == segment.date.year &&
bucket.date.month == segment.date.month;
return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month;
},
);
}
@ -299,10 +290,7 @@ class ScrubberState extends ConsumerState<Scrubber>
final viewportHeight = _scrollController.position.viewportDimension;
final targetScrollOffset = layoutSegment.startOffset;
final centeredOffset = targetScrollOffset -
(viewportHeight / 4) +
100 +
(widget.monthSegmentSnappingOffset ?? 0.0);
final centeredOffset = targetScrollOffset - (viewportHeight / 4) + 100 + (widget.monthSegmentSnappingOffset ?? 0.0);
_scrollController.jumpTo(centeredOffset.clamp(0.0, maxScrollExtent));
}
@ -354,8 +342,7 @@ class ScrubberState extends ConsumerState<Scrubber>
isDragging: _isDragging,
),
),
if (_scrollController.hasClients &&
_scrollController.position.maxScrollExtent > 0)
if (_scrollController.hasClients && _scrollController.position.maxScrollExtent > 0)
PositionedDirectional(
top: _thumbTopOffset + widget.topPadding,
end: 0,
@ -492,9 +479,8 @@ class _Scrubber extends StatelessWidget {
@override
Widget build(BuildContext context) {
final backgroundColor = context.isDarkTheme
? context.colorScheme.primary.darken(amount: .5)
: context.colorScheme.primary;
final backgroundColor =
context.isDarkTheme ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary;
return _SlideFadeTransition(
animation: thumbAnimation,
@ -590,8 +576,7 @@ class _SlideFadeTransition extends StatelessWidget {
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) =>
_animation.value == 0.0 ? const SizedBox() : child!,
builder: (context, child) => _animation.value == 0.0 ? const SizedBox() : child!,
child: SlideTransition(
position: Tween(
begin: const Offset(0.3, 0.0),

View file

@ -42,8 +42,7 @@ abstract class Segment {
bool containsIndex(int index) => firstIndex <= index && index <= lastIndex;
bool isWithinOffset(double offset) =>
startOffset <= offset && offset <= endOffset;
bool isWithinOffset(double offset) => startOffset <= offset && offset <= endOffset;
int getMinChildIndexForScrollOffset(double scrollOffset);
int getMaxChildIndexForScrollOffset(double scrollOffset);
@ -88,13 +87,9 @@ abstract class Segment {
}
extension SegmentListExtension on List<Segment> {
bool equals(List<Segment> other) =>
length == other.length &&
lastOrNull?.endOffset == other.lastOrNull?.endOffset;
bool equals(List<Segment> other) => length == other.length && lastOrNull?.endOffset == other.lastOrNull?.endOffset;
Segment? findByIndex(int index) =>
firstWhereOrNull((s) => s.containsIndex(index));
Segment? findByIndex(int index) => firstWhereOrNull((s) => s.containsIndex(index));
Segment? findByOffset(double offset) =>
firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull;
Segment? findByOffset(double offset) => firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull;
}

View file

@ -105,9 +105,7 @@ final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>(
final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1));
final tileExtent = math.max(0, availableTileWidth) / columnCount;
final groupBy = args.groupBy ??
GroupAssetsBy
.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)];
final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)];
final timelineService = ref.watch(timelineServiceProvider);
yield* timelineService.watchBuckets().map((buckets) {
@ -123,7 +121,6 @@ final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>(
dependencies: [timelineServiceProvider, timelineArgsProvider],
);
final timelineStateProvider =
NotifierProvider<TimelineStateNotifier, TimelineState>(
final timelineStateProvider = NotifierProvider<TimelineStateNotifier, TimelineState>(
TimelineStateNotifier.new,
);

View file

@ -101,8 +101,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
@override
void initState() {
super.initState();
_reloadSubscription =
EventStream.shared.listen<TimelineReloadEvent>((_) => setState(() {}));
_reloadSubscription = EventStream.shared.listen<TimelineReloadEvent>((_) => setState(() {}));
}
@override
@ -115,8 +114,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
@override
Widget build(BuildContext _) {
final asyncSegments = ref.watch(timelineSegmentProvider);
final maxHeight =
ref.watch(timelineArgsProvider.select((args) => args.maxHeight));
final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight));
final isSelectionMode = ref.watch(
multiSelectProvider.select((s) => s.forceEnable),
);
@ -124,17 +122,11 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
return asyncSegments.widgetWhen(
onData: (segments) {
final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1;
final double appBarExpandedHeight =
widget.appBar != null && widget.appBar is MesmerizingSliverAppBar
? 200
: 0;
final topPadding = context.padding.top +
(widget.appBar == null ? 0 : kToolbarHeight) +
10;
final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar ? 200 : 0;
final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10;
const scrubberBottomPadding = 100.0;
final bottomPadding = context.padding.bottom +
(widget.appBar == null ? 0 : scrubberBottomPadding);
final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding);
return PrimaryScrollController(
controller: _scrollController,
@ -145,16 +137,12 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
timelineHeight: maxHeight,
topPadding: topPadding,
bottomPadding: bottomPadding,
monthSegmentSnappingOffset:
widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight,
monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight,
child: CustomScrollView(
primary: true,
cacheExtent: maxHeight * 2,
slivers: [
if (isSelectionMode)
const SelectionSliverAppBar()
else if (widget.appBar != null)
widget.appBar!,
if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!,
if (widget.topSliverWidget != null) widget.topSliverWidget!,
_SliverSegmentedList(
segments: segments,
@ -162,8 +150,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
(ctx, index) {
if (index >= childCount) return null;
final segment = segments.findByIndex(index);
return segment?.builder(ctx, index) ??
const SizedBox.shrink();
return segment?.builder(ctx, index) ?? const SizedBox.shrink();
},
childCount: childCount,
addAutomaticKeepAlives: false,
@ -233,8 +220,7 @@ class _SliverSegmentedList extends SliverMultiBoxAdaptorWidget {
}) : _segments = segments;
@override
_RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) =>
_RenderSliverTimelineBoxAdaptor(
_RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => _RenderSliverTimelineBoxAdaptor(
childManager: context as SliverMultiBoxAdaptorElement,
segments: _segments,
);
@ -266,17 +252,13 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
}) : _segments = segments;
int getMinChildIndexForScrollOffset(double offset) =>
_segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ??
0;
_segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? 0;
int getMaxChildIndexForScrollOffset(double offset) =>
_segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ??
0;
_segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ?? 0;
double indexToLayoutOffset(int index) =>
(_segments.findByIndex(index) ?? _segments.lastOrNull)
?.indexToLayoutOffset(index) ??
0;
(_segments.findByIndex(index) ?? _segments.lastOrNull)?.indexToLayoutOffset(index) ?? 0;
double estimateMaxScrollOffset() => _segments.lastOrNull?.endOffset ?? 0;
@ -288,8 +270,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
// Assume initially that we have enough children to fill the viewport/cache area.
childManager.setDidUnderflow(false);
final double scrollOffset =
constraints.scrollOffset + constraints.cacheOrigin;
final double scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;
assert(scrollOffset >= 0.0);
final double remainingExtent = constraints.remainingCacheExtent;
@ -298,31 +279,26 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
final double targetScrollOffset = scrollOffset + remainingExtent;
// Find the index of the first child that should be visible or in the leading cache area.
final int firstRequiredChildIndex =
getMinChildIndexForScrollOffset(scrollOffset);
final int firstRequiredChildIndex = getMinChildIndexForScrollOffset(scrollOffset);
// Find the index of the last child that should be visible or in the trailing cache area.
final int? lastRequiredChildIndex = targetScrollOffset.isFinite
? getMaxChildIndexForScrollOffset(targetScrollOffset)
: null;
final int? lastRequiredChildIndex =
targetScrollOffset.isFinite ? getMaxChildIndexForScrollOffset(targetScrollOffset) : null;
// Remove children that are no longer visible or within the cache area.
if (firstChild == null) {
collectGarbage(0, 0);
} else {
final int leadingChildrenToRemove =
calculateLeadingGarbage(firstIndex: firstRequiredChildIndex);
final int trailingChildrenToRemove = lastRequiredChildIndex == null
? 0
: calculateTrailingGarbage(lastIndex: lastRequiredChildIndex);
final int leadingChildrenToRemove = calculateLeadingGarbage(firstIndex: firstRequiredChildIndex);
final int trailingChildrenToRemove =
lastRequiredChildIndex == null ? 0 : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex);
collectGarbage(leadingChildrenToRemove, trailingChildrenToRemove);
}
// If there are currently no children laid out (e.g., initial load),
// try to add the first child needed for the current scroll offset.
if (firstChild == null) {
final double firstChildLayoutOffset =
indexToLayoutOffset(firstRequiredChildIndex);
final double firstChildLayoutOffset = indexToLayoutOffset(firstRequiredChildIndex);
final bool childAdded = addInitialChild(
index: firstRequiredChildIndex,
layoutOffset: firstChildLayoutOffset,
@ -330,8 +306,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
if (!childAdded) {
// There are either no children, or we are past the end of all our children.
final double max =
firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset();
final double max = firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset();
geometry = SliverGeometry(scrollExtent: max, maxPaintExtent: max);
childManager.didFinishLayout();
return;
@ -342,26 +317,20 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
RenderBox? highestLaidOutChild;
final childConstraints = constraints.asBoxConstraints();
for (int currentIndex = indexOf(firstChild!) - 1;
currentIndex >= firstRequiredChildIndex;
--currentIndex) {
final RenderBox? newLeadingChild =
insertAndLayoutLeadingChild(childConstraints);
for (int currentIndex = indexOf(firstChild!) - 1; currentIndex >= firstRequiredChildIndex; --currentIndex) {
final RenderBox? newLeadingChild = insertAndLayoutLeadingChild(childConstraints);
if (newLeadingChild == null) {
// If a child is missing where we expect one, it indicates
// an inconsistency in offset that needs correction.
final Segment? segment =
_segments.findByIndex(currentIndex) ?? _segments.firstOrNull;
final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.firstOrNull;
geometry = SliverGeometry(
// Request a scroll correction based on where the missing child should have been.
scrollOffsetCorrection:
segment?.indexToLayoutOffset(currentIndex) ?? 0.0,
scrollOffsetCorrection: segment?.indexToLayoutOffset(currentIndex) ?? 0.0,
);
// Parent will re-layout everything.
return;
}
final childParentData =
newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData;
final childParentData = newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData;
childParentData.layoutOffset = indexToLayoutOffset(currentIndex);
assert(childParentData.index == currentIndex);
highestLaidOutChild ??= newLeadingChild;
@ -375,10 +344,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
// The [firstChild] that existed at the start of performLayout is still the first one we need.
if (highestLaidOutChild == null) {
firstChild!.layout(childConstraints);
final childParentData =
firstChild!.parentData! as SliverMultiBoxAdaptorParentData;
childParentData.layoutOffset =
indexToLayoutOffset(firstRequiredChildIndex);
final childParentData = firstChild!.parentData! as SliverMultiBoxAdaptorParentData;
childParentData.layoutOffset = indexToLayoutOffset(firstRequiredChildIndex);
highestLaidOutChild = firstChild;
}
@ -389,8 +356,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
double calculatedMaxScrollOffset = double.infinity;
for (int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1;
lastRequiredChildIndex == null ||
currentIndex <= lastRequiredChildIndex;
lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex;
++currentIndex) {
RenderBox? child = childAfter(mostRecentlyLaidOutChild!);
@ -400,11 +366,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
after: mostRecentlyLaidOutChild,
);
if (child == null) {
final Segment? segment =
_segments.findByIndex(currentIndex) ?? _segments.lastOrNull;
calculatedMaxScrollOffset =
segment?.indexToLayoutOffset(currentIndex) ??
computeMaxScrollOffset();
final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.lastOrNull;
calculatedMaxScrollOffset = segment?.indexToLayoutOffset(currentIndex) ?? computeMaxScrollOffset();
break;
}
} else {
@ -412,28 +375,23 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
}
mostRecentlyLaidOutChild = child;
final childParentData = mostRecentlyLaidOutChild.parentData!
as SliverMultiBoxAdaptorParentData;
final childParentData = mostRecentlyLaidOutChild.parentData! as SliverMultiBoxAdaptorParentData;
assert(childParentData.index == currentIndex);
childParentData.layoutOffset = indexToLayoutOffset(currentIndex);
}
final int lastLaidOutChildIndex = indexOf(lastChild!);
final double leadingScrollOffset =
indexToLayoutOffset(firstRequiredChildIndex);
final double trailingScrollOffset =
indexToLayoutOffset(lastLaidOutChildIndex + 1);
final double leadingScrollOffset = indexToLayoutOffset(firstRequiredChildIndex);
final double trailingScrollOffset = indexToLayoutOffset(lastLaidOutChildIndex + 1);
assert(
firstRequiredChildIndex == 0 ||
(childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <=
precisionErrorTolerance,
(childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <= precisionErrorTolerance,
);
assert(debugAssertChildListIsNonEmptyAndContiguous());
assert(indexOf(firstChild!) == firstRequiredChildIndex);
assert(
lastRequiredChildIndex == null ||
lastLaidOutChildIndex <= lastRequiredChildIndex,
lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex,
);
calculatedMaxScrollOffset = math.min(
@ -453,11 +411,9 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
to: trailingScrollOffset,
);
final double targetEndScrollOffsetForPaint =
constraints.scrollOffset + constraints.remainingPaintExtent;
final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite
? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint)
: null;
final double targetEndScrollOffsetForPaint = constraints.scrollOffset + constraints.remainingPaintExtent;
final int? targetLastIndexForPaint =
targetEndScrollOffsetForPaint.isFinite ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) : null;
final maxPaintExtent = math.max(paintExtent, calculatedMaxScrollOffset);
@ -468,8 +424,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor {
// Indicates if there's content scrolled off-screen.
// This is true if the last child needed for painting is actually laid out,
// or if the first child is partially visible.
hasVisualOverflow: (targetLastIndexForPaint != null &&
lastLaidOutChildIndex >= targetLastIndexForPaint) ||
hasVisualOverflow: (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) ||
constraints.scrollOffset > 0.0,
cacheExtent: cacheExtent,
);
@ -489,8 +444,7 @@ class _MultiSelectStatusButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectCount =
ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length));
final selectCount = ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length));
return ElevatedButton.icon(
onPressed: () => ref.read(multiSelectProvider.notifier).reset(),
icon: Icon(