import 'package:flutter/material.dart'; import '../models/app_version.dart'; import '../services/app_update_service.dart'; /// A reusable dialog that can render both flexible and forced updates and /// report download progress. Callers should wrap this with `showDialog` and /// control ``barrierDismissible`` according to ``info.isForceUpdate``. class UpdateDialog extends StatefulWidget { final AppUpdateInfo info; const UpdateDialog({required this.info, super.key}); @override State createState() => _UpdateDialogState(); } class _UpdateDialogState extends State { double _progress = 0; bool _downloading = false; bool _failed = false; Future _startDownload() async { setState(() { _downloading = true; _failed = false; }); try { await AppUpdateService.instance.downloadAndInstallApk( widget.info.latestVersion!.downloadUrl, onProgress: (p) => setState(() => _progress = p), ); // once the installer launches the app is likely to be stopped; we // don't pop the dialog explicitly. } catch (err) { if (!mounted) return; setState(() { _failed = true; _downloading = false; }); ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Download failed: $err'))); } } @override Widget build(BuildContext context) { final notes = widget.info.latestVersion?.releaseNotes ?? ''; // WillPopScope is deprecated in newer Flutter versions but PopScope // has a different API; to avoid breaking changes we continue to use the // old widget and suppress the warning. // ignore: deprecated_member_use return WillPopScope( onWillPop: () async { // prevent the user from dismissing when download is in progress or // when the dialog is forcing an update return !widget.info.isForceUpdate && !_downloading; }, child: AlertDialog( title: const Text('Update Available'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (notes.isNotEmpty) ...[Text(notes), const SizedBox(height: 12)], if (_downloading) Column( children: [ LinearProgressIndicator(value: _progress), const SizedBox(height: 8), Text('${(_progress * 100).toStringAsFixed(0)}%'), ], ), if (_failed) const Text( 'An error occurred while downloading. Please try again.', style: TextStyle(color: Colors.red), ), ], ), actions: _buildActions(), ), ); } List _buildActions() { if (_downloading) { // don't show any actions while the apk is being fetched return []; } if (widget.info.isForceUpdate) { return [ FilledButton( onPressed: _startDownload, child: const Text('Update Now'), ), ]; } return [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Later'), ), FilledButton(onPressed: _startDownload, child: const Text('Update Now')), ]; } }