From 4621846ec407e2a407e90c0911c36186529bcc09 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 11 May 2026 08:21:55 -0400 Subject: [PATCH] fix(flutter): seed connectivityProvider with initial value (unhang detail screens) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause for "only the first tile loads, others spin forever": Connectivity().onConnectivityChanged only emits on connectivity *changes*, not on subscription. Cold-cache code paths in albumProvider, artistProvider, playlistsProvider, likes etc. all gate their fetches on `await ref.read(connectivityProvider.future)`. Until the OS happened to report the first connectivity flip, that future never resolved — so any detail screen for an album/artist/playlist not already in the drift cache hung indefinitely at the connectivity check. The "first tile" appeared to work because by the time the user tapped it, a connectivity event had landed (or that album was already cached from a prior session). Subsequent tiles whose data wasn't yet cached blocked. Switch the provider to async* and seed it with an immediate checkConnectivity() before forwarding the change stream. .future now resolves on first read regardless of whether the OS has reported a change yet. --- .../lib/cache/connectivity_provider.dart | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/flutter_client/lib/cache/connectivity_provider.dart b/flutter_client/lib/cache/connectivity_provider.dart index d3cf4d71..4e175661 100644 --- a/flutter_client/lib/cache/connectivity_provider.dart +++ b/flutter_client/lib/cache/connectivity_provider.dart @@ -5,9 +5,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; /// connectivity_plus reports the union (wifi, mobile, vpn, etc.) — operator /// chose "no Wi-Fi gate" for #357, so any connection means "go ahead and /// pull/cache". -final connectivityProvider = StreamProvider((ref) { +/// +/// IMPORTANT: onConnectivityChanged only emits on *changes*, not on +/// subscription. Without seeding an initial value via checkConnectivity(), +/// any consumer using `ref.read(connectivityProvider.future)` would +/// block until the OS happened to report a connectivity flip — which +/// is exactly what made album/artist/playlist detail screens spin +/// forever for tiles tapped before the first event landed. +final connectivityProvider = StreamProvider((ref) async* { final c = Connectivity(); - return c.onConnectivityChanged.map( - (results) => results.any((r) => r != ConnectivityResult.none), - ); + bool isOnline(List rs) => + rs.any((r) => r != ConnectivityResult.none); + + yield isOnline(await c.checkConnectivity()); + yield* c.onConnectivityChanged.map(isOnline); });