-
Notifications
You must be signed in to change notification settings - Fork 266
Description
Description
Hi there,
We need guidance on how to reliably coordinate Amplify DataStore synchronization with our app’s navigation flow. We’ve observed two problematic scenarios:
- App cold start (user already signed in):
-
On launch, we show the native splash screen until the DataStore emits its ready event, then navigate to the Home screen and immediately run our DataStore queries.
-
Occasionally, despite receiving ready, our initial queries return empty lists. Manually pulling to refresh then populates the data, suggesting we’re querying too early.
Our current approach for the cold start:
bool _userWasSignedInAtLaunch = false;
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await _configureAmplify();
final session = await Amplify.Auth.fetchAuthSession(
options: const FetchAuthSessionOptions(forceRefresh: false),
);
_userWasSignedInAtLaunch = session.isSignedIn;
if (!_userWasSignedInAtLaunch) {
FlutterNativeSplash.remove();
}
runApp(
MyApp(),
);
}
Future<void> _configureAmplify() async {
try {
final auth = AmplifyAuthCognito();
final datastorePlugin =
AmplifyDataStore(modelProvider: ModelProvider.instance);
final api = AmplifyAPI(
options: APIPluginOptions(modelProvider: ModelProvider.instance),
);
final storage = AmplifyStorageS3();
if (Amplify.isConfigured == false) {
await Amplify.addPlugins([datastorePlugin, auth, api, storage]);
}
await Amplify.configure(amplifyconfig);
Amplify.Hub.listen(HubChannel.DataStore, (hubEvent) {
if (hubEvent.eventName == 'ready') {
if (_userWasSignedInAtLaunch) {
FlutterNativeSplash.remove();
}
}
});
Amplify.Hub.listen(HubChannel.Auth, (hubEvent) async {
switch (hubEvent.type) {
case AuthHubEventType.signedIn:
safePrint('User is signed in.');
goRouter.go('/splash');
break;
case AuthHubEventType.signedOut:
safePrint('User is signed out.');
break;
case AuthHubEventType.sessionExpired:
safePrint('The session has expired.');
break;
case AuthHubEventType.userDeleted:
safePrint('The user has been deleted.');
break;
}
});
safePrint("Amplify has been configured successfully.");
} on Exception catch (e) {
safePrint('An error occurred configuring Amplify: $e');
}
}
- Login flow (user signed out → signed in) on unreliable networks:
-
Immediately after a successful sign-in, we route to a loading screen and kick off a full DataStore sync. We deliberately keep the user stuck on that screen until sync completes, because we don’t want to show an empty Home.
-
Problem: On a poor connection, the sync can stall indefinitely, trapping the user on the loading screen. The only workaround currently is to restart the app—since they’re still authenticated, the app jumps straight to Home, but then shows a completely empty UI even though data exists in the cloud.
-
This exposes that our “wait-for-ready” approach isn’t robust: neither retrying nor back-off logic is in place, and no fallback path exists to recover when sync never finishes.
Listen to the signedIn Event
Amplify.Hub.listen(HubChannel.Auth, (hubEvent) async {
switch (hubEvent.type) {
case AuthHubEventType.signedIn:
safePrint('User is signed in.');
goRouter.go('/loadingScreen');
break;
Within the loading widget/screen the trigger the following:
class DataStoreSyncService {
Future<void> syncAfterSignIn() async {
final completer = Completer<void>();
final sub = Amplify.Hub.listen(HubChannel.DataStore, (hubEvent) {
if ((hubEvent.eventName == 'ready') && !completer.isCompleted) {
completer.complete();
}
});
await Amplify.DataStore.clear();
await Amplify.DataStore.start();
await completer.future;
await sub.cancel();
}
}
What’s the recommended way to detect and wait for a truly complete initial sync, beyond listening for the ready event?
How can we implement retry/back-off or timeout logic so that users aren’t stranded on a loading screen forever?
Can we leverage additional Hub events (e.g. network status changes) or DataStore APIs (like observeQuery().isSynced) to build a more resilient loading flow and fallback UI?
We’ve tried following the Amplify docs examples, but still run into this race condition on slow networks. Any pointers, sample patterns, or code snippets would be greatly appreciated. Thanks!
Categories
- Analytics
- API (REST)
- API (GraphQL)
- Auth
- Authenticator
- DataStore
- Notifications (Push)
- Storage
Steps to Reproduce
- Start App + DataStore
- Wait until DataStore is ready
- Navigate to homescreen and fetch data
Or.
- Login Screen -> Login
- Switch to loading to sync DataStore and wait until ready
- Navigate to homescreen and fetch data
Screenshots
No response
Platforms
- iOS
- Android
- Web
- macOS
- Windows
- Linux
Flutter Version
3.29.3
Amplify Flutter Version
2.6.0
Deployment Method
Amplify CLI (Gen 1)