Skip to content

Commit 12358a5

Browse files
committed
feat(ui): add update required page
- Implement UpdateRequiredPage widget for mandatory app updates - Add logic to launch app store URL for update - Display error message if update URL is unavailable - Use AppBloc to fetch remote config for platform-specific update URLs - Reuse InitialStateWidget for consistent UI
1 parent 5de474a commit 12358a5

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_bloc.dart';
4+
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/app_localizations.dart';
5+
import 'package:ui_kit/ui_kit.dart';
6+
import 'package:url_launcher/url_launcher.dart';
7+
8+
/// A page displayed to the user when a mandatory app update is required.
9+
///
10+
/// This page informs the user that they must update the application to the
11+
/// latest version to continue using it. It provides a button that links
12+
/// directly to the appropriate app store page by fetching the URL from the
13+
/// remote configuration.
14+
class UpdateRequiredPage extends StatelessWidget {
15+
/// {@macro update_required_page}
16+
const UpdateRequiredPage({super.key});
17+
18+
/// Attempts to launch the given URL in an external application (e.g., browser
19+
/// or app store).
20+
///
21+
/// Shows a [SnackBar] with an error message if the URL cannot be launched.
22+
Future<void> _launchUrl(BuildContext context, String url) async {
23+
// Ensure the URL is not empty before attempting to parse.
24+
if (url.isEmpty) {
25+
ScaffoldMessenger.of(context)
26+
..hideCurrentSnackBar()
27+
..showSnackBar(
28+
// TODO(fulleni): localize later.
29+
const SnackBar(content: Text('Update URL is not available.')),
30+
);
31+
return;
32+
}
33+
34+
final uri = Uri.parse(url);
35+
if (await canLaunchUrl(uri)) {
36+
// Launch the URL externally. This will open the App Store, Play Store,
37+
// or a browser.
38+
await launchUrl(uri, mode: LaunchMode.externalApplication);
39+
} else {
40+
// If the URL can't be launched, inform the user.
41+
ScaffoldMessenger.of(context)
42+
..hideCurrentSnackBar()
43+
..showSnackBar(
44+
SnackBar(content: Text('Could not open update URL: $url')),
45+
);
46+
}
47+
}
48+
49+
@override
50+
Widget build(BuildContext context) {
51+
final l10n = AppLocalizations.of(context);
52+
53+
// This is the robust, production-ready way to get the update URL.
54+
// It uses BlocProvider.of(context) to access the AppBloc instance and
55+
// determines the correct URL based on the current platform (iOS/Android).
56+
// It falls back to an empty string if the remote config is not available.
57+
final appBloc = BlocProvider.of<AppBloc>(context);
58+
final updateUrl = Theme.of(context).platform == TargetPlatform.android
59+
? appBloc.state.remoteConfig?.appStatus.androidUpdateUrl
60+
: appBloc.state.remoteConfig?.appStatus.iosUpdateUrl;
61+
62+
return Scaffold(
63+
body: Padding(
64+
padding: const EdgeInsets.all(AppSpacing.lg),
65+
child: Column(
66+
mainAxisAlignment: MainAxisAlignment.center,
67+
children: [
68+
// Reusing the InitialStateWidget for a consistent UI.
69+
InitialStateWidget(
70+
icon: Icons.system_update_alt,
71+
headline: l10n.updateRequiredHeadline,
72+
subheadline: l10n.updateRequiredSubheadline,
73+
),
74+
const SizedBox(height: AppSpacing.lg),
75+
// The button to direct the user to the app store.
76+
// It's disabled if the update URL is not available.
77+
ElevatedButton(
78+
onPressed: updateUrl != null && updateUrl.isNotEmpty
79+
? () => _launchUrl(context, updateUrl)
80+
: null,
81+
child: Text(l10n.updateRequiredButton),
82+
),
83+
],
84+
),
85+
),
86+
);
87+
}
88+
}

0 commit comments

Comments
 (0)