about.dart 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. import 'dart:io';
  2. import 'package:ctjt_flutter/common/states.dart';
  3. import 'package:ctjt_flutter/common/styles.dart';
  4. import 'package:ctjt_flutter/common/utils.dart';
  5. import 'package:ctjt_flutter/service/app_service.dart';
  6. import 'package:ctjt_flutter/widget/button.dart';
  7. import 'package:ctjt_flutter/widget/contact.dart';
  8. import 'package:ctjt_flutter/widget/title.dart';
  9. import 'package:flutter/material.dart';
  10. import 'package:flutter_screenutil/flutter_screenutil.dart';
  11. import 'package:fluttertoast/fluttertoast.dart';
  12. import 'package:path_provider/path_provider.dart';
  13. import 'package:permission_handler/permission_handler.dart';
  14. import 'package:provider/provider.dart';
  15. import 'package:r_upgrade/r_upgrade.dart';
  16. // 关于页(APP更新页)
  17. class AboutPage extends StatefulWidget {
  18. AboutPage({Key? key}) : super(key: key);
  19. @override
  20. _AboutPageState createState() => _AboutPageState();
  21. }
  22. class _AboutPageState extends State<AboutPage> {
  23. final _formKey = GlobalKey<FormState>();
  24. late AppService _appService;
  25. bool isLatest = false;
  26. static const String LoadingTip = '正在获取...';
  27. TextEditingController _latestVersionController =
  28. TextEditingController(text: LoadingTip);
  29. String _downloadUrl = "";
  30. String _versionDescription = "";
  31. @override
  32. void initState() {
  33. super.initState();
  34. _appService = AppService.getInstance(this.context);
  35. initVersion();
  36. }
  37. Future<Null> initVersion() async {
  38. // 在common中初始化时,已获取当前APP版本并缓存,本地APP版本相对稳定,此处不再获取
  39. // 从服务器获取手机APP新版本信息
  40. var localVersion =
  41. Provider.of<AppVersion>(context, listen: false).appVersion;
  42. var resp = await _appService.fetchVersionInfo(localVersion);
  43. if (null == resp ||
  44. resp.status != 1 ||
  45. (resp.targetVersion!.isNotEmpty &&
  46. resp.targetVersion!.trim() != localVersion)) {
  47. _latestVersionController.text = localVersion;
  48. isLatest = true;
  49. return;
  50. }
  51. setState(() {
  52. _downloadUrl = resp.url!;
  53. _versionDescription = resp.instructions!;
  54. var lv = Version(localVersion);
  55. var rv = Version(resp.lastVersion!);
  56. if (rv.moreThan(lv)) {
  57. _latestVersionController.text = '${lv.origin} -> ${rv.origin}';
  58. isLatest = false;
  59. } else {
  60. _latestVersionController.text = lv.origin;
  61. isLatest = true;
  62. }
  63. });
  64. }
  65. // 下载进度对话框
  66. void _showDownloadDialog() {
  67. showDialog(
  68. // 下载对话框
  69. context: context,
  70. barrierDismissible: false, // 点击遮罩不关闭
  71. builder: (context) {
  72. return SimpleDialog(
  73. title: Text('更新', style: TextStyle(color: Styles.primaryColor)),
  74. children: <Widget>[
  75. Offstage(
  76. // 更新说明
  77. offstage: _versionDescription.trim() != "",
  78. child: Container(
  79. height: 600.h,
  80. child: SingleChildScrollView(
  81. child: Text(_versionDescription.trim()),
  82. )),
  83. ),
  84. Container(
  85. // 进度条
  86. height: 200.h,
  87. alignment: Alignment.centerLeft,
  88. padding: EdgeInsets.only(
  89. left: 60.w,
  90. right: 60.w),
  91. child: Column(
  92. children: [
  93. Container(
  94. // 条状进度条
  95. margin: EdgeInsets.symmetric(
  96. horizontal: 50.w,
  97. vertical: 50.h),
  98. height: 25.h,
  99. child: LinearProgressIndicator(
  100. backgroundColor: Styles.primaryColor,
  101. value: Provider.of<AppDownloadProcess>(context)
  102. .process, //设置比例
  103. valueColor: AlwaysStoppedAnimation<Color>(Colors.black),
  104. // Navigator.of(mContext).pop(); // 下载完成关闭对话框
  105. ),
  106. ),
  107. Text('正在为您更新,请耐心等待。'),
  108. ],
  109. ),
  110. ),
  111. ]);
  112. },
  113. );
  114. }
  115. @override
  116. Widget build(BuildContext context) {
  117. return Form(
  118. key: _formKey,
  119. child: Scaffold(
  120. backgroundColor: Colors.white,
  121. appBar: PageHead(),
  122. body: Container(
  123. padding: Styles.primaryPadding,
  124. child: Column(
  125. crossAxisAlignment: CrossAxisAlignment.start,
  126. children: <Widget>[
  127. // 标题
  128. PageTitle(text: "关于"),
  129. Padding(
  130. padding: EdgeInsets.only(
  131. top: 75.h,
  132. bottom: 75.h),
  133. child: Text('最新应用软件版本:', style: Styles.configDescStyle),
  134. ),
  135. TextFormField(
  136. controller: _latestVersionController,
  137. readOnly: true,
  138. enabled: false,
  139. decoration: InputDecoration(
  140. suffixIcon: isLatest
  141. ? Icon(Icons.done,
  142. color: Styles.primaryColor, size: Styles.iconSize)
  143. : Icon(Icons.rotate_right,
  144. color: Styles.primaryColor, size: Styles.iconSize),
  145. ),
  146. ),
  147. Padding(
  148. padding: EdgeInsets.only(top: 25.h),
  149. child: Text(isLatest ? '您的APP是最新的' : '有新的APP版本,点击按钮立即更新',
  150. style: Styles.configDescStyle),
  151. ),
  152. // 更新按钮
  153. Offstage(
  154. offstage: isLatest,
  155. child: BigSButton(
  156. text: '更新',
  157. onPressed: () async {
  158. if (Platform.isIOS) {
  159. _launchToAppStore();
  160. } else if (Platform.isAndroid) {
  161. var hasPermission =
  162. await Utils.checkPermission(Permission.storage);
  163. if (hasPermission) {
  164. _installApk(
  165. _downloadUrl, _latestVersionController.text);
  166. _showDownloadDialog();
  167. } else {
  168. Fluttertoast.showToast(
  169. msg: "安装更新需要存储权限,该权限未打开",
  170. toastLength: Toast.LENGTH_LONG,
  171. gravity: ToastGravity.CENTER,
  172. timeInSecForIosWeb: 3);
  173. }
  174. } else {
  175. print('A platform witch unsupported.' +
  176. Platform.operatingSystem +
  177. ' ' +
  178. Platform.operatingSystemVersion);
  179. }
  180. },
  181. ),
  182. ),
  183. Offstage(
  184. offstage: Provider.of<UserStatus>(context).userToken.isEmpty,
  185. child: BigSButton(
  186. text: '退出登录',
  187. onPressed: () {
  188. States.reset(context);
  189. },
  190. ),
  191. ),
  192. TButton(
  193. child: Text("用户协议和隐私政策",
  194. style: TextStyle(color: Styles.linkColor)),
  195. onPressed: () {
  196. Contact.showUserContactDialog(context);
  197. },
  198. ),
  199. ],
  200. ),
  201. ),
  202. ),
  203. );
  204. }
  205. /// 跳转到App Store指定页,用于IOS
  206. Future<Null> _launchToAppStore() async {
  207. // 参数为APP Store中的应用ID,对应APP Store页面 https://apps.apple.com/cn/app/XXXX/id1234
  208. RUpgrade.upgradeFromAppStore('id1234')
  209. .then((result) {
  210. print('install apk $result');
  211. }).catchError((error) {
  212. print('install apk error: $error');
  213. });
  214. }
  215. /// 下载并安装APK,用于Android
  216. Future<Null> _installApk(String url, String version) async {
  217. Directory? storageDir = await getExternalStorageDirectory();
  218. if (null == storageDir) {
  219. print('can not get storage dir');
  220. _showDownloadErrorDialog();
  221. return;
  222. }
  223. String storagePath = storageDir.path;
  224. String fileName = '$storagePath/lyiew_v$version.apk';
  225. RUpgrade.stream.listen((DownloadInfo info) { // 下载进度监听,通过状态通知更新界面进度条
  226. Provider.of<AppDownloadProcess>(context, listen: false).process =
  227. info.percent!;
  228. });
  229. RUpgrade.upgrade(url, fileName: fileName, isAutoRequestInstall: true) // 下载并安装
  230. .then((result) {
  231. print('install apk $result');
  232. }).catchError((error) {
  233. print('install apk error: $error');
  234. });
  235. }
  236. // 下载失败提示对话框
  237. void _showDownloadErrorDialog() {
  238. showDialog(
  239. context: context,
  240. builder: (context) {
  241. return SimpleDialog(
  242. title: Text('提示', style: TextStyle(color: Styles.primaryColor)),
  243. children: <Widget>[
  244. Container(
  245. height: 200.h,
  246. alignment: Alignment.centerLeft,
  247. padding: EdgeInsets.only(
  248. left: 60.h,
  249. right: 60.h),
  250. child: Text('下载失败。'),
  251. ),
  252. Divider(),
  253. TButton(
  254. child: Text("确定",
  255. style: TextStyle(fontWeight: FontWeight.w700)),
  256. onPressed: () {
  257. // 关闭对话框
  258. Navigator.of(context).pop();
  259. })
  260. ]);
  261. });
  262. }
  263. }