import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../providers/update_provider.dart'; import '../../shared/app_info.dart'; import '../../shared/utils/format_utils.dart'; class UpdateDialog extends ConsumerStatefulWidget { const UpdateDialog({super.key}); @override ConsumerState createState() => _UpdateDialogState(); } class _UpdateDialogState extends ConsumerState { bool _installTriggered = false; @override Widget build(BuildContext context) { final state = ref.watch(updateProvider); final info = state.info; final appVersion = ref.watch(appVersionProvider).value ?? kSmPlayerVersionFallback; if (state.status == UpdateStatus.readyToInstall && !_installTriggered) { _installTriggered = true; WidgetsBinding.instance.addPostFrameCallback((_) async { final navigator = Navigator.of(context); await ref.read(updateProvider.notifier).install(); if (mounted && navigator.canPop()) { navigator.pop(); } }); } if (info == null) { return FDialog.adaptive( title: const Text('检查更新'), body: _ErrorContent(message: state.error ?? '没有可用的更新信息'), actions: [ FButton( onPress: () => ref.read(updateProvider.notifier).manualCheck(), child: const Text('重试'), ), FButton( onPress: () => Navigator.of(context).pop(), variant: FButtonVariant.outline, child: const Text('关闭'), ), ], ); } final isDownloading = state.status == UpdateStatus.downloading; final isReady = state.status == UpdateStatus.readyToInstall; final hasError = state.status == UpdateStatus.error; final noAsset = info.noAssetForPlatform; return FDialog.adaptive( title: Text('发现新版本 ${info.version}'), body: SizedBox( width: 460, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('当前版本 $appVersion → 最新版本 ${info.version}'), const SizedBox(height: 12), ConstrainedBox( constraints: const BoxConstraints(maxHeight: 220), child: SingleChildScrollView( child: Text( info.releaseNotes.trim().isEmpty ? '暂无更新说明' : info.releaseNotes.trim(), style: Theme.of(context).textTheme.bodySmall, ), ), ), if (isDownloading) ...[ const SizedBox(height: 16), FDeterminateProgress( value: downloadProgressValue( received: state.received, total: state.total, ) ?? 0, ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${formatDownloadBytes(state.received)} / ${formatDownloadBytes(state.total)}', style: Theme.of(context).textTheme.bodySmall, ), Text( formatDownloadSpeed(state.speedBytesPerSec), style: Theme.of(context).textTheme.bodySmall, ), ], ), ], if (isReady) ...[ const SizedBox(height: 16), const Text('安装程序已下载,正在打开。'), ], if (hasError) ...[ const SizedBox(height: 16), _ErrorContent(message: state.error ?? '更新失败'), ], ], ), ), actions: [ if (isDownloading) FButton( onPress: () => ref.read(updateProvider.notifier).cancel(), variant: FButtonVariant.outline, child: const Text('取消'), ) else ...[ FButton( onPress: () => Navigator.of(context).pop(), variant: FButtonVariant.outline, child: const Text('稍后'), ), if (hasError) FButton( onPress: () => ref.read(updateProvider.notifier).startDownload(), child: const Text('重试'), ) else if (noAsset) FButton( onPress: () => _openDownloadPage(info.htmlUrl), child: const Text('前往下载页'), ) else FButton( onPress: isReady ? null : () => ref.read(updateProvider.notifier).startDownload(), child: const Text('下载并安装'), ), ], ], ); } Future _openDownloadPage(Uri uri) async { if (!uri.hasScheme) return; await launchUrl(uri, mode: LaunchMode.externalApplication); } } class _ErrorContent extends StatelessWidget { final String message; const _ErrorContent({required this.message}); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.error_outline, size: 18, color: Theme.of(context).colorScheme.error, ), const SizedBox(width: 8), Flexible( child: Text( message, style: TextStyle(color: Theme.of(context).colorScheme.error), ), ), ], ); } }