import 'dart:developer'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:elysia/plugin/HTTP.dart'; import 'package:elysia/plugin/Remind.dart'; import 'package:flutter/material.dart'; import 'dart:async'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import '../../plugin/ApkInstaller.dart'; import '../../plugin/C.dart'; import 'TargetLog.dart'; class UpdateCheckPage extends StatefulWidget { @override _UpdateCheckPageState createState() => _UpdateCheckPageState(); } class _UpdateCheckPageState extends State { String _currentVersion = '0'; int _currentVersionCode = 0; bool _isChecking = false; bool _hasUpdate = false; String _latestVersion = '0'; String _changelog = ''; double _downloadProgress = 0.0; bool _isDownloading = false; int _downloadCode = 0; String _downloadUrl = ""; @override void initState() { super.initState(); _getVersionInfo(); Future.delayed(Duration(milliseconds: 200), () { _checkForUpdate(); }); } Future _checkForUpdate() async { setState(() { _isChecking = true; }); try { dynamic json = await HTTP.create("${C.BASE_URL}/server/latest-version").execute(); if (json["code"] != 200) { setState(() { _isChecking = false; _hasUpdate = false; }); } else { _hasUpdate = int.parse(json["data"]["versionCode"].toString()) > _currentVersionCode; if (_hasUpdate) { _latestVersion = json["data"]["version"]; _downloadCode = json["data"]["versionCode"]; _downloadUrl = json["data"]["url"]; _changelog = json["data"]["updateInfo"]; } setState(() { _isChecking = false; }); // ✅检查本地文件是否已存在 if (_hasUpdate) { bool exist = await _isLocalFileExist(_downloadCode); if (exist) { setState(() { _downloadProgress = 1.0; _isDownloading = false; }); } } } } catch (_) { setState(() { _isChecking = false; _hasUpdate = false; }); } } Future _startDownload() async { setState(() { _downloadProgress = 0.0; _isDownloading = true; }); try { final savePath = await _getLocalFilePath(_downloadCode); log(savePath); await Dio().download( _downloadUrl, savePath, onReceiveProgress: (received, total) { if (total != -1) { double progress = received / total; setState(() { _downloadProgress = progress; }); } }, ); setState(() { _isDownloading = false; _downloadProgress = 1.0; }); print('下载完成,文件保存在: $savePath'); _installApk(); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) { print('下载已取消'); } else { print('下载失败: $e'); } setState(() { _isDownloading = false; }); } } Future _installApk() async { final path = await _getLocalFilePath(_downloadCode); if (File(path).existsSync()) { Remind.show(context, "下载完成", "是否安装?", cancel: "取消", confirm: "现在安装", onTap: (s) async { if (!s) return; log(path); await ApkInstaller.installApk(path); }); } else { _startDownload(); } } Future _getLocalFilePath(int code) async { final directory = await getTemporaryDirectory(); String path = '${directory.path}/download/'; final Directory folder = Directory(path); if (!(await folder.exists())) { await folder.create(recursive: true); } return '${directory.path}/download/$code.apk'; } Future _isLocalFileExist(int code) async { final path = await _getLocalFilePath(code); return File(path).existsSync(); } Future _getVersionInfo() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); setState(() { _currentVersion = packageInfo.version; _currentVersionCode = int.tryParse(packageInfo.buildNumber) ?? 0; }); } static const Duration kFadeDuration = Duration(milliseconds: 300); static const Duration kSwitchDuration = Duration(milliseconds: 350); @override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { if (didPop) { return; } if (_isDownloading) { Remind.show(context, "退出", "退出后将取消下载,是否退出?", onTap: (s) { if (s) { Navigator.pop(context); } }); } else { Navigator.pop(context); } }, child: Scaffold( backgroundColor: Colors.grey[50], appBar: AppBar( backgroundColor: Colors.grey[100], elevation: 0, centerTitle: true, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black), onPressed: () { if (_isDownloading) { Remind.show(context, "退出", "退出后将取消下载,是否退出?", onTap: (s) { if (s) { Navigator.pop(context); } }); } else { Navigator.pop(context); } }, ), title: Text( "检查更新", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black), ), actions: [ Padding( padding: EdgeInsets.only(right: 10), child: GestureDetector( child: Icon(Icons.info_outline, color: Colors.black), onTap: () { Remind.showWidget(context, 'V${_currentVersion}(${_currentVersionCode})',TargetLog(),showCancel: false); }, ), ) ], ), body: SingleChildScrollView( child: Column( children: [ _buildStatusCard(), SizedBox(height: 16), AnimatedSwitcher( duration: kSwitchDuration, transitionBuilder: (child, animation) { return FadeTransition( opacity: animation, child: SlideTransition( position: Tween(begin: Offset(0, 0.03), end: Offset.zero).animate(animation), child: child, ), ); }, child: (_hasUpdate && !_isChecking) ? _buildUpdateContent(key: ValueKey('updateContent')) : SizedBox(key: ValueKey('noUpdateContent'), height: 0), ), AnimatedSwitcher( duration: kSwitchDuration, transitionBuilder: (child, animation) { return FadeTransition( opacity: animation, child: SizeTransition( axisAlignment: 1, sizeFactor: animation, child: child, ), ); }, child: _isDownloading ? _buildDownloadProgress(key: ValueKey('downloading')) : SizedBox(key: ValueKey('nodownload'), height: 0), ), ], ), ), ), ); } Widget _buildStatusCard() { return Container( margin: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 2))], ), child: Column( children: [ Container( padding: EdgeInsets.all(24), child: Column( children: [ Container( width: 80, height: 80, decoration: BoxDecoration( color: Colors.blue[50], borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.blue[100]!), ), child: Center( child: Image.asset('assets/app_icon.png', width: 70, height: 70, fit: BoxFit.cover), ), ), SizedBox(height: 16), Text('当前版本', style: TextStyle(fontSize: 14, color: Colors.grey[600])), SizedBox(height: 4), Text('V${_currentVersion}(${_currentVersionCode})', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87)), ], ), ), Divider(height: 1, color: Colors.grey[200]), Container( padding: EdgeInsets.all(16), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _isChecking ? '正在检查更新...' : _hasUpdate ? '发现新版本' : '已是最新版本', key: ValueKey(_isChecking ? 'checking' : (_hasUpdate ? 'hasUpdate' : 'noUpdate')), style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: _hasUpdate ? Colors.lightBlueAccent : Colors.lightBlueAccent), ), SizedBox(height: 4), Text( _isChecking ? '请稍等片刻' : _hasUpdate ? '立即体验新功能' : '您的应用已更新至最新版本', key: ValueKey(_isChecking ? 'checkingSub' : (_hasUpdate ? 'hasUpdateSub' : 'noUpdateSub')), style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), ], ), ), AnimatedSwitcher( duration: kSwitchDuration, transitionBuilder: (child, animation) { return FadeTransition(opacity: animation, child: ScaleTransition(scale: animation, child: child)); }, child: _isChecking ? SizedBox(width: 32, height: 32, key: ValueKey('checkingIndicator')) : (!_hasUpdate ? Icon(Icons.check_circle, color: Colors.lightBlueAccent, size: 32, key: ValueKey('checkIcon')) : Container( key: ValueKey('newBadge'), padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration(color: Colors.blue[50], borderRadius: BorderRadius.circular(12)), child: Text('NEW', style: TextStyle(color: Colors.lightBlueAccent, fontWeight: FontWeight.bold, fontSize: 12)), )), ), ], ), ), if (!_isChecking && !_isDownloading) Container( padding: EdgeInsets.fromLTRB(16, 0, 16, 16), child: AnimatedSwitcher( duration: kSwitchDuration, transitionBuilder: (child, animation) { return FadeTransition( opacity: animation, child: SlideTransition( position: Tween(begin: Offset(0, 0.05), end: Offset.zero).animate(animation), child: child)); }, child: ElevatedButton( key: ValueKey( _hasUpdate ? (_downloadProgress == 1.0 ? 'install' : 'update') : 'check', ), onPressed: _hasUpdate ? (_downloadProgress == 1.0 ? _installApk : _startDownload) : _checkForUpdate, style: ElevatedButton.styleFrom( backgroundColor: _hasUpdate ? Colors.lightBlueAccent : Colors.lightBlueAccent, foregroundColor: Colors.white, minimumSize: Size(double.infinity, 48), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), elevation: 0, ), child: Text( _hasUpdate ? (_downloadProgress == 1.0 ? '立即安装' : '立即更新') : '检查更新', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), ), ), ), ], ), ); } Widget _buildUpdateContent({Key? key}) { return Container( key: key, margin: EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 2))], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(20), child: Row( children: [ AnimatedOpacity(duration: kFadeDuration, opacity: 1.0, child: Icon(Icons.new_releases, color: Colors.lightBlueAccent, size: 20)), SizedBox(width: 8), Text('更新内容', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.black87)), ], ), ), Container( padding: EdgeInsets.symmetric(horizontal: 20), child: Row( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration(color: Colors.blue[50], borderRadius: BorderRadius.circular(8)), child: Text('V$_latestVersion', style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold)), ), SizedBox(width: 8), Text('${_changelog.split('\n').length}项更新', style: TextStyle(color: Colors.grey[600], fontSize: 14)), ], ), ), SizedBox(height: 16), AnimatedSize( duration: kSwitchDuration, curve: Curves.easeInOut, child: Container( padding: EdgeInsets.symmetric(horizontal: 20), child: Text(_changelog, style: TextStyle(fontSize: 15, height: 1.6, color: Colors.grey[700])), ), ), SizedBox(height: 20), ], ), ); } Widget _buildDownloadProgress({Key? key}) { return Container( key: key, margin: EdgeInsets.all(16), padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 2))], ), child: Column( children: [ Row( children: [ Icon(Icons.download, color: Colors.blue, size: 20), SizedBox(width: 8), Text('下载更新', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)), Spacer(), TweenAnimationBuilder( tween: Tween(begin: 0, end: _downloadProgress), duration: Duration(milliseconds: 300), builder: (context, value, child) { return Text('${(value * 100).toStringAsFixed(1)}%', style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold)); }, ), ], ), SizedBox(height: 16), TweenAnimationBuilder( tween: Tween(begin: 0, end: _downloadProgress), duration: Duration(milliseconds: 300), builder: (context, value, child) { return ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: value, backgroundColor: Colors.grey[200], valueColor: AlwaysStoppedAnimation(Colors.blue), minHeight: 8, ), ); }, ), SizedBox(height: 8), AnimatedSwitcher( duration: Duration(milliseconds: 250), child: Text( _downloadProgress < 1 ? '正在下载更新包...' : '下载完成!', key: ValueKey(_downloadProgress < 1), style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ), ], ), ); } }