123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- import 'dart:io';
- import 'package:app_installer/app_installer.dart';
- import 'package:dio/dio.dart';
- import 'package:eitc_erm_dental_flutter/exts.dart';
- import 'package:eitc_erm_dental_flutter/funcs.dart';
- import 'package:eitc_erm_dental_flutter/widget/custom_divider.dart';
- import 'package:eitc_erm_dental_flutter/widget/main_button.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_screenutil/flutter_screenutil.dart';
- import 'package:package_info_plus/package_info_plus.dart';
- import 'package:path_provider/path_provider.dart';
- import 'package:percent_indicator/percent_indicator.dart' as pi;
- import 'package:permission_handler/permission_handler.dart';
- import '../global.dart';
- import '../http/http.dart';
- ///app更新弹窗
- class AppUpdateDialog extends StatefulWidget {
- final String version;
- final String content;
- final String url;
- final bool isForce;
- const AppUpdateDialog(
- {super.key,
- required this.version,
- required this.content,
- required this.url,
- required this.isForce});
- @override
- State<AppUpdateDialog> createState() => _AppUpdateDialogState();
- }
- class _AppUpdateDialogState extends State<AppUpdateDialog> {
- _State state = _State.waiting;
- double _downloadProgress = 0.0;
- CancelToken? _downloadCancelToken;
- String _filePath = "";
- @override
- Widget build(BuildContext context) {
- return PopScope(
- //强制更新或下载时不允许返回关闭
- canPop: !widget.isForce && state != _State.downloading,
- child: Dialog(
- shape:
- RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r)),
- child: Padding(
- padding: EdgeInsets.all(20.r),
- child:
- state == _State.waiting ? _getUpdateContent() : _getDownload(),
- ),
- ));
- }
- ///获取更新内容
- Widget _getUpdateContent() {
- return Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- getS().hasNewVersion,
- style: context.titleMedium,
- ),
- Text("V${widget.version}"),
- SizedBox(
- height: 10.h,
- ),
- Text(widget.content),
- SizedBox(
- height: 10.h,
- ),
- Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Visibility(
- visible: !widget.isForce,
- child: Row(
- children: [
- MainButton(
- text: getS().nextTime,
- onPressed: _cancelUpdate,
- isOutlined: true,
- ),
- SizedBox(
- width: 15.w,
- )
- ],
- ),
- ),
- MainButton(text: getS().updateNow, onPressed: _startUpdate)
- ],
- ),
- )
- ],
- );
- }
- ///获取下载内容
- Widget _getDownload() {
- String title = switch (state) {
- _State.downloading => getS().downloading,
- _State.waiting => "",
- _State.downloadError => getS().downloadFailed,
- _State.downloadSuccess => getS().downloadComplete,
- };
- return Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- title,
- style: context.titleMedium,
- ),
- SizedBox(
- height: 10.h,
- ),
- pi.LinearPercentIndicator(
- percent: _downloadProgress,
- lineHeight: 15.h,
- progressColor: context.primaryColor,
- barRadius: Radius.circular(15.h),
- center: Text(
- "${(_downloadProgress * 100.0).round()}%",
- style: TextStyle(fontSize: 12.sp, color: Colors.white),
- ),
- ),
- SizedBox(
- height: 10.h,
- ),
- _getDownloadFuncButton(),
- ],
- );
- }
- ///获取下载功能按钮
- Widget _getDownloadFuncButton() {
- //如果是下载中且强制更新,就没有按钮
- if (state == _State.downloading && widget.isForce) {
- return SizedBox();
- }
- String text = switch (state) {
- _State.downloading => getS().cancel,
- _State.waiting => "",
- _State.downloadError => getS().close,
- _State.downloadSuccess => getS().install,
- };
- return Column(
- children: [
- const CustomDivider(),
- MainButton(text: text, onPressed: _onDownloadButtonFunc)
- ],
- );
- }
- ///取消更新
- void _cancelUpdate() {
- Navigator.pop(context);
- }
- ///开始更新
- void _startUpdate() async {
- PackageInfo packageInfo = await PackageInfo.fromPlatform();
- if (Platform.isIOS) {
- logd("app更新,ios打开appstore,appid=$iosAppId");
- AppInstaller.goStore(packageInfo.packageName, iosAppId);
- } else {
- //地址可能不是具体文件,所以不能根据后缀来判断
- if (widget.url.isEmpty) {
- logd("app更新,android的url为空,打开appstore,appid=${packageInfo.packageName}");
- AppInstaller.goStore(packageInfo.packageName, iosAppId);
- } else {
- logd("app更新,开始下载,url=${widget.url}");
- _startDownload(widget.url);
- }
- }
- }
- ///开始下载
- void _startDownload(String url) async {
- if (!url.endsWith(".apk")) {
- setState(() {
- state = _State.downloadError;
- });
- }
- List<Directory>? list = await getExternalCacheDirectories();
- if (list == null || list.isEmpty) {
- setState(() {
- state = _State.downloadError;
- });
- return;
- }
- _downloadCancelToken = CancelToken();
- Directory dir = Directory("${list.first.path}/update");
- if (!await dir.exists()) {
- try {
- dir = await dir.create();
- } catch (e) {
- loge("创建文件夹失败", error: e);
- setState(() {
- state = _State.downloadError;
- });
- return;
- }
- }
- String fileName = url.substring(url.lastIndexOf("/") + 1);
- if (fileName.isEmpty) {
- setState(() {
- state = _State.downloadError;
- });
- return;
- }
- _filePath = "${dir.path}/$fileName";
- logd("app更新,下载保存路径=$_filePath");
- try {
- File file = File(_filePath);
- if (await file.exists()) {
- await file.delete();
- }
- } catch (e) {
- loge("app更新,删除旧apk文件失败", error: e);
- }
- Http.instance.download(url, _filePath, cancelToken: _downloadCancelToken,
- onReceiveProgress: (downloaded, total) {
- setState(() {
- _downloadProgress = downloaded.toDouble() / total.toDouble();
- logd(
- "app更新,下载进度=$_downloadProgress,downloaded=$downloaded,total=$total");
- if (_downloadProgress >= 1) {
- _onDownloadComplete();
- }
- });
- });
- setState(() {
- state = _State.downloading;
- });
- }
- ///下载完毕
- void _onDownloadComplete() {
- if (state != _State.downloading) {
- return;
- }
- state = _State.downloadSuccess;
- logd("app更新,下载完毕");
- _installApk();
- }
- ///下载按钮功能
- void _onDownloadButtonFunc() {
- ///正在下载
- if (state == _State.downloading) {
- //取消下载
- _downloadCancelToken?.cancel();
- //关闭弹窗
- _cancelUpdate();
- }
- //下载失败
- else if (state == _State.downloadError) {
- //强制更新就退出程序
- if (widget.isForce) {
- exitApp();
- }
- //否则关闭弹窗
- else {
- _cancelUpdate();
- }
- }
- //下载成功
- else if (state == _State.downloadSuccess) {
- _installApk();
- }
- }
- ///安装apk
- void _installApk() async {
- if (!await Permission.requestInstallPackages.isGranted) {
- logd("app更新,没有安装apk权限,进行申请");
- if (!await Permission.requestInstallPackages.request().isGranted) {
- logd("app更新,申请安装apk权限被拒绝,终止安装");
- return;
- }
- }
- logd("app更新,安装apk,path=$_filePath");
- AppInstaller.installApk(_filePath);
- }
- }
- enum _State { waiting, downloading, downloadError, downloadSuccess }
|