From bd77beb60a2babb41c1bd4aac025acbc15a05ee8 Mon Sep 17 00:00:00 2001 From: jyuesong <425698907@qq.com> Date: Mon, 30 May 2022 14:02:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5ios=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/base/routes.dart | 3 +- lib/base/theme.dart | 25 +- lib/module/env/add_env_page.dart | 2 +- lib/module/env/env_bean.dart | 6 + lib/module/home/home_page.dart | 14 - lib/module/login/login_page.dart | 14 - lib/module/others/about_page.dart | 14 - .../others/scripts/script_edit_page.dart | 50 ++- lib/module/others/scripts/script_page.dart | 375 ++++++++++++---- .../others/task_log/task_log_detail_page.dart | 21 +- lib/module/task/add_task_page.dart | 2 +- lib/module/task/task_bean.dart | 34 +- lib/module/task/task_page.dart | 1 - lib/utils/update_utils.dart | 399 ------------------ 14 files changed, 412 insertions(+), 548 deletions(-) delete mode 100644 lib/utils/update_utils.dart diff --git a/lib/base/routes.dart b/lib/base/routes.dart index 15e3616..8200f7f 100644 --- a/lib/base/routes.dart +++ b/lib/base/routes.dart @@ -105,7 +105,8 @@ class Routes { case routeTaskLogDetail: return CupertinoPageRoute( builder: (context) => TaskLogDetailPage( - title: settings.arguments as String, + title: (settings.arguments as Map)['title'], + path: (settings.arguments as Map)['path'], ), ); case routeScriptDetail: diff --git a/lib/base/theme.dart b/lib/base/theme.dart index b68ec53..c95b8aa 100644 --- a/lib/base/theme.dart +++ b/lib/base/theme.dart @@ -10,7 +10,7 @@ import 'package:qinglong_app/utils/sp_utils.dart'; var themeProvider = ChangeNotifierProvider((ref) => ThemeViewModel()); -const commonColor = Color(0xFF299343); +Color commonColor = const Color(0xFF299343); Color _primaryColor = commonColor; class ThemeViewModel extends ChangeNotifier { @@ -71,7 +71,10 @@ class ThemeViewModel extends ChangeNotifier { ), scaffoldBackgroundColor: const Color(0xfff5f5f5), inputDecorationTheme: InputDecorationTheme( - labelStyle: TextStyle(color: _primaryColor), + labelStyle: TextStyle( + color: _primaryColor, + fontSize: 14, + ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: _primaryColor, @@ -152,7 +155,10 @@ class ThemeViewModel extends ChangeNotifier { ), ), inputDecorationTheme: InputDecorationTheme( - labelStyle: TextStyle(color: _primaryColor), + labelStyle: TextStyle( + color: _primaryColor, + fontSize: 14, + ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: _primaryColor, @@ -208,12 +214,15 @@ class ThemeViewModel extends ChangeNotifier { abstract class ThemeColors { Color settingBgColor(); + Color settingBordorColor(); Color titleColor(); Color descColor(); + Color tabBarColor(); + Color pinColor(); Color buttonBgColor(); @@ -256,6 +265,11 @@ class LightThemeColors extends ThemeColors { Color settingBordorColor() { return Colors.white; } + + @override + Color tabBarColor() { + return const Color(0xffF7F7F7); + } } class DartThemeColors extends ThemeColors { @@ -293,4 +307,9 @@ class DartThemeColors extends ThemeColors { Color settingBordorColor() { return Color(0xff333333); } + + @override + Color tabBarColor() { + return Colors.black; + } } diff --git a/lib/module/env/add_env_page.dart b/lib/module/env/add_env_page.dart index 339b86a..18c34de 100644 --- a/lib/module/env/add_env_page.dart +++ b/lib/module/env/add_env_page.dart @@ -187,7 +187,7 @@ class _AddEnvPageState extends ConsumerState { envBean.remarks = _remarkController.text; HttpResponse response = await Api.addEnv( _nameController.text, _valueController.text, _remarkController.text, - id: envBean.sId); + id: envBean.id,nId: envBean.nId,); if (response.success) { (envBean.sId == null) ? "新增成功" : "修改成功".toast(); diff --git a/lib/module/env/env_bean.dart b/lib/module/env/env_bean.dart index 87dfb45..d149410 100644 --- a/lib/module/env/env_bean.dart +++ b/lib/module/env/env_bean.dart @@ -4,6 +4,8 @@ import 'package:json_conversion_annotation/json_conversion_annotation.dart'; class EnvBean { String? value; String? sId; + String? _id; + int? id; int? created; int? status; String? timestamp; @@ -12,8 +14,12 @@ class EnvBean { EnvBean({this.value, this.sId, this.created, this.status, this.timestamp, this.name, this.remarks}); + get nId => _id; + EnvBean.fromJson(Map json) { value = json['value']; + id = json['id']; + _id = json['_id']; sId = json.containsKey('_id') ? json['_id'].toString() : (json.containsKey('id') ? json['id'].toString() : ""); created = int.tryParse(json['created'].toString()); status = json['status']; diff --git a/lib/module/home/home_page.dart b/lib/module/home/home_page.dart index d960c49..ae3eb04 100644 --- a/lib/module/home/home_page.dart +++ b/lib/module/home/home_page.dart @@ -8,11 +8,9 @@ import 'package:qinglong_app/base/ql_app_bar.dart'; import 'package:qinglong_app/base/routes.dart'; import 'package:qinglong_app/module/config/config_page.dart'; import 'package:qinglong_app/module/env/env_page.dart'; -import 'package:qinglong_app/module/home/system_bean.dart'; import 'package:qinglong_app/module/others/other_page.dart'; import 'package:qinglong_app/module/task/task_page.dart'; import 'package:move_to_background/move_to_background.dart'; -import 'package:qinglong_app/utils/update_utils.dart'; import 'dart:math' as math; import '../../utils/utils.dart'; @@ -47,7 +45,6 @@ class _HomePageState extends ConsumerState _title = titles[0].title; super.initState(); WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { - update(); getSystemBean(); }); } @@ -243,17 +240,6 @@ class _HomePageState extends ConsumerState ); } - void update() async { - String? result = await UpdateUtils().checkUpdate(); - if (result != null && result.isNotEmpty) { - UpdateDialog updateDialog = UpdateDialog(context, - title: "发现新版本", updateContent: "版本号:v$result", onUpdate: () { - UpdateUtils.launchURL(result); - }); - updateDialog.show(); - } - } - void isNewYearDuration() { DateTime date = DateTime.now(); DateTime dateYear1 = DateTime(2022, 1, 29); diff --git a/lib/module/login/login_page.dart b/lib/module/login/login_page.dart index aac0efb..ce6444d 100644 --- a/lib/module/login/login_page.dart +++ b/lib/module/login/login_page.dart @@ -3,16 +3,13 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:qinglong_app/base/http/api.dart'; import 'package:qinglong_app/base/http/http.dart'; import 'package:qinglong_app/base/routes.dart'; import 'package:qinglong_app/base/theme.dart'; import 'package:qinglong_app/base/userinfo_viewmodel.dart'; import 'package:qinglong_app/main.dart'; -import 'package:qinglong_app/module/login/user_bean.dart'; import 'package:qinglong_app/utils/extension.dart'; import 'package:qinglong_app/utils/login_helper.dart'; -import 'package:qinglong_app/utils/update_utils.dart'; import 'package:qinglong_app/utils/utils.dart'; import 'package:flip_card/flip_card.dart'; @@ -66,7 +63,6 @@ class _LoginPageState extends ConsumerState { } getIt().updateToken(""); WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { - update(); if (useSecretLogin) { cardKey.currentState?.toggleCard(); } @@ -522,16 +518,6 @@ class _LoginPageState extends ConsumerState { }); } - void update() async { - String? result = await UpdateUtils().checkUpdate(); - if (result != null && result.isNotEmpty) { - UpdateDialog updateDialog = UpdateDialog(context, title: "发现新版本", updateContent: "版本号:v${result}", onUpdate: () { - UpdateUtils.launchURL(result); - }); - updateDialog.show(); - } - } - Widget buildCell(UserInfoBean bean) { return ListTile( title: Text( diff --git a/lib/module/others/about_page.dart b/lib/module/others/about_page.dart index ba2a206..78d852e 100644 --- a/lib/module/others/about_page.dart +++ b/lib/module/others/about_page.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:qinglong_app/base/ql_app_bar.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:qinglong_app/base/theme.dart'; -import 'package:qinglong_app/utils/update_utils.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../main.dart'; @@ -25,19 +24,6 @@ class _AboutPageState extends ConsumerState { super.initState(); getInfo(); - WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { - update(); - }); - } - - void update() async { - String? result = await UpdateUtils().checkUpdate(true); - if (result != null && result.isNotEmpty) { - UpdateDialog updateDialog = UpdateDialog(context, title: "发现新版本", updateContent: "版本号:v${result}", onUpdate: () { - UpdateUtils.launchURL(result); - }); - updateDialog.show(); - } } @override diff --git a/lib/module/others/scripts/script_edit_page.dart b/lib/module/others/scripts/script_edit_page.dart index 4cc1b2c..2ef643a 100644 --- a/lib/module/others/scripts/script_edit_page.dart +++ b/lib/module/others/scripts/script_edit_page.dart @@ -2,6 +2,11 @@ import 'package:code_text_field/code_text_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:highlight/languages/javascript.dart'; +import 'package:highlight/languages/json.dart'; +import 'package:highlight/languages/powershell.dart'; +import 'package:highlight/languages/python.dart'; +import 'package:highlight/languages/vbscript-html.dart'; +import 'package:highlight/languages/yaml.dart'; import 'package:qinglong_app/base/http/api.dart'; import 'package:qinglong_app/base/http/http.dart'; import 'package:qinglong_app/base/ql_app_bar.dart'; @@ -41,11 +46,32 @@ class _ScriptEditPageState extends ConsumerState { }); } + getLanguageType(String title) { + if (title.endsWith(".js")) { + return javascript; + } + + if (title.endsWith(".sh")) { + return powershell; + } + + if (title.endsWith(".py")) { + return python; + } + if (title.endsWith(".json")) { + return json; + } + if (title.endsWith(".yaml")) { + return yaml; + } + return vbscriptHtml; + } + @override Widget build(BuildContext context) { _codeController ??= CodeController( text: widget.content, - language: javascript, + language: getLanguageType(widget.title), onChange: (value) { result = value; }, @@ -91,10 +117,24 @@ class _ScriptEditPageState extends ConsumerState { ), body: SafeArea( top: false, - child: CodeField( - controller: _codeController!, - expands: true, - background: ref.watch(themeProvider).themeColor.settingBgColor(), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), + child: CodeField( + controller: _codeController!, + expands: true, + wrap: true, + lineNumberStyle: LineNumberStyle( + width: 0, + margin: 0, + textStyle: TextStyle( + color: ref.watch(themeProvider).themeColor.descColor(), + ), + ), + background: ref.watch(themeProvider).themeColor.tabBarColor(), + ), ), ), ); diff --git a/lib/module/others/scripts/script_page.dart b/lib/module/others/scripts/script_page.dart index d56918a..34c3425 100644 --- a/lib/module/others/scripts/script_page.dart +++ b/lib/module/others/scripts/script_page.dart @@ -20,6 +20,18 @@ class ScriptPage extends ConsumerStatefulWidget { class _ScriptPageState extends ConsumerState with LazyLoadState { List list = []; + final TextEditingController _searchController = TextEditingController(); + + final TextEditingController _nameController = TextEditingController(); + String? path; + + @override + void initState() { + super.initState(); + _searchController.addListener(() { + setState(() {}); + }); + } @override Widget build(BuildContext context) { @@ -30,83 +42,167 @@ class _ScriptPageState extends ConsumerState with LazyLoadState ListTile( - onTap: () { - Navigator.of(context).pushNamed( - Routes.routeScriptDetail, - arguments: { - "title": e.title, - "path": e.parent, - }, - ).then((value) { - if (value != null && value == true) { - loadData(); - } - }); - }, - title: Text( - e.title ?? "", - style: TextStyle( - color: (item.disabled ?? false) - ? ref.watch(themeProvider).themeColor.descColor() - : ref.watch(themeProvider).themeColor.titleColor(), - fontSize: 14, - ), - ), - )) - .toList(), - ) - : ListTile( - onTap: () { - Navigator.of(context).pushNamed( - Routes.routeScriptDetail, - arguments: { - "title": item.title, - "path": "", - }, - ).then( - (value) { - if (value != null && value == true) { - loadData(); - } - }, - ); - }, - title: Text( - item.title ?? "", - style: TextStyle( - color: - (item.disabled ?? false) ? ref.watch(themeProvider).themeColor.descColor() : ref.watch(themeProvider).themeColor.titleColor(), - fontSize: 16, - ), - ), - ), - ); - }, - itemCount: list.length, - ), + ScriptBean item = list[index - 1]; + + if (_searchController.text.isEmpty || + (item.title?.contains(_searchController.text) ?? false) || + (item.value?.contains(_searchController.text) ?? false) || + ((item.children?.where((e) { + return (e.title?.contains(_searchController.text) ?? false) || (e.value?.contains(_searchController.text) ?? false); + }).isNotEmpty ?? + false))) { + return ColoredBox( + color: ref.watch(themeProvider).themeColor.settingBgColor(), + child: (item.children != null && item.children!.isNotEmpty) + ? ExpansionTile( + title: Text( + item.title ?? "", + style: TextStyle( + color: (item.disabled ?? false) + ? ref.watch(themeProvider).themeColor.descColor() + : ref.watch(themeProvider).themeColor.titleColor(), + fontSize: 16, + ), + ), + children: item.children! + .where((element) { + if (_searchController.text.isEmpty) { + return true; + } + return (element.title?.contains(_searchController.text) ?? false) || + (element.value?.contains(_searchController.text) ?? false); + }) + .map((e) => ListTile( + onTap: () { + Navigator.of(context).pushNamed( + Routes.routeScriptDetail, + arguments: { + "title": e.title, + "path": e.parent, + }, + ).then((value) { + if (value != null && value == true) { + loadData(); + } + }); + }, + title: Text( + e.title ?? "", + style: TextStyle( + color: (item.disabled ?? false) + ? ref.watch(themeProvider).themeColor.descColor() + : ref.watch(themeProvider).themeColor.titleColor(), + fontSize: 14, + ), + ), + )) + .toList(), + ) + : ListTile( + onTap: () { + Navigator.of(context).pushNamed( + Routes.routeScriptDetail, + arguments: { + "title": item.title, + "path": "", + }, + ).then( + (value) { + if (value != null && value == true) { + loadData(); + } + }, + ); + }, + title: Text( + item.title ?? "", + style: TextStyle( + color: (item.disabled ?? false) + ? ref.watch(themeProvider).themeColor.descColor() + : ref.watch(themeProvider).themeColor.titleColor(), + fontSize: 16, + ), + ), + ), + ); + } else { + return const SizedBox.shrink(); + } + }, + itemCount: list.length + 1, + ), + ); + } + + Widget searchCell(WidgetRef context) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 10, + ), + child: CupertinoSearchTextField( + onSubmitted: (value) { + setState(() {}); + }, + onSuffixTap: () { + _searchController.text = ""; + setState(() {}); + }, + controller: _searchController, + borderRadius: BorderRadius.circular( + 30, + ), + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 5, + ), + suffixInsets: const EdgeInsets.only( + right: 15, + ), + prefixInsets: const EdgeInsets.only( + top: 6, + bottom: 6, + left: 15, + ), + placeholderStyle: TextStyle( + fontSize: 16, + color: context.watch(themeProvider).themeColor.descColor(), + ), + style: const TextStyle( + fontSize: 16, + ), + placeholder: "搜索", + ), ); } @@ -129,4 +225,139 @@ class _ScriptPageState extends ConsumerState with LazyLoadState CupertinoAlertDialog( + title: const Text("新增脚本"), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + const Text( + "脚本名称:", + style: TextStyle( + fontSize: 14, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: TextField( + controller: _nameController, + decoration: const InputDecoration( + isDense: true, + contentPadding: EdgeInsets.all(4), + hintText: "请输入脚本名称", + hintStyle: TextStyle( + fontSize: 14, + ), + ), + autofocus: false, + ), + ), + const SizedBox( + height: 10, + ), + const Text( + "脚本所属文件夹:", + style: TextStyle( + fontSize: 14, + ), + ), + const SizedBox( + height: 10, + ), + DropdownButtonFormField( + items: list + .where((element) => element.children?.isNotEmpty ?? false) + .map((e) => DropdownMenuItem( + value: e.value, + child: SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: Text( + e.value ?? "", + maxLines: 2, + ), + ), + )) + .toList() + ..insert( + 0, + DropdownMenuItem( + value: "", + child: SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: const Text( + "根目录", + maxLines: 2, + ), + ), + )), + value: scriptPath, + onChanged: (value) { + scriptPath = value ?? ""; + }, + ), + ], + ), + actions: [ + CupertinoDialogAction( + child: const Text( + "取消", + style: TextStyle( + color: Color(0xff999999), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + CupertinoDialogAction( + child: Text( + "确定", + style: TextStyle( + color: ref.watch(themeProvider).primaryColor, + ), + ), + onPressed: () async { + "提交中...".toast(); + HttpResponse response = await Api.addScript( + _nameController.text, + scriptPath, + "## created by 青龙客户端 ${DateTime.now()}\n\n", + ); + if (response.success) { + "提交成功".toast(); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + Routes.routeScriptUpdate, + arguments: { + "title": _nameController.text, + "path": scriptPath, + "content": "## created by 青龙客户端 ${DateTime.now()}\n\n", + }, + ).then((value) { + if (value != null && value == true) { + _nameController.text = ""; + loadData(); + } + }); + } else { + (response.message ?? "").toast(); + } + }, + ), + ], + ), + ); + } } diff --git a/lib/module/others/task_log/task_log_detail_page.dart b/lib/module/others/task_log/task_log_detail_page.dart index faa9701..dfcd3b6 100644 --- a/lib/module/others/task_log/task_log_detail_page.dart +++ b/lib/module/others/task_log/task_log_detail_page.dart @@ -12,10 +12,12 @@ import 'package:qinglong_app/utils/extension.dart'; /// @author NewTab class TaskLogDetailPage extends ConsumerStatefulWidget { final String title; + final String path; const TaskLogDetailPage({ Key? key, required this.title, + required this.path, }) : super(key: key); @override @@ -38,21 +40,22 @@ class _TaskLogDetailPageState extends ConsumerState with Lazy body: content == null ? const Center(child: CupertinoActivityIndicator()) : Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15, - ), - child: SelectableText( - (content == null || content!.isEmpty) ? "暂无数据" : content!, - selectionHeightStyle: BoxHeightStyle.max, - selectionWidthStyle: BoxWidthStyle.max, - ), - ), + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: SelectableText( + (content == null || content!.isEmpty) ? "暂无数据" : content!, + selectionHeightStyle: BoxHeightStyle.max, + selectionWidthStyle: BoxWidthStyle.max, + ), + ), ); } Future loadData() async { HttpResponse response = await Api.taskLogDetail( widget.title, + widget.path, ); if (response.success) { diff --git a/lib/module/task/add_task_page.dart b/lib/module/task/add_task_page.dart index 5f6c575..d83a419 100644 --- a/lib/module/task/add_task_page.dart +++ b/lib/module/task/add_task_page.dart @@ -206,7 +206,7 @@ class _AddTaskPageState extends ConsumerState { taskBean.schedule = _cronController.text; HttpResponse response = await Api.addTask( _nameController.text, _commandController.text, _cronController.text, - id: taskBean.sId); + id: taskBean.id,); if (response.success) { (widget.taskBean?.sId == null) ? "新增成功" : "修改成功".toast(); diff --git a/lib/module/task/task_bean.dart b/lib/module/task/task_bean.dart index bc56a25..97a9c3a 100644 --- a/lib/module/task/task_bean.dart +++ b/lib/module/task/task_bean.dart @@ -11,6 +11,8 @@ class TaskBean { String? schedule; bool? saved; String? sId; + int? id; + String? _id; int? created; int? status; String? timestamp; @@ -24,20 +26,22 @@ class TaskBean { TaskBean( {this.name, - this.command, - this.schedule, - this.saved, - this.sId, - this.created, - this.status, - this.timestamp, - this.isSystem, - this.isDisabled, - this.logPath, - this.isPinned, - this.lastExecutionTime, - this.lastRunningTime, - this.pid}); + this.command, + this.schedule, + this.saved, + this.sId, + this.created, + this.status, + this.timestamp, + this.isSystem, + this.isDisabled, + this.logPath, + this.isPinned, + this.lastExecutionTime, + this.lastRunningTime, + this.pid}); + + get nId => _id; TaskBean.fromJson(Map json) { try { @@ -45,6 +49,8 @@ class TaskBean { command = json['command'].toString(); schedule = json['schedule'].toString(); saved = json['saved']; + id = json['id']; + _id = json['_id']; sId = json.containsKey('_id') ? json['_id'].toString() : (json.containsKey('id') ? json['id'].toString() : ""); created = int.tryParse(json['created'].toString()); status = json['status']; diff --git a/lib/module/task/task_page.dart b/lib/module/task/task_page.dart index b5145bd..e2b153a 100644 --- a/lib/module/task/task_page.dart +++ b/lib/module/task/task_page.dart @@ -6,7 +6,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:qinglong_app/base/base_state_widget.dart'; import 'package:qinglong_app/base/routes.dart'; -import 'package:qinglong_app/base/single_account_page.dart'; import 'package:qinglong_app/base/theme.dart'; import 'package:qinglong_app/module/task/intime_log/intime_log_page.dart'; import 'package:qinglong_app/module/task/task_bean.dart'; diff --git a/lib/utils/update_utils.dart b/lib/utils/update_utils.dart deleted file mode 100644 index 3e54921..0000000 --- a/lib/utils/update_utils.dart +++ /dev/null @@ -1,399 +0,0 @@ -import 'dart:io'; -import 'dart:math'; - -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:qinglong_app/base/http/url.dart'; -import 'package:qinglong_app/utils/extension.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../main.dart'; - -/// @author NewTab - -class UpdateUtils { - Future checkUpdate([bool remind = false]) async { - try { - if (!Platform.isAndroid) { - return null; - } - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - - String version = packageInfo.version; - String url = Url.checkUpdateUrl; - Dio dio = Dio(BaseOptions( - connectTimeout: 50000, - receiveTimeout: 50000, - sendTimeout: 50000, - )); - var response = await dio.get(url); - if (response.statusCode == 200) { - if (version == response.data as String) { - return null; - } - return response.data as String; - } - } on DioError catch (e) { - if (remind) { - "无法连接到github服务器,版本更新检测失败".toast(); - } - logger.e(e); - return null; - } - } - - static void launchURL(String version) async { - try { - await launch("https://github.com/qinglong-app/qinglong_app/releases/download/v$version/app-release-v$version.apk"); - } catch (e) { - logger.e(e); - } - } -} - -///版本更新加提示框 -class UpdateDialog { - bool _isShowing = false; - late BuildContext _context; - late UpdateWidget _widget; - - UpdateDialog(BuildContext context, - {double width = 0.0, - required String title, - required String updateContent, - required VoidCallback onUpdate, - double titleTextSize = 16.0, - double contentTextSize = 14.0, - double buttonTextSize = 14.0, - double progress = -1.0, - Color progressBackgroundColor = const Color(0xFFFFCDD2), - Image? topImage, - double extraHeight = 5.0, - double radius = 4.0, - Color themeColor = Colors.red, - bool enableIgnore = false, - VoidCallback? onIgnore, - bool isForce = false, - String? updateButtonText, - String? ignoreButtonText, - VoidCallback? onClose}) { - _context = context; - _widget = UpdateWidget( - width: width, - title: title, - updateContent: updateContent, - onUpdate: onUpdate, - titleTextSize: titleTextSize, - contentTextSize: contentTextSize, - buttonTextSize: buttonTextSize, - progress: progress, - topImage: topImage, - extraHeight: extraHeight, - radius: radius, - themeColor: themeColor, - progressBackgroundColor: progressBackgroundColor, - enableIgnore: enableIgnore, - onIgnore: onIgnore, - isForce: isForce, - updateButtonText: updateButtonText ?? '更新', - ignoreButtonText: ignoreButtonText ?? '忽略此版本', - onClose: onClose ?? () => dismiss()); - } - - /// 显示弹窗 - Future show() { - try { - if (isShowing()) { - return Future.value(false); - } - showDialog( - context: _context, - barrierDismissible: false, - builder: (BuildContext context) { - return WillPopScope(onWillPop: () => Future.value(false), child: _widget); - }); - _isShowing = true; - return Future.value(true); - } catch (err) { - _isShowing = false; - return Future.value(false); - } - } - - /// 隐藏弹窗 - Future dismiss() { - try { - if (_isShowing) { - _isShowing = false; - Navigator.pop(_context); - return Future.value(true); - } else { - return Future.value(false); - } - } catch (err) { - return Future.value(false); - } - } - - /// 是否显示 - bool isShowing() { - return _isShowing; - } - - /// 更新进度 - void update(double progress) { - if (isShowing()) { - _widget.update(progress); - } - } - - /// 显示版本更新提示框 - static UpdateDialog showUpdate(BuildContext context, - {double width = 0.0, - required String title, - required String updateContent, - required VoidCallback onUpdate, - double titleTextSize = 16.0, - double contentTextSize = 14.0, - double buttonTextSize = 14.0, - double progress = -1.0, - Color progressBackgroundColor = const Color(0xFFFFCDD2), - Image? topImage, - double extraHeight = 5.0, - double radius = 4.0, - Color themeColor = Colors.red, - bool enableIgnore = false, - VoidCallback? onIgnore, - String? updateButtonText, - String? ignoreButtonText, - bool isForce = false}) { - final UpdateDialog dialog = UpdateDialog(context, - width: width, - title: title, - updateContent: updateContent, - onUpdate: onUpdate, - titleTextSize: titleTextSize, - contentTextSize: contentTextSize, - buttonTextSize: buttonTextSize, - progress: progress, - topImage: topImage, - extraHeight: extraHeight, - radius: radius, - themeColor: themeColor, - progressBackgroundColor: progressBackgroundColor, - enableIgnore: enableIgnore, - isForce: isForce, - updateButtonText: updateButtonText, - ignoreButtonText: ignoreButtonText, - onIgnore: onIgnore); - dialog.show(); - return dialog; - } -} - -// ignore: must_be_immutable -class UpdateWidget extends StatefulWidget { - /// 对话框的宽度 - final double width; - - /// 升级标题 - final String title; - - /// 更新内容 - final String updateContent; - - /// 标题文字的大小 - final double titleTextSize; - - /// 更新文字内容的大小 - final double contentTextSize; - - /// 按钮文字的大小 - final double buttonTextSize; - - /// 顶部图片 - final Widget? topImage; - - /// 拓展高度(适配顶部图片高度不一致的情况) - final double extraHeight; - - /// 边框圆角大小 - final double radius; - - /// 主题颜色 - final Color themeColor; - - /// 更新事件 - final VoidCallback onUpdate; - - /// 可忽略更新 - final bool enableIgnore; - - /// 更新事件 - final VoidCallback? onIgnore; - - double progress; - - /// 进度条的背景颜色 - final Color progressBackgroundColor; - - /// 更新事件 - final VoidCallback? onClose; - - /// 是否是强制更新 - final bool isForce; - - /// 更新按钮内容 - final String updateButtonText; - - /// 忽略按钮内容 - final String ignoreButtonText; - - UpdateWidget( - {Key? key, - this.width = 0.0, - required this.title, - required this.updateContent, - required this.onUpdate, - this.titleTextSize = 16.0, - this.contentTextSize = 14.0, - this.buttonTextSize = 14.0, - this.progress = -1.0, - this.progressBackgroundColor = const Color(0xFFFFCDD2), - this.topImage, - this.extraHeight = 5.0, - this.radius = 4.0, - this.themeColor = Colors.red, - this.enableIgnore = false, - this.onIgnore, - this.isForce = false, - this.updateButtonText = '更新', - this.ignoreButtonText = '忽略此版本', - this.onClose}) - : super(key: key); - - final _UpdateWidgetState _state = _UpdateWidgetState(); - - void update(double progress) { - _state.update(progress); - } - - @override - _UpdateWidgetState createState() => _state; -} - -class _UpdateWidgetState extends State { - void update(double progress) { - if (!mounted) { - return; - } - setState(() { - widget.progress = progress; - }); - } - - @override - Widget build(BuildContext context) { - final double dialogWidth = widget.width <= 0 ? getFitWidth(context) * 0.618 : widget.width; - return Material( - type: MaterialType.transparency, - child: SizedBox( - width: dialogWidth, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - width: dialogWidth, - child: widget.topImage ?? Image.asset('assets/images/update_bg_app_top.png', fit: BoxFit.fill), - ), - Container( - width: dialogWidth, - alignment: Alignment.center, - padding: const EdgeInsets.only(left: 16, right: 16, top: 8, bottom: 8), - decoration: ShapeDecoration( - color: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(widget.radius), bottomRight: Radius.circular(widget.radius)), - ), - ), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: EdgeInsets.only(top: widget.extraHeight), - child: Text(widget.title, style: TextStyle(fontSize: widget.titleTextSize, color: Colors.black)), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Text(widget.updateContent, style: TextStyle(fontSize: widget.contentTextSize, color: const Color(0xFF666666))), - ), - Column(children: [ - FractionallySizedBox( - widthFactor: 1, - child: ElevatedButton( - style: ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - textStyle: MaterialStateProperty.all(TextStyle(fontSize: widget.buttonTextSize)), - foregroundColor: MaterialStateProperty.all(Colors.white), - shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5))), - elevation: MaterialStateProperty.all(0), - backgroundColor: MaterialStateProperty.all(widget.themeColor), - ), - child: Text(widget.updateButtonText), - onPressed: widget.onUpdate, - ), - ), - if (widget.enableIgnore && widget.onIgnore != null) - FractionallySizedBox( - widthFactor: 1, - child: TextButton( - style: ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - textStyle: MaterialStateProperty.all(TextStyle(fontSize: widget.buttonTextSize)), - foregroundColor: MaterialStateProperty.all(const Color(0xFF666666)), - shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5))), - ), - child: Text(widget.ignoreButtonText), - onPressed: widget.onIgnore, - )) - else - const SizedBox() - ]), - ], - )), - ), - if (!widget.isForce) - Column(children: [ - const SizedBox(width: 1.5, height: 50, child: DecoratedBox(decoration: BoxDecoration(color: Colors.white))), - IconButton( - iconSize: 30, - constraints: const BoxConstraints(maxHeight: 30, maxWidth: 30), - padding: EdgeInsets.zero, - icon: Image.asset( - 'assets/images/update_ic_close.png', - ), - onPressed: widget.onClose, - ) - ]) - else - const SizedBox() - ], - ), - )); - } - - double getFitWidth(BuildContext context) { - return min(getScreenHeight(context), getScreenWidth(context)); - } - - double getScreenHeight(BuildContext context) { - return MediaQuery.of(context).size.height; - } - - double getScreenWidth(BuildContext context) { - return MediaQuery.of(context).size.width; - } -}