fix: riverpod reactivity regression from lint fixes

This commit is contained in:
shenlong-tanwen 2026-08-12 18:06:35 +05:30
parent 199723261c
commit 44ce9c7b17
6 changed files with 100 additions and 52 deletions

View file

@ -316,12 +316,11 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final backupAlbums = ref.watch(backupAlbumProvider);
Widget buildSelectedAlbumName() {
String text = context.t.backup_controller_page_backup_selected;
final albums = ref
.read(backupAlbumProvider)
.where((album) => album.backupSelection == BackupSelection.selected)
.toList();
final albums = backupAlbums.where((album) => album.backupSelection == BackupSelection.selected).toList();
if (albums.isNotEmpty) {
for (var album in albums) {
@ -352,10 +351,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget {
Widget buildExcludedAlbumName() {
String text = context.t.backup_controller_page_excluded;
final albums = ref
.read(backupAlbumProvider)
.where((album) => album.backupSelection == BackupSelection.excluded)
.toList();
final albums = backupAlbums.where((album) => album.backupSelection == BackupSelection.excluded).toList();
if (albums.isNotEmpty) {
for (var album in albums) {

View file

@ -32,6 +32,8 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
final userId = ref.watch(authProvider).userId;
final activityEnabled = useState(album.isActivityEnabled);
final isOwner = album.ownerId == userId;
final owner = isOwner ? ref.watch(currentUserProvider) : null;
final allUsers = isOwner ? null : ref.watch(driftUsersProvider);
void showErrorMessage() {
ContextHelper(context).pop();
@ -141,7 +143,6 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
Widget buildOwnerInfo() {
if (isOwner) {
final owner = ref.read(currentUserProvider);
return ListTile(
leading: owner != null ? UserCircleAvatar(user: owner) : const SizedBox(),
title: Text(album.ownerName, style: const TextStyle(fontWeight: FontWeight.w500)),
@ -149,8 +150,11 @@ class DriftAlbumOptionsPage extends HookConsumerWidget {
trailing: Text(context.t.owner, style: context.textTheme.labelLarge),
);
} else {
final usersProvider = ref.read(driftUsersProvider);
return usersProvider.maybeWhen(
if (allUsers == null) {
return const SizedBox();
}
return allUsers.maybeWhen(
data: (users) {
final user = users.firstWhereOrNull((u) => u.id == album.ownerId);

View file

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/config/slideshow_config.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@ -295,14 +296,7 @@ class _DriftSlideshowPageState extends ConsumerState<DriftSlideshowPage> with Si
color: context.colorScheme.primary,
);
} else {
return LinearProgressIndicator(
color: context.colorScheme.primary,
borderRadius: BorderRadius.zero,
minHeight: 5,
value:
ref.read(videoPlayerProvider(asset.id).select((s) => s.position)).inMilliseconds /
asset.duration.inMilliseconds,
);
return _VideoProgressBar(asset: asset);
}
}
@ -374,25 +368,13 @@ class _DriftSlideshowPageState extends ConsumerState<DriftSlideshowPage> with Si
builder: (context, value, _) => buildPhotoView(scale * (1.0 + value * _kenBurnsZoom)),
);
} else {
final status = ref.read(videoPlayerProvider(asset.id).select((s) => s.status));
final position = ref.read(videoPlayerProvider(asset.id)).position;
if (status == VideoPlaybackStatus.completed && isCurrent && position.inMicroseconds > 0) {
unawaited(_nextPage());
} else if (status == VideoPlaybackStatus.playing) {
unawaited(ref.read(videoPlayerProvider(asset.id).notifier).setLoop(false));
}
return PhotoView.customChild(
onTapUp: (_, _, _) => _onTapUp(),
disableScaleGestures: true,
filterQuality: FilterQuality.high,
initialScale: scale,
child: NativeVideoViewer(
asset: asset,
isCurrent: isCurrent,
image: Image(image: imageProvider, fit: BoxFit.contain, alignment: Alignment.center),
),
return _VideoChild(
asset: asset,
isCurrent: isCurrent,
scale: scale,
imageProvider: imageProvider,
onTapUp: _onTapUp,
onCompleted: _nextPage,
);
}
}
@ -477,6 +459,67 @@ class _DriftSlideshowPageState extends ConsumerState<DriftSlideshowPage> with Si
}
}
class _VideoChild extends ConsumerWidget {
final BaseAsset asset;
final bool isCurrent;
final PhotoViewComputedScale scale;
final ImageProvider imageProvider;
final VoidCallback onTapUp;
final VoidCallback onCompleted;
const _VideoChild({
required this.asset,
required this.isCurrent,
required this.scale,
required this.imageProvider,
required this.onTapUp,
required this.onCompleted,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(videoPlayerProvider(asset.id).select((s) => s.status), (_, status) {
if (status == VideoPlaybackStatus.completed) {
if (isCurrent && ref.read(videoPlayerProvider(asset.id)).position.inMicroseconds > 0) {
onCompleted();
}
} else if (status == VideoPlaybackStatus.playing) {
unawaited(ref.read(videoPlayerProvider(asset.id).notifier).setLoop(false));
}
});
return PhotoView.customChild(
onTapUp: (_, _, _) => onTapUp(),
disableScaleGestures: true,
filterQuality: FilterQuality.high,
initialScale: scale,
child: NativeVideoViewer(
asset: asset,
isCurrent: isCurrent,
image: Image(image: imageProvider, fit: BoxFit.contain, alignment: Alignment.center),
),
);
}
}
class _VideoProgressBar extends ConsumerWidget {
final BaseAsset asset;
const _VideoProgressBar({required this.asset});
@override
Widget build(BuildContext context, WidgetRef ref) {
final position = ref.watch(videoPlayerProvider(asset.id).select((s) => s.position));
return LinearProgressIndicator(
color: context.colorScheme.primary,
borderRadius: BorderRadius.zero,
minHeight: 5,
value: position.inMilliseconds / asset.duration.inMilliseconds,
);
}
}
/// Progress bar for image slides, driven by an explicit [AnimationController].
///
/// [TweenAnimationBuilder] creates its controller internally with the default

View file

@ -52,6 +52,7 @@ class DriftUserSelectionPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<List<UserDto>> suggestedShareUsers = ref.watch(driftUsersProvider);
final sharedUsers = ref.watch(remoteAlbumSharedUsersProvider(album.id));
final sharedUsersList = useState<Set<UserDto>>({});
void addNewUsersHandler() {
@ -136,9 +137,6 @@ class DriftUserSelectionPage extends HookConsumerWidget {
),
body: suggestedShareUsers.widgetWhen(
onData: (users) {
// Get shared users for this album from the database
final sharedUsers = ref.read(remoteAlbumSharedUsersProvider(album.id));
return sharedUsers.when(
data: (albumSharedUsers) {
// Filter out users that are already shared with this album and the owner

View file

@ -742,9 +742,7 @@ class _SearchResultGrid extends ConsumerWidget {
return false;
}
Widget? _bottomWidget(BuildContext context, WidgetRef ref) {
final isLoading = ref.read(paginatedSearchProvider.select((s) => s.isLoading));
Widget? _bottomWidget(BuildContext context, {required bool isLoading, required bool hasMore}) {
if (isLoading) {
return const SliverFillRemaining(
hasScrollBody: false,
@ -755,8 +753,6 @@ class _SearchResultGrid extends ConsumerWidget {
);
}
final hasMore = ref.read(paginatedSearchProvider.select((s) => s.nextPage != null));
if (hasMore) {
return null;
}
@ -778,6 +774,7 @@ class _SearchResultGrid extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final hasAssets = ref.watch(paginatedSearchProvider.select((s) => s.assets.isNotEmpty));
final isLoading = ref.watch(paginatedSearchProvider.select((s) => s.isLoading));
final hasMore = ref.watch(paginatedSearchProvider.select((s) => s.nextPage != null));
if (!hasAssets && !isLoading) {
return const _SearchNoResults();
@ -807,7 +804,7 @@ class _SearchResultGrid extends ConsumerWidget {
bottomSheet: const GeneralBottomSheet(minChildSize: 0.20),
snapToMonth: false,
loadingWidget: const SizedBox.shrink(),
bottomSliverWidget: _bottomWidget(context, ref),
bottomSliverWidget: _bottomWidget(context, isLoading: isLoading, hasMore: hasMore),
),
),
),

View file

@ -177,7 +177,15 @@ class _BackupIndicator extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final indicatorIcon = _getBackupBadgeIcon(context, ref);
final backupEnabled = ref.watch(appConfigProvider.select((c) => c.backup.enabled));
final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none));
final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty));
final indicatorIcon = _getBackupBadgeIcon(
context,
backupEnabled: backupEnabled,
hasError: hasError,
isUploading: isUploading,
);
return IconButton(
onPressed: () => context.pushRoute(const DriftBackupRoute()),
@ -192,12 +200,14 @@ class _BackupIndicator extends ConsumerWidget {
);
}
Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) {
final backupEnabled = ref.read(appConfigProvider.select((c) => c.backup.enabled));
final hasError = ref.read(driftBackupProvider.select((state) => state.error != BackupError.none));
Widget? _getBackupBadgeIcon(
BuildContext context, {
required bool backupEnabled,
required bool hasError,
required bool isUploading,
}) {
final isDarkTheme = context.isDarkTheme;
final iconColor = isDarkTheme ? Colors.white : Colors.black;
final isUploading = ref.read(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty));
if (!backupEnabled) {
return _BadgeLabel(