Files
jinzhi/lib/features/settings/presentation/update_dialog.dart
T
2026-09-07 11:03:05 +08:00

126 lines
4.0 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../../common/theme/spacing.dart';
import '../application/update_progress.dart';
import '../application/update_service.dart';
import '../data/update_api_client.dart';
class UpdateDialog extends ConsumerStatefulWidget {
const UpdateDialog({super.key, required this.upgrade, required this.source});
final UpgradeInfo upgrade;
final UpgradeCheckSource source;
@override
ConsumerState<UpdateDialog> createState() => _UpdateDialogState();
}
class _UpdateDialogState extends ConsumerState<UpdateDialog> {
bool _clickReported = false;
@override
Widget build(BuildContext context) {
ensureUpdateProgressListener(context);
final colorScheme = ShadTheme.of(context).colorScheme;
final progress = ref.watch(updateProgressProvider);
final forced = widget.upgrade.force == 1;
final canStart = progress == null || progress <= 0 || progress == 1;
final version = widget.upgrade.latestVersion.trim().isEmpty
? widget.upgrade.versionCode
: widget.upgrade.latestVersion;
return PopScope(
canPop: !forced,
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.fromLTRB(22, 28, 22, 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'发现新版本 V$version',
textAlign: TextAlign.center,
style: TextStyle(
color: colorScheme.foreground,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
if (widget.upgrade.message.trim().isNotEmpty) ...[
const SizedBox(height: 12),
Text(
widget.upgrade.message,
style: TextStyle(
color: colorScheme.mutedForeground,
fontSize: 14,
height: 1.6,
),
),
],
const SizedBox(height: Spacing.sectionGap),
if (canStart)
FilledButton(
onPressed: () => _startUpdate(progress),
child: Text(progress == 1 ? '安装此版本' : '升级此版本'),
)
else
LinearProgressIndicator(
value: progress.clamp(0, 1),
color: colorScheme.primary,
backgroundColor: colorScheme.muted,
),
if (!forced && !canStart) ...[
const SizedBox(height: 10),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('转后台下载'),
),
],
if (!forced) ...[
const SizedBox(height: 6),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('稍后再说'),
),
],
],
),
),
),
);
}
Future<void> _startUpdate(double? progress) async {
if (!_clickReported) {
_clickReported = true;
unawaited(_reportClick());
}
final localPackage = ref.read(updateUrlProvider);
if (progress == 1 && localPackage != null && localPackage.isNotEmpty) {
await UpdateService.install(localPackage);
return;
}
await UpdateService.startUpdate(widget.upgrade.url);
}
Future<void> _reportClick() async {
if (widget.upgrade.versionCode.isEmpty) return;
try {
await ref
.read(updateApiClientProvider)
.reportClick(
buildId: widget.upgrade.versionCode,
source: widget.source == UpgradeCheckSource.manual
? 'manual'
: 'auto',
);
} catch (_) {}
}
}