openStore method
Resolves the store URL and attempts to open it, falling back to a constructed store-search URL when the remote value is unusable or fails to launch.
Implementation
Future<void> openStore() async {
if (!isClosed) emit(const ForceUpdateState(status: ForceUpdateStatus.opening));
try {
final String storeUrl = Platform.isAndroid
? _remoteConfig.getString(RemoteConfigKeys.storeUrlAndroid)
: _remoteConfig.getString(RemoteConfigKeys.storeUrlIos);
// Uri.tryParse accepts relative URIs, so also require isAbsolute to
// ensure the URL is actually launchable (has scheme + host).
final Uri? parsedUri = storeUrl.isNotEmpty ? Uri.tryParse(storeUrl) : null;
final bool hasValidRemoteUri = parsedUri != null && parsedUri.isAbsolute;
// Always build the package-specific fallback upfront — used when the
// remote URL is missing, relative, or fails to launch.
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final Uri fallbackUri = Platform.isAndroid
? Uri.https('play.google.com', '/store/apps/details', <String, String>{'id': packageInfo.packageName})
: Uri.https('apps.apple.com', '/search', <String, String>{'term': packageInfo.appName});
if (!hasValidRemoteUri) {
_logger.warn(
'[ForceUpdateCubit] Store URL from Remote Config is missing, malformed, or not absolute — using fallback',
extras: <String, Object?>{'original': storeUrl, 'fallback': fallbackUri.toString()},
escalate: true,
);
if (!await launchUrl(fallbackUri, mode: LaunchMode.externalApplication)) {
_logger.warn('[ForceUpdateCubit] Could not open fallback store URL', extras: <String, Object?>{'url': fallbackUri.toString()});
}
return;
}
if (!await launchUrl(parsedUri, mode: LaunchMode.externalApplication)) {
_logger.warn(
'[ForceUpdateCubit] Could not open remote store URL — retrying with fallback',
extras: <String, Object?>{'url': parsedUri.toString()},
);
if (!await launchUrl(fallbackUri, mode: LaunchMode.externalApplication)) {
_logger.warn('[ForceUpdateCubit] Could not open fallback store URL', extras: <String, Object?>{'url': fallbackUri.toString()});
}
}
} catch (e, st) {
// Non-fatal: PackageInfo.fromPlatform() or launchUrl() can throw
// (e.g. a platform-channel failure) rather than returning false. Log
// and surface a message on state rather than letting this propagate
// uncaught out of the button's onPressed handler.
_logger.error('[ForceUpdateCubit] Failed to open the store', error: e, stackTrace: st);
if (!isClosed) emit(state.copyWith(errorMessage: e.toString()));
} finally {
if (!isClosed) emit(state.copyWith(status: ForceUpdateStatus.idle));
}
}