diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e48bee..9ab63a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.0.4 +* 增加主题切换功能,各种颜色主题随意切换 +* 修改无法停止任务的bug +* 修改脚本管理页面json解析失败的bug +* 修改变更配置文件没有及时刷新UI的bug +* 增加版本更新提醒功能(需要手机能联通github) +* 登录页面长按青龙logo可以呼出网络请求日志,再次长按可关闭 +* 打开系统最近任务时展示APP的名称 +* 优化使用体验 + ## 1.0.3 * 解决2.10.12及以下版本无法登录的问题 diff --git a/README.md b/README.md index 4d242aa..ff135ed 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ iOS端暂无上架打算,用户自行下载main分支源码编译安装

+ -

@@ -35,9 +35,9 @@ iOS端暂无上架打算,用户自行下载main分支源码编译安装

+

### 不支持的功能 - >* 应用设置 >* 通知设置 diff --git a/art/14.jpg b/art/14.jpg new file mode 100644 index 0000000..3353034 Binary files /dev/null and b/art/14.jpg differ diff --git a/assets/images/update_bg_app_top.png b/assets/images/update_bg_app_top.png new file mode 100644 index 0000000..d97da82 Binary files /dev/null and b/assets/images/update_bg_app_top.png differ diff --git a/assets/images/update_ic_close.png b/assets/images/update_ic_close.png new file mode 100644 index 0000000..29e55e9 Binary files /dev/null and b/assets/images/update_ic_close.png differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a07c4ef..fb6eca4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -5,15 +5,21 @@ PODS: - Toast - move_to_background (0.0.1): - Flutter + - package_info_plus (0.4.5): + - Flutter - shared_preferences_ios (0.0.1): - Flutter - Toast (4.0.0) + - url_launcher_ios (0.0.1): + - Flutter DEPENDENCIES: - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - move_to_background (from `.symlinks/plugins/move_to_background/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) SPEC REPOS: trunk: @@ -26,15 +32,21 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/fluttertoast/ios" move_to_background: :path: ".symlinks/plugins/move_to_background/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" shared_preferences_ios: :path: ".symlinks/plugins/shared_preferences_ios/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a fluttertoast: 6122fa75143e992b1d3470f61000f591a798cc58 move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d + package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e shared_preferences_ios: aef470a42dc4675a1cdd50e3158b42e3d1232b32 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 + url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af PODFILE CHECKSUM: aea2ed8a4b05dec076f6f7cea39202b1133ed51c diff --git a/lib/base/http/api.dart b/lib/base/http/api.dart index 577da93..903faac 100644 --- a/lib/base/http/api.dart +++ b/lib/base/http/api.dart @@ -87,7 +87,7 @@ class Api { static Future> stopTasks(List crons) async { return await Http.put( - Url.runTasks, + Url.stopTasks, crons, ); } diff --git a/lib/base/http/url.dart b/lib/base/http/url.dart index 4b6345d..6e919ab 100644 --- a/lib/base/http/url.dart +++ b/lib/base/http/url.dart @@ -76,4 +76,6 @@ class Url { } return false; } + + static String checkUpdateUrl = "https://raw.githubusercontent.com/qinglong-app/qinglong_app/main/version"; } diff --git a/lib/base/routes.dart b/lib/base/routes.dart index 0a51954..caafac2 100644 --- a/lib/base/routes.dart +++ b/lib/base/routes.dart @@ -15,6 +15,7 @@ import 'package:qinglong_app/module/others/scripts/script_edit_page.dart'; import 'package:qinglong_app/module/others/scripts/script_page.dart'; import 'package:qinglong_app/module/others/task_log/task_log_detail_page.dart'; import 'package:qinglong_app/module/others/task_log/task_log_page.dart'; +import 'package:qinglong_app/module/others/theme_page.dart'; import 'package:qinglong_app/module/others/update_password_page.dart'; import 'package:qinglong_app/module/task/add_task_page.dart'; import 'package:qinglong_app/module/task/task_bean.dart'; @@ -39,6 +40,7 @@ class Routes { static const String routeDependency = "/Dependency"; static const String routeUpdatePassword = "/updatePassword"; static const String routeAbout = "/about"; + static const String routeTheme = "/theme"; static Route? generateRoute(RouteSettings settings) { switch (settings.name) { @@ -56,7 +58,8 @@ class Routes { return CupertinoPageRoute(builder: (context) => const AddTaskPage()); } case routeAddDependency: - return CupertinoPageRoute(builder: (context) => const AddDependencyPage()); + return CupertinoPageRoute( + builder: (context) => const AddDependencyPage()); case routeAddEnv: if (settings.arguments != null) { return CupertinoPageRoute( @@ -122,6 +125,10 @@ class Routes { return CupertinoPageRoute( builder: (context) => const AboutPage(), ); + case routeTheme: + return CupertinoPageRoute( + builder: (context) => const ThemePage(), + ); case routeScriptUpdate: return CupertinoPageRoute( builder: (context) => ScriptEditPage( diff --git a/lib/base/sp_const.dart b/lib/base/sp_const.dart index 647ce53..b4ae7b4 100644 --- a/lib/base/sp_const.dart +++ b/lib/base/sp_const.dart @@ -4,3 +4,4 @@ String spUserName = "username"; String spPassWord = "password"; String spTheme = "dart_mode"; String spSecretLogined = "secret_logined"; +String spCustomColor = "customColor"; diff --git a/lib/base/theme.dart b/lib/base/theme.dart index 7d78418..5150117 100644 --- a/lib/base/theme.dart +++ b/lib/base/theme.dart @@ -3,21 +3,25 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:qinglong_app/base/sp_const.dart'; +import 'package:qinglong_app/base/userinfo_viewmodel.dart'; +import 'package:qinglong_app/main.dart'; import 'package:qinglong_app/utils/codeeditor_theme.dart'; import 'package:qinglong_app/utils/sp_utils.dart'; var themeProvider = ChangeNotifierProvider((ref) => ThemeViewModel()); -const Color _primaryColor = Color(0xFF299343); - -get primaryColor => _primaryColor; +Color _primaryColor = const Color(0xFF299343); class ThemeViewModel extends ChangeNotifier { - ThemeData currentTheme = lightTheme; + late ThemeData currentTheme; + bool _isInDarkMode = false; + Color primaryColor = const Color(0xFF299343); ThemeColors themeColor = LightThemeColors(); ThemeViewModel() { + _primaryColor = Color(getIt().primaryColor); + primaryColor = Color(getIt().primaryColor); var brightness = SchedulerBinding.instance!.window.platformBrightness; _isInDarkMode = brightness == Brightness.dark; changeThemeReal(_isInDarkMode, false); @@ -31,10 +35,10 @@ class ThemeViewModel extends ChangeNotifier { _isInDarkMode = dark; SpUtil.putBool(spTheme, dark); if (!dark) { - currentTheme = lightTheme; + currentTheme = getLightTheme(); themeColor = LightThemeColors(); } else { - currentTheme = darkTheme; + currentTheme = getDartTheme(); themeColor = DartThemeColors(); } if (notify) { @@ -44,149 +48,161 @@ class ThemeViewModel extends ChangeNotifier { get darkMode => _isInDarkMode; + void changePrimaryColor(Color color) { + _primaryColor = color; + primaryColor = color; + getIt().updateCustomColor(color.value); + changeThemeReal(SpUtil.getBool(spTheme, defValue: false), true); + } + void changeTheme() { changeThemeReal(!SpUtil.getBool(spTheme, defValue: false), true); } -} -ThemeData darkTheme = ThemeData.dark().copyWith( - brightness: Brightness.dark, - primaryColor: const Color(0xffffffff), - scaffoldBackgroundColor: Colors.black, - appBarTheme: const AppBarTheme( - backgroundColor: Colors.black, - titleTextStyle: TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - inputDecorationTheme: const InputDecorationTheme( - labelStyle: TextStyle(color: _primaryColor), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( + ThemeData getLightTheme() { + return ThemeData.light().copyWith( + brightness: Brightness.light, + primaryColor: _primaryColor, + colorScheme: ColorScheme.light( + secondary: _primaryColor, + primary: _primaryColor, + ), + scaffoldBackgroundColor: const Color(0xfff5f5f5), + inputDecorationTheme: InputDecorationTheme( + labelStyle: TextStyle(color: _primaryColor), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: _primaryColor, + ), + ), + border: const UnderlineInputBorder( + borderSide: BorderSide( + color: Color(0xff999999), + ), + ), + enabledBorder: const UnderlineInputBorder( + borderSide: BorderSide( + color: Color(0xff999999), + ), + ), + ), + appBarTheme: AppBarTheme( + backgroundColor: _primaryColor, + titleTextStyle: const TextStyle( + color: Colors.white, + fontSize: 18, + ), + ), + bottomNavigationBarTheme: BottomNavigationBarThemeData( + selectedItemColor: _primaryColor, + ), + buttonTheme: ButtonThemeData( + buttonColor: _primaryColor, + ), + progressIndicatorTheme: ProgressIndicatorThemeData( color: _primaryColor, ), - ), - border: UnderlineInputBorder( - borderSide: BorderSide( - color: Color(0xff999999), + tabBarTheme: TabBarTheme( + labelStyle: const TextStyle( + fontSize: 14, + ), + unselectedLabelStyle: const TextStyle( + fontSize: 14, + ), + labelColor: _primaryColor, + unselectedLabelColor: const Color(0xff999999), + indicator: UnderlineTabIndicator( + borderSide: BorderSide(color: _primaryColor), + ), ), - ), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Color(0xff999999), + toggleableActiveColor: _primaryColor, + checkboxTheme: CheckboxThemeData( + checkColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.disabled)) { + return Colors.transparent; + } + if (states.contains(MaterialState.selected)) { + return Colors.white; + } + return Colors.black; + }, + ), ), - ), - ), - tabBarTheme: const TabBarTheme( - labelStyle: TextStyle( - fontSize: 14, - ), - unselectedLabelStyle: TextStyle( - fontSize: 14, - ), - labelColor: Color(0xffffffff), - unselectedLabelColor: Color(0xff999999), - ), - colorScheme: const ColorScheme.light( - secondary: _primaryColor, - primary: _primaryColor, - ), - toggleableActiveColor: _primaryColor, - checkboxTheme: CheckboxThemeData( - checkColor: MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.disabled)) { - return Colors.transparent; - } - if (states.contains(MaterialState.selected)) { - return Colors.white; - } - return Colors.white; - }, - ), - ), - cupertinoOverrideTheme: const NoDefaultCupertinoThemeData( - brightness: Brightness.dark, - primaryColor: Color(0xffffffff), - scaffoldBackgroundColor: Colors.black, - ), -); -ThemeData lightTheme = ThemeData.light().copyWith( - brightness: Brightness.light, - primaryColor: _primaryColor, - colorScheme: const ColorScheme.light( - secondary: _primaryColor, - primary: _primaryColor, - ), - scaffoldBackgroundColor: const Color(0xfff5f5f5), - inputDecorationTheme: const InputDecorationTheme( - labelStyle: TextStyle(color: _primaryColor), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: _primaryColor, + cupertinoOverrideTheme: NoDefaultCupertinoThemeData( + brightness: Brightness.light, + primaryColor: _primaryColor, + scaffoldBackgroundColor: const Color(0xfff5f5f5), ), - ), - border: UnderlineInputBorder( - borderSide: BorderSide( - color: Color(0xff999999), + ); + } + + ThemeData getDartTheme() { + return ThemeData.dark().copyWith( + brightness: Brightness.dark, + primaryColor: const Color(0xffffffff), + scaffoldBackgroundColor: Colors.black, + appBarTheme: const AppBarTheme( + backgroundColor: Colors.black, + titleTextStyle: TextStyle( + color: Colors.white, + fontSize: 18, + ), ), - ), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Color(0xff999999), + inputDecorationTheme: InputDecorationTheme( + labelStyle: TextStyle(color: _primaryColor), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: _primaryColor, + ), + ), + border: const UnderlineInputBorder( + borderSide: BorderSide( + color: Color(0xff999999), + ), + ), + enabledBorder: const UnderlineInputBorder( + borderSide: BorderSide( + color: Color(0xff999999), + ), + ), ), - ), - ), - appBarTheme: const AppBarTheme( - backgroundColor: _primaryColor, - titleTextStyle: TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - bottomNavigationBarTheme: const BottomNavigationBarThemeData( - selectedItemColor: _primaryColor, - ), - buttonTheme: const ButtonThemeData( - buttonColor: _primaryColor, - ), - progressIndicatorTheme: const ProgressIndicatorThemeData( - color: _primaryColor, - ), - tabBarTheme: const TabBarTheme( - labelStyle: TextStyle( - fontSize: 14, - ), - unselectedLabelStyle: TextStyle( - fontSize: 14, - ), - labelColor: _primaryColor, - unselectedLabelColor: Color(0xff999999), - indicator: UnderlineTabIndicator( - borderSide: BorderSide(color: _primaryColor), - ), - ), - toggleableActiveColor: _primaryColor, - checkboxTheme: CheckboxThemeData( - checkColor: MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.disabled)) { - return Colors.transparent; - } - if (states.contains(MaterialState.selected)) { - return Colors.white; - } - return Colors.black; - }, - ), - ), - cupertinoOverrideTheme: const NoDefaultCupertinoThemeData( - brightness: Brightness.light, - primaryColor: _primaryColor, - scaffoldBackgroundColor: Color(0xfff5f5f5), - ), -); + tabBarTheme: const TabBarTheme( + labelStyle: TextStyle( + fontSize: 14, + ), + unselectedLabelStyle: TextStyle( + fontSize: 14, + ), + labelColor: Color(0xffffffff), + unselectedLabelColor: Color(0xff999999), + ), + colorScheme: ColorScheme.light( + secondary: _primaryColor, + primary: _primaryColor, + ), + toggleableActiveColor: _primaryColor, + checkboxTheme: CheckboxThemeData( + checkColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.disabled)) { + return Colors.transparent; + } + if (states.contains(MaterialState.selected)) { + return Colors.white; + } + return Colors.white; + }, + ), + ), + cupertinoOverrideTheme: const NoDefaultCupertinoThemeData( + brightness: Brightness.dark, + primaryColor: Color(0xffffffff), + scaffoldBackgroundColor: Colors.black, + ), + ); + } +} abstract class ThemeColors { Color settingBgColor(); diff --git a/lib/base/userinfo_viewmodel.dart b/lib/base/userinfo_viewmodel.dart index ab33602..3da7a1f 100644 --- a/lib/base/userinfo_viewmodel.dart +++ b/lib/base/userinfo_viewmodel.dart @@ -8,11 +8,13 @@ class UserInfoViewModel { String? _userName; String? _passWord; bool _useSecertLogined = false; + int _primaryColor = 0xFF299343; UserInfoViewModel() { String userInfoJson = SpUtil.getString(spUserInfo); _userName = SpUtil.getString(spUserName); _passWord = SpUtil.getString(spPassWord); + _primaryColor = SpUtil.getInt(spCustomColor, defValue: 0xFF299343); _useSecertLogined = SpUtil.getBool(spSecretLogined, defValue: false); _host = SpUtil.getString(spHost, defValue: ''); @@ -39,6 +41,11 @@ class UserInfoViewModel { SpUtil.putBool(spSecretLogined, _useSecertLogined); } + void updateCustomColor(int color) { + _primaryColor = color; + SpUtil.putInt(spCustomColor, color); + } + void updateHost(String host) { _host = host; SpUtil.putString(spHost, host); @@ -54,6 +61,8 @@ class UserInfoViewModel { bool get useSecretLogined => _useSecertLogined; + int get primaryColor => _primaryColor; + bool isLogined() { return token != null && token!.isNotEmpty; } diff --git a/lib/main.dart b/lib/main.dart index 6c2103c..9b12a29 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -57,7 +57,8 @@ class QlAppState extends ConsumerState { child: MediaQuery( data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window).copyWith(textScaleFactor: 1,), child: MaterialApp( - locale: const Locale('zh', 'cn'), + title: "青龙", + locale: const Locale('zh', 'CN'), navigatorKey: navigatorState, theme: ref.watch(themeProvider).currentTheme, onGenerateRoute: (setting) { @@ -65,9 +66,6 @@ class QlAppState extends ConsumerState { }, home: Builder( builder: (context) { - if (!kReleaseMode) { - showDebugBtn(context, btnColor: Colors.blue); - } return getIt().isLogined() ? const HomePage() : const LoginPage(); }, ), diff --git a/lib/module/a.dart b/lib/module/a.dart deleted file mode 100644 index 8b13789..0000000 --- a/lib/module/a.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/module/config/config_edit_page.dart b/lib/module/config/config_edit_page.dart index b7e7268..bf47439 100644 --- a/lib/module/config/config_edit_page.dart +++ b/lib/module/config/config_edit_page.dart @@ -49,7 +49,7 @@ class _ConfigEditPageState extends ConsumerState { if (response.success) { "提交成功".toast(); ref.read(configProvider).loadContent(widget.title); - Navigator.of(context).pop(); + Navigator.of(context).pop(widget.title); } else { (response.message ?? "").toast(); } @@ -80,7 +80,7 @@ class _ConfigEditPageState extends ConsumerState { child: TextField( focusNode: node, style: TextStyle( - color: ref.read(themeProvider).themeColor.descColor(), + color: ref.read(themeProvider).themeColor.titleColor(), fontSize: 14, ), controller: _controller, diff --git a/lib/module/config/config_page.dart b/lib/module/config/config_page.dart index d51b9ff..de4b25d 100644 --- a/lib/module/config/config_page.dart +++ b/lib/module/config/config_page.dart @@ -5,6 +5,7 @@ import 'package:qinglong_app/base/base_state_widget.dart'; import 'package:qinglong_app/base/routes.dart'; import 'package:qinglong_app/base/theme.dart'; import 'package:qinglong_app/base/ui/abs_underline_tabindicator.dart'; +import 'package:qinglong_app/base/ui/empty_widget.dart'; import 'package:qinglong_app/main.dart'; import 'config_viewmodel.dart'; @@ -29,6 +30,9 @@ class ConfigPageState extends State Widget build(BuildContext context) { return BaseStateWidget( builder: (ref, model, child) { + if (model.list.isEmpty) { + return const EmptyWidget(); + } _tabController ??= TabController(length: model.list.length, vsync: this); @@ -89,6 +93,11 @@ class ConfigPageState extends State "title": ref.read(configProvider).list[_tabController?.index ?? 0].title, "content": ref.read(configProvider).content[ ref.read(configProvider).list[_tabController?.index ?? 0].title] + }).then((value) async { + if (value != null && (value as String).isNotEmpty) { + await ref.read(configProvider).loadContent(value); + setState(() {}); + } }); } } diff --git a/lib/module/env/env_bean.dart b/lib/module/env/env_bean.dart index d54c3d5..87dfb45 100644 --- a/lib/module/env/env_bean.dart +++ b/lib/module/env/env_bean.dart @@ -15,7 +15,7 @@ class EnvBean { EnvBean.fromJson(Map json) { value = json['value']; sId = json.containsKey('_id') ? json['_id'].toString() : (json.containsKey('id') ? json['id'].toString() : ""); - created = json['created']; + created = int.tryParse(json['created'].toString()); status = json['status']; timestamp = json['timestamp']; name = json['name']; diff --git a/lib/module/env/env_detail_page.dart b/lib/module/env/env_detail_page.dart index fdc3540..913e7db 100644 --- a/lib/module/env/env_detail_page.dart +++ b/lib/module/env/env_detail_page.dart @@ -249,7 +249,7 @@ class _TaskDetailPageState extends ConsumerState { child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () async { diff --git a/lib/module/env/env_page.dart b/lib/module/env/env_page.dart index 0ba5be8..c9f22b3 100644 --- a/lib/module/env/env_page.dart +++ b/lib/module/env/env_page.dart @@ -238,13 +238,24 @@ class EnvItemCell extends StatelessWidget { ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), - border: Border.all(color: primaryColor, width: 1), + border: Border.all(color: ref.watch(themeProvider).primaryColor, width: 1), ), child: Text( "$index", - style: TextStyle(color: primaryColor, fontSize: 12), + style: TextStyle(color: ref.watch(themeProvider).primaryColor, fontSize: 12), ), ), + const SizedBox( + width: 5, + ), + bean.status == 1 + ? const Icon( + Icons.dnd_forwardslash, + size: 12, + color: Colors.red, + ) + : const SizedBox.shrink(), + const SizedBox( width: 5, ), @@ -261,16 +272,6 @@ class EnvItemCell extends StatelessWidget { ), ), ), - const SizedBox( - width: 5, - ), - bean.status == 1 - ? const Icon( - Icons.dnd_forwardslash, - size: 12, - color: Colors.red, - ) - : const SizedBox.shrink(), const Spacer(), ], ), @@ -331,7 +332,7 @@ class EnvItemCell extends StatelessWidget { child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () { diff --git a/lib/module/home/home_page.dart b/lib/module/home/home_page.dart index 4bee986..ca37d09 100644 --- a/lib/module/home/home_page.dart +++ b/lib/module/home/home_page.dart @@ -9,6 +9,7 @@ import 'package:qinglong_app/module/env/env_page.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'; class HomePage extends ConsumerStatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -30,6 +31,9 @@ class _HomePageState extends ConsumerState { initTitles(); _title = titles[0].title; super.initState(); + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + update(); + }); } @override @@ -185,6 +189,16 @@ 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(); + } + } } class IndexBean { diff --git a/lib/module/login/login_bean.dart b/lib/module/login/login_bean.dart index c686f77..1b26726 100644 --- a/lib/module/login/login_bean.dart +++ b/lib/module/login/login_bean.dart @@ -21,7 +21,7 @@ class LoginBean { token = json['token']; lastip = json['lastip']; lastaddr = json['lastaddr']; - lastlogon = json['lastlogon']; + lastlogon = int.tryParse(json['lastlogon'].toString()); retries = json['retries']; platform = json['platform']; } diff --git a/lib/module/login/login_page.dart b/lib/module/login/login_page.dart index 90a07d1..a638c1d 100644 --- a/lib/module/login/login_page.dart +++ b/lib/module/login/login_page.dart @@ -1,3 +1,4 @@ +import 'package:dio_log/dio_log.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -9,6 +10,7 @@ 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/update_utils.dart'; import 'package:qinglong_app/utils/utils.dart'; import 'package:flip_card/flip_card.dart'; @@ -22,7 +24,8 @@ class LoginPage extends ConsumerStatefulWidget { } class _LoginPageState extends ConsumerState { - final TextEditingController _hostController = TextEditingController(text: getIt().host); + final TextEditingController _hostController = + TextEditingController(text: getIt().host); final TextEditingController _userNameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); final TextEditingController _cIdController = TextEditingController(); @@ -38,7 +41,8 @@ class _LoginPageState extends ConsumerState { super.initState(); useSecretLogin = getIt().useSecretLogined; - if (getIt().userName != null && getIt().userName!.isNotEmpty) { + if (getIt().userName != null && + getIt().userName!.isNotEmpty) { if (getIt().useSecretLogined) { _cIdController.text = getIt().userName!; } else { @@ -48,7 +52,8 @@ class _LoginPageState extends ConsumerState { } else { rememberPassword = false; } - if (getIt().passWord != null && getIt().passWord!.isNotEmpty) { + if (getIt().passWord != null && + getIt().passWord!.isNotEmpty) { if (getIt().useSecretLogined) { _cSecretController.text = getIt().passWord!; } else { @@ -57,6 +62,7 @@ class _LoginPageState extends ConsumerState { } getIt().updateToken(""); WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + update(); if (useSecretLogin) { cardKey.currentState?.toggleCard(); } @@ -107,9 +113,28 @@ class _LoginPageState extends ConsumerState { ), ), ), - Image.asset( - "assets/images/ql.png", - height: 45, + GestureDetector( + onDoubleTap: () { + if (debugBtnIsShow()) { + dismissDebugBtn(); + } else { + showDebugBtn(context, + btnColor: ref + .watch(themeProvider) + .primaryColor); + } + WidgetsBinding.instance?.endOfFrame; + }, + child: ColorFiltered( + colorFilter: ColorFilter.mode( + ref.watch(themeProvider).primaryColor, + BlendMode.srcIn, + ), + child: Image.asset( + "assets/images/ql.png", + height: 45, + ), + ), ), ], ), @@ -136,6 +161,7 @@ class _LoginPageState extends ConsumerState { ), FlipCard( key: cardKey, + flipOnTouch: false, onFlipDone: (back) { useSecretLogin = back; setState(() {}); @@ -164,7 +190,8 @@ class _LoginPageState extends ConsumerState { }, controller: _userNameController, decoration: const InputDecoration( - contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + contentPadding: + EdgeInsets.fromLTRB(0, 5, 0, 5), hintText: "请输入用户名", ), autofocus: false, @@ -186,7 +213,8 @@ class _LoginPageState extends ConsumerState { controller: _passwordController, obscureText: true, decoration: const InputDecoration( - contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + contentPadding: + EdgeInsets.fromLTRB(0, 5, 0, 5), hintText: "请输入密码", ), autofocus: false, @@ -220,7 +248,8 @@ class _LoginPageState extends ConsumerState { }, controller: _cIdController, decoration: const InputDecoration( - contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + contentPadding: + EdgeInsets.fromLTRB(0, 5, 0, 5), hintText: "请输入client_id", ), autofocus: false, @@ -242,7 +271,8 @@ class _LoginPageState extends ConsumerState { controller: _cSecretController, obscureText: true, decoration: const InputDecoration( - contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + contentPadding: + EdgeInsets.fromLTRB(0, 5, 0, 5), hintText: "请输入client_secret", ), autofocus: false, @@ -280,18 +310,19 @@ class _LoginPageState extends ConsumerState { GestureDetector( onTap: () { cardKey.currentState?.toggleCard(); - WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + WidgetsBinding.instance + ?.addPostFrameCallback((timeStamp) { setState(() {}); }); }, child: Text( - loginByUserName() ? "client_id登录" : "用户名密码登录", + loginByUserName() ? "client_id登录" : "账号登录", style: const TextStyle( fontSize: 14, ), ), ), - SizedBox( + const SizedBox( width: 10, ), ], @@ -313,7 +344,12 @@ class _LoginPageState extends ConsumerState { padding: const EdgeInsets.symmetric( vertical: 5, ), - color: canClickLoginBtn() ? primaryColor : primaryColor.withOpacity(0.4), + color: canClickLoginBtn() + ? ref.watch(themeProvider).primaryColor + : ref + .watch(themeProvider) + .primaryColor + .withOpacity(0.4), child: isLoading ? const CupertinoActivityIndicator() : const Text( @@ -325,12 +361,15 @@ class _LoginPageState extends ConsumerState { onPressed: () { Http.pushedLoginPage = false; Utils.hideKeyBoard(context); - getIt().updateHost(_hostController.text); + getIt() + .updateHost(_hostController.text); Http.clear(); if (loginByUserName()) { - login(_userNameController.text, _passwordController.text); + login(_userNameController.text, + _passwordController.text); } else { - login(_cIdController.text, _cSecretController.text); + login(_cIdController.text, + _cSecretController.text); } }, ), @@ -367,7 +406,8 @@ class _LoginPageState extends ConsumerState { loginSuccess(response, userName, password); } else if (loginByUserName() && response.code == 401) { //可能用户使用的是老版本qinglong - HttpResponse oldResponse = await Api.loginOld(userName, password); + HttpResponse oldResponse = + await Api.loginOld(userName, password); if (oldResponse.success) { loginSuccess(oldResponse, userName, password); } else { @@ -398,11 +438,14 @@ class _LoginPageState extends ConsumerState { setState(() {}); } - void loginSuccess(HttpResponse response, String userName, String password) { + void loginSuccess( + HttpResponse response, String userName, String password) { getIt().updateToken(response.bean?.token ?? ""); getIt().useSecretLogin(!loginByUserName()); if (rememberPassword) { getIt().updateUserName(userName, password); + } else { + getIt().updateUserName("", ""); } Navigator.of(context).pushReplacementNamed(Routes.routeHomePage); } @@ -412,9 +455,11 @@ class _LoginPageState extends ConsumerState { if (_hostController.text.isEmpty) return false; if (!loginByUserName()) { - return _cIdController.text.isNotEmpty && _cSecretController.text.isNotEmpty; + return _cIdController.text.isNotEmpty && + _cSecretController.text.isNotEmpty; } else { - return _userNameController.text.isNotEmpty && _passwordController.text.isNotEmpty; + return _userNameController.text.isNotEmpty && + _passwordController.text.isNotEmpty; } } @@ -460,12 +505,13 @@ class _LoginPageState extends ConsumerState { child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () async { Navigator.of(context).pop(true); - HttpResponse response = await Api.loginTwo(userName, password, twoFact); + HttpResponse response = + await Api.loginTwo(userName, password, twoFact); if (response.success) { loginSuccess(response, userName, password); } else { @@ -481,4 +527,15 @@ 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(); + } + } } diff --git a/lib/module/others/about_page.dart b/lib/module/others/about_page.dart index 6a21fcd..ba2a206 100644 --- a/lib/module/others/about_page.dart +++ b/lib/module/others/about_page.dart @@ -3,6 +3,7 @@ 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'; @@ -23,6 +24,20 @@ class _AboutPageState extends ConsumerState { void initState() { 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 @@ -76,7 +91,7 @@ class _AboutPageState extends ConsumerState { child: Text( "Telegram频道", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, fontSize: 16, ), ), @@ -92,7 +107,7 @@ class _AboutPageState extends ConsumerState { child: Text( "版本更新", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, fontSize: 16, ), ), @@ -107,7 +122,7 @@ class _AboutPageState extends ConsumerState { child: Text( "项目地址", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, fontSize: 16, ), ), diff --git a/lib/module/others/dependencies/add_dependency_page.dart b/lib/module/others/dependencies/add_dependency_page.dart index f1ba40b..e762532 100644 --- a/lib/module/others/dependencies/add_dependency_page.dart +++ b/lib/module/others/dependencies/add_dependency_page.dart @@ -37,10 +37,6 @@ class _AddDependencyPageState extends ConsumerState { title: "新增依赖", actions: [ InkWell( - onLongPress: () { - showDebugBtn(context, btnColor: Colors.blue); - WidgetsBinding.instance?.endOfFrame; - }, onTap: () { submit(); }, diff --git a/lib/module/others/dependencies/dependency_bean.dart b/lib/module/others/dependencies/dependency_bean.dart index 88ca001..e596c53 100644 --- a/lib/module/others/dependencies/dependency_bean.dart +++ b/lib/module/others/dependencies/dependency_bean.dart @@ -24,10 +24,10 @@ class DependencyBean { DependencyBean.fromJson(Map json) { sId = json['_id']; - created = json['created']; + created = int.tryParse(json['created'].toString()); status = json['status']; type = json['type']; - timestamp = json['timestamp']; + timestamp = json['timestamp'].toString(); name = json['name']; log = json['log'].cast(); remark = json['remark']; diff --git a/lib/module/others/dependencies/dependency_page.dart b/lib/module/others/dependencies/dependency_page.dart index 594e9a7..aad98b5 100644 --- a/lib/module/others/dependencies/dependency_page.dart +++ b/lib/module/others/dependencies/dependency_page.dart @@ -231,7 +231,7 @@ class DependencyCell extends ConsumerWidget { bean.status == 1 ? Icon( CupertinoIcons.checkmark_circle, - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, size: 16, ) : (bean.status == 2 @@ -348,7 +348,7 @@ class DependencyCell extends ConsumerWidget { child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () { diff --git a/lib/module/others/login_log/login_log_bean.dart b/lib/module/others/login_log/login_log_bean.dart index 95ea622..5305ee1 100644 --- a/lib/module/others/login_log/login_log_bean.dart +++ b/lib/module/others/login_log/login_log_bean.dart @@ -13,7 +13,7 @@ class LoginLogBean { {this.timestamp, this.address, this.ip, this.platform, this.status}); LoginLogBean.fromJson(Map json) { - timestamp = json['timestamp']; + timestamp = int.tryParse(json['timestamp'].toString()); address = json['address']; ip = json['ip']; platform = json['platform']; diff --git a/lib/module/others/login_log/login_log_page.dart b/lib/module/others/login_log/login_log_page.dart index 7ceb496..8c985a7 100644 --- a/lib/module/others/login_log/login_log_page.dart +++ b/lib/module/others/login_log/login_log_page.dart @@ -86,7 +86,7 @@ class _LoginLogPageState extends ConsumerState with LazyLoadState< trailing: item.status == 0 ? Icon( CupertinoIcons.checkmark_circle, - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, size: 16, ) : const Icon( diff --git a/lib/module/others/other_page.dart b/lib/module/others/other_page.dart index 339bf68..f85f3ca 100644 --- a/lib/module/others/other_page.dart +++ b/lib/module/others/other_page.dart @@ -59,7 +59,10 @@ class _OtherPageState extends ConsumerState { Text( "脚本管理", style: TextStyle( - color: ref.watch(themeProvider).themeColor.titleColor(), + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), fontSize: 16, ), ), @@ -92,7 +95,10 @@ class _OtherPageState extends ConsumerState { Text( "依赖管理", style: TextStyle( - color: ref.watch(themeProvider).themeColor.titleColor(), + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), fontSize: 16, ), ), @@ -116,16 +122,21 @@ class _OtherPageState extends ConsumerState { ); }, child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 15, + padding: const EdgeInsets.only( + top: 8, + bottom: 15, + left: 15, + right: 15, ), child: Row( children: [ Text( "任务日志", style: TextStyle( - color: ref.watch(themeProvider).themeColor.titleColor(), + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), fontSize: 16, ), ), @@ -178,7 +189,10 @@ class _OtherPageState extends ConsumerState { Text( "登录日志", style: TextStyle( - color: ref.watch(themeProvider).themeColor.titleColor(), + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), fontSize: 16, ), ), @@ -206,9 +220,11 @@ class _OtherPageState extends ConsumerState { }, behavior: HitTestBehavior.opaque, child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 10, - horizontal: 15, + padding: const EdgeInsets.only( + top: 8, + bottom: 15, + left: 15, + right: 15, ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -217,7 +233,10 @@ class _OtherPageState extends ConsumerState { Text( "修改密码", style: TextStyle( - color: ref.watch(themeProvider).themeColor.titleColor(), + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), fontSize: 16, ), ), @@ -261,7 +280,8 @@ class _OtherPageState extends ConsumerState { Text( "夜间模式", style: TextStyle( - color: ref.watch(themeProvider).themeColor.titleColor(), + color: + ref.watch(themeProvider).themeColor.titleColor(), fontSize: 16, ), ), @@ -278,6 +298,44 @@ class _OtherPageState extends ConsumerState { const Divider( indent: 15, ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.of(context).pushNamed( + Routes.routeTheme, + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 5, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "主题设置", + style: TextStyle( + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), + fontSize: 16, + ), + ), + const Spacer(), + const Icon( + CupertinoIcons.right_chevron, + size: 16, + ), + ], + ), + ), + ), + const Divider( + indent: 15, + ), GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { @@ -299,7 +357,10 @@ class _OtherPageState extends ConsumerState { Text( "关于", style: TextStyle( - color: ref.watch(themeProvider).themeColor.titleColor(), + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), fontSize: 16, ), ), @@ -353,12 +414,13 @@ class _OtherPageState extends ConsumerState { child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () { getIt().updateToken(""); - Navigator.of(context).pushReplacementNamed(Routes.routeLogin); + Navigator.of(context) + .pushReplacementNamed(Routes.routeLogin); }, ), ], diff --git a/lib/module/others/scripts/script_bean.dart b/lib/module/others/scripts/script_bean.dart index 4370716..3e65f64 100644 --- a/lib/module/others/scripts/script_bean.dart +++ b/lib/module/others/scripts/script_bean.dart @@ -5,24 +5,14 @@ import 'package:json_conversion_annotation/json_conversion_annotation.dart'; class ScriptBean { String? title; String? value; - String? key; - double? mtime; bool? disabled; List? children; - ScriptBean( - {this.title, - this.value, - this.key, - this.mtime, - this.disabled, - this.children}); + ScriptBean({this.title, this.value, this.disabled, this.children}); ScriptBean.fromJson(Map json) { title = json['title']; value = json['value']; - key = json['key']; - mtime = json['mtime']; disabled = json['disabled']; if (json['children'] != null) { children = []; @@ -36,8 +26,6 @@ class ScriptBean { final Map data = new Map(); data['title'] = this.title; data['value'] = this.value; - data['key'] = this.key; - data['mtime'] = this.mtime; data['disabled'] = this.disabled; if (this.children != null) { data['children'] = this.children!.map((v) => v.toJson()).toList(); @@ -53,17 +41,13 @@ class ScriptBean { class ScriptChildren { String? title; String? value; - String? key; - double? mtime; String? parent; - ScriptChildren({this.title, this.value, this.key, this.mtime, this.parent}); + ScriptChildren({this.title, this.value, this.parent}); ScriptChildren.fromJson(Map json) { title = json['title']; value = json['value']; - key = json['key']; - mtime = json['mtime']; parent = json['parent']; } @@ -71,8 +55,6 @@ class ScriptChildren { final Map data = new Map(); data['title'] = this.title; data['value'] = this.value; - data['key'] = this.key; - data['mtime'] = this.mtime; data['parent'] = this.parent; return data; } diff --git a/lib/module/others/scripts/script_detail_page.dart b/lib/module/others/scripts/script_detail_page.dart index 2a1c06c..3460f6c 100644 --- a/lib/module/others/scripts/script_detail_page.dart +++ b/lib/module/others/scripts/script_detail_page.dart @@ -98,7 +98,7 @@ class _ScriptDetailPageState extends ConsumerState with LazyLo child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () async { diff --git a/lib/module/others/theme_page.dart b/lib/module/others/theme_page.dart new file mode 100644 index 0000000..80b4f50 --- /dev/null +++ b/lib/module/others/theme_page.dart @@ -0,0 +1,478 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_colorpicker/flutter_colorpicker.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:qinglong_app/base/ql_app_bar.dart'; +import 'package:qinglong_app/base/theme.dart'; + +class ThemePage extends ConsumerStatefulWidget { + const ThemePage({Key? key}) : super(key: key); + + @override + _ThemePageState createState() => _ThemePageState(); +} + +class _ThemePageState extends ConsumerState { + late Color _primaryColor; + List colors = []; + double colorHeight = 40; + double smallColorHeight = 8; + + @override + void initState() { + super.initState(); + _primaryColor = ref.read(themeProvider).primaryColor; + colors.add(const Color(0xFF299343)); + colors.addAll([ + Colors.red, + Colors.pink, + Colors.purple, + Colors.deepPurple, + Colors.indigo, + Colors.blue, + Colors.lightBlue, + Colors.cyan, + Colors.teal, + Colors.green, + Colors.lightGreen, + Colors.lime, + Colors.yellow, + Colors.amber, + Colors.orange, + Colors.deepOrange, + Colors.brown, + Colors.blueGrey, + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xff999999), + appBar: QlAppBar( + title: '主题设置', + canBack: true, + actions: [ + InkWell( + onTap: () { + submit(); + }, + child: const Padding( + padding: EdgeInsets.symmetric( + horizontal: 15, + ), + child: Center( + child: Text( + "保存", + style: TextStyle( + color: Colors.white, + fontSize: 16, + ), + ), + ), + ), + ) + ], + ), + body: SafeArea( + bottom: true, + child: Column( + children: [ + const SizedBox( + height: 15, + ), + Expanded( + child: SizedBox( + height: + MediaQuery.of(context).size.height * 3 / 4 - kToolbarHeight, + child: Row( + children: [ + const SizedBox( + width: 20, + ), + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular(15), + child: Scaffold( + appBar: AppBar( + backgroundColor: _primaryColor, + title: Text("示例页面"), + elevation: 0, + automaticallyImplyLeading: false, + centerTitle: true, + ), + body: ListView.builder( + itemBuilder: (context, index) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width, + color: index == 0 + ? ref + .watch(themeProvider) + .themeColor + .pinColor() + : Colors.transparent, + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Expanded( + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Random().nextBool() + ? const SizedBox.shrink() + : SizedBox( + width: 15, + height: 15, + child: + CircularProgressIndicator( + strokeWidth: 2, + color: _primaryColor, + ), + ), + SizedBox( + width: Random().nextBool() + ? 0 + : 5, + ), + Expanded( + child: Material( + color: Colors.transparent, + child: Text( + "示例名称", + maxLines: 1, + overflow: + TextOverflow.ellipsis, + style: TextStyle( + overflow: TextOverflow + .ellipsis, + color: ref + .watch( + themeProvider) + .themeColor + .titleColor(), + fontSize: 18, + ), + ), + ), + ), + ], + ), + ), + Material( + color: Colors.transparent, + child: Text( + "上午10:00", + maxLines: 1, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: ref + .watch(themeProvider) + .themeColor + .descColor(), + fontSize: 12, + ), + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Random().nextBool() + ? const Icon( + Icons.dnd_forwardslash, + size: 12, + color: Colors.red, + ) + : const SizedBox.shrink(), + const SizedBox( + width: 5, + ), + Material( + color: Colors.transparent, + child: Text( + "10 1-12/2 * * *", + maxLines: 1, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: ref + .watch(themeProvider) + .themeColor + .descColor(), + fontSize: 12, + ), + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + Material( + color: Colors.transparent, + child: Text( + "task raw post.js", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: ref + .watch(themeProvider) + .themeColor + .descColor(), + fontSize: 12, + ), + ), + ), + ], + ), + ), + const Divider( + height: 1, + indent: 15, + ), + ], + ); + }, + itemCount: 100, + ), + ), + ), + ), + const SizedBox( + width: 20, + ), + ], + ), + ), + ), + const SizedBox( + height: 15, + ), + Container( + margin: const EdgeInsets.symmetric( + horizontal: 15, + ), + height: colorHeight + 20, + padding: const EdgeInsets.symmetric(vertical: 10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + child: ListView.separated( + physics: BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + if (index == 0) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + pickColor(); + }, + child: SizedBox( + width: colorHeight, + height: colorHeight, + child: Stack( + children: [ + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[0], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + left: 0, + top: colorHeight / 4 - smallColorHeight / 2, + ), + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[1], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + left: 0, + bottom: colorHeight / 4 - smallColorHeight / 2, + ), + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[2], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + left: colorHeight / 2 - smallColorHeight / 2, + top: 0, + ), + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[3], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + right: 0, + top: colorHeight / 4 - smallColorHeight / 2, + ), + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[4], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + right: 0, + bottom: colorHeight / 4 - smallColorHeight / 2, + ), + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[5], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + left: colorHeight / 2 - smallColorHeight / 2, + bottom: 0, + ), + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[6], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + left: colorHeight / 2 - smallColorHeight / 2, + bottom: 0, + ), + Positioned( + child: Container( + width: smallColorHeight, + height: smallColorHeight, + decoration: BoxDecoration( + color: colors[7], + borderRadius: + BorderRadius.circular(smallColorHeight), + ), + ), + left: colorHeight / 2 - smallColorHeight / 2, + bottom: colorHeight / 2 - smallColorHeight / 2, + ), + ], + ), + ), + ); + } else { + index = index - 1; + + return GestureDetector( + onTap: () { + _primaryColor = colors[index]; + setState(() {}); + }, + child: SizedBox( + width: colorHeight, + height: colorHeight, + child: Container( + decoration: BoxDecoration( + color: colors[index], + borderRadius: BorderRadius.circular(colorHeight), + ), + ), + ), + ); + } + }, + itemCount: colors.length + 1, + separatorBuilder: (BuildContext context, int index) { + return const SizedBox( + width: 15, + ); + }, + ), + ), + ], + ), + ), + ); + } + + void pickColor() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + titlePadding: const EdgeInsets.all(0), + contentPadding: const EdgeInsets.all(0), + content: SingleChildScrollView( + child: ColorPicker( + pickerColor: _primaryColor, + onColorChanged: (color) { + _primaryColor = color; + setState(() {}); + }, + colorPickerWidth: 300, + pickerAreaHeightPercent: 0.7, + enableAlpha: false, + pickerAreaBorderRadius: const BorderRadius.only( + topLeft: Radius.circular(2), + topRight: Radius.circular(2), + ), + hexInputBar: false, + ), + ), + ); + }, + ); + } + + void submit() { + ref.read(themeProvider).changePrimaryColor(_primaryColor); + Navigator.of(context).pop(); + } +} diff --git a/lib/module/task/task_bean.dart b/lib/module/task/task_bean.dart index 7d8ddf6..aa3c8b7 100644 --- a/lib/module/task/task_bean.dart +++ b/lib/module/task/task_bean.dart @@ -44,14 +44,14 @@ class TaskBean { schedule = json['schedule'].toString(); saved = json['saved']; sId = json.containsKey('_id') ? json['_id'].toString() : (json.containsKey('id') ? json['id'].toString() : ""); - created = json['created']; + created = int.tryParse(json['created'].toString()); status = json['status']; timestamp = json['timestamp'].toString(); isSystem = json['isSystem']; isDisabled = json['isDisabled']; logPath = json['log_path'].toString(); isPinned = json['isPinned']; - lastExecutionTime = json['last_execution_time']; + lastExecutionTime = int.tryParse(json['last_execution_time'].toString()); lastRunningTime = json['last_running_time']; pid = json['pid'].toString(); } catch (e) { diff --git a/lib/module/task/task_detail/task_detail_page.dart b/lib/module/task/task_detail/task_detail_page.dart index bf27596..16df237 100644 --- a/lib/module/task/task_detail/task_detail_page.dart +++ b/lib/module/task/task_detail/task_detail_page.dart @@ -385,7 +385,7 @@ class _TaskDetailPageState extends ConsumerState { child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () async { diff --git a/lib/module/task/task_page.dart b/lib/module/task/task_page.dart index cf5c532..3698cc4 100644 --- a/lib/module/task/task_page.dart +++ b/lib/module/task/task_page.dart @@ -102,9 +102,17 @@ class _TaskPageState extends State { TaskBean item = list[index]; if (_searchController.text.isEmpty || - (item.name?.toLowerCase().contains(_searchController.text.toLowerCase()) ?? false) || - (item.command?.toLowerCase().contains(_searchController.text.toLowerCase()) ?? false) || - (item.schedule?.contains(_searchController.text.toLowerCase()) ?? false)) { + (item.name + ?.toLowerCase() + .contains(_searchController.text.toLowerCase()) ?? + false) || + (item.command + ?.toLowerCase() + .contains(_searchController.text.toLowerCase()) ?? + false) || + (item.schedule + ?.contains(_searchController.text.toLowerCase()) ?? + false)) { return TaskItemCell(item, ref); } else { return const SizedBox.shrink(); @@ -175,10 +183,16 @@ class TaskItemCell extends StatelessWidget { child: Text( bean.status! == 1 ? "运行" : "停止运行", ), - trailingIcon: bean.status! == 1 ? CupertinoIcons.memories : CupertinoIcons.stop_circle, + trailingIcon: bean.status! == 1 + ? CupertinoIcons.memories + : CupertinoIcons.stop_circle, onPressed: () { Navigator.of(context).pop(); - startCron(context, ref); + if (bean.status! == 1) { + startCron(context, ref); + } else { + stopCron(context, ref); + } }, ), QLCupertinoContextMenuAction( @@ -193,7 +207,8 @@ class TaskItemCell extends StatelessWidget { child: const Text("编辑"), onPressed: () { Navigator.of(context).pop(); - Navigator.of(context).pushNamed(Routes.routeAddTask, arguments: bean); + Navigator.of(context) + .pushNamed(Routes.routeAddTask, arguments: bean); }, trailingIcon: CupertinoIcons.pencil_outline, ), @@ -203,7 +218,9 @@ class TaskItemCell extends StatelessWidget { Navigator.of(context).pop(); pinTask(); }, - trailingIcon: bean.isPinned! == 0 ? CupertinoIcons.pin : CupertinoIcons.pin_slash, + trailingIcon: bean.isPinned! == 0 + ? CupertinoIcons.pin + : CupertinoIcons.pin_slash, ), QLCupertinoContextMenuAction( child: Text(bean.isDisabled! == 0 ? "禁用" : "启用"), @@ -212,7 +229,9 @@ class TaskItemCell extends StatelessWidget { enableTask(); }, isDestructiveAction: true, - trailingIcon: bean.isDisabled! == 0 ? Icons.dnd_forwardslash : Icons.check_circle_outline_sharp, + trailingIcon: bean.isDisabled! == 0 + ? Icons.dnd_forwardslash + : Icons.check_circle_outline_sharp, ), QLCupertinoContextMenuAction( child: const Text("删除"), @@ -229,15 +248,17 @@ class TaskItemCell extends StatelessWidget { return ClipRRect( borderRadius: BorderRadius.circular(15), child: Container( - color: isDark ? const Color(0xff333333) : Theme.of(context).scaffoldBackgroundColor, + color: isDark + ? const Color(0xff333333) + : Theme.of(context).scaffoldBackgroundColor, padding: EdgeInsets.only( left: 5, right: 5, top: 5, bottom: isDark ? 5 : 20, ), - child: ClipRRect( - borderRadius: BorderRadius.circular(15), + child: SizedBox( + height: 200, child: TaskDetailPage( bean, hideAppbar: true, @@ -252,7 +273,9 @@ class TaskItemCell extends StatelessWidget { children: [ Container( width: MediaQuery.of(context).size.width, - color: bean.isPinned == 1 ? ref.watch(themeProvider).themeColor.pinColor() : Colors.transparent, + color: bean.isPinned == 1 + ? ref.watch(themeProvider).themeColor.pinColor() + : Colors.transparent, padding: const EdgeInsets.symmetric( horizontal: 15, vertical: 8, @@ -265,39 +288,59 @@ class TaskItemCell extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Material( - color: Colors.transparent, - child: Text( - bean.name ?? "", - maxLines: 1, - style: TextStyle( - overflow: TextOverflow.ellipsis, - color: ref.watch(themeProvider).themeColor.titleColor(), - fontSize: 18, - ), - ), - ), - const SizedBox( - width: 5, - ), - bean.status == 1 - ? const SizedBox.shrink() - : const SizedBox( - width: 15, - height: 15, - child: CircularProgressIndicator( - strokeWidth: 2, + Expanded( + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + bean.status == 1 + ? const SizedBox.shrink() + : SizedBox( + width: 15, + height: 15, + child: CircularProgressIndicator( + strokeWidth: 2, + color: + ref.watch(themeProvider).primaryColor, + ), + ), + SizedBox( + width: bean.status == 1 ? 0 : 5, + ), + Expanded( + child: Material( + color: Colors.transparent, + child: Text( + bean.name ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: ref + .watch(themeProvider) + .themeColor + .titleColor(), + fontSize: 18, + ), + ), ), ), - const Spacer(), + ], + ), + ), Material( color: Colors.transparent, child: Text( - (bean.lastExecutionTime == null || bean.lastExecutionTime == 0) ? "-" : Utils.formatMessageTime(bean.lastExecutionTime!), + (bean.lastExecutionTime == null || + bean.lastExecutionTime == 0) + ? "-" + : Utils.formatMessageTime( + bean.lastExecutionTime!), maxLines: 1, style: TextStyle( overflow: TextOverflow.ellipsis, - color: ref.watch(themeProvider).themeColor.descColor(), + color: + ref.watch(themeProvider).themeColor.descColor(), fontSize: 12, ), ), @@ -310,21 +353,6 @@ class TaskItemCell extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Material( - color: Colors.transparent, - child: Text( - bean.schedule ?? "", - maxLines: 1, - style: TextStyle( - overflow: TextOverflow.ellipsis, - color: ref.watch(themeProvider).themeColor.descColor(), - fontSize: 12, - ), - ), - ), - const SizedBox( - width: 5, - ), bean.isDisabled == 1 ? const Icon( Icons.dnd_forwardslash, @@ -332,6 +360,22 @@ class TaskItemCell extends StatelessWidget { color: Colors.red, ) : const SizedBox.shrink(), + const SizedBox( + width: 5, + ), + Material( + color: Colors.transparent, + child: Text( + bean.schedule ?? "", + maxLines: 1, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: + ref.watch(themeProvider).themeColor.descColor(), + fontSize: 12, + ), + ), + ), ], ), const SizedBox( @@ -433,7 +477,7 @@ class TaskItemCell extends StatelessWidget { child: Text( "确定", style: TextStyle( - color: primaryColor, + color: ref.watch(themeProvider).primaryColor, ), ), onPressed: () { diff --git a/lib/module/task/task_viewmodel.dart b/lib/module/task/task_viewmodel.dart index 22104fb..261e1c0 100644 --- a/lib/module/task/task_viewmodel.dart +++ b/lib/module/task/task_viewmodel.dart @@ -48,6 +48,7 @@ class TaskViewModel extends BaseViewModel { HttpResponse result = await Api.startTasks([cron]); if (result.success) { list.firstWhere((element) => element.sId == cron).status = 0; + sortList(); notifyListeners(); } else { failToast(result.message, notify: true); @@ -58,6 +59,7 @@ class TaskViewModel extends BaseViewModel { HttpResponse result = await Api.stopTasks([cron]); if (result.success) { list.firstWhere((element) => element.sId == cron).status = 1; + sortList(); notifyListeners(); } else { failToast(result.message, notify: true); diff --git a/lib/utils/update_utils.dart b/lib/utils/update_utils.dart new file mode 100644 index 0000000..2cd9f98 --- /dev/null +++ b/lib/utils/update_utils.dart @@ -0,0 +1,395 @@ +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 { + 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; + } +} diff --git a/pubspec.lock b/pubspec.lock index 32253ba..f722a25 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,245 +5,245 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "22.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.7.2" archive: dependency: transitive description: name: archive - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "3.1.8" + version: "3.1.9" args: dependency: transitive description: name: args - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.0" async: dependency: transitive description: name: async - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.8.2" back_button_interceptor: dependency: "direct main" description: name: back_button_interceptor - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.0.2" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" build: dependency: transitive description: name: build - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.2.1" build_config: dependency: transitive description: name: build_config - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.0" build_daemon: dependency: transitive description: name: build_daemon - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.4" build_runner: dependency: "direct dev" description: name: build_runner - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.7" build_runner_core: dependency: transitive description: name: build_runner_core - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "7.2.3" built_collection: dependency: transitive description: name: built_collection - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "8.1.4" change_app_package_name: dependency: "direct dev" description: name: change_app_package_name - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.1.3" characters: dependency: transitive description: name: characters - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.0" charcode: dependency: transitive description: name: charcode - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.1" checked_yaml: dependency: transitive description: name: checked_yaml - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" cli_util: dependency: transitive description: name: cli_util - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.3.5" clock: dependency: transitive description: name: clock - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.0" code_builder: dependency: transitive description: name: code_builder - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.1.0" collection: dependency: transitive description: name: collection - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.15.0" convert: dependency: transitive description: name: convert - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" crypto: dependency: transitive description: name: crypto - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.4" dart_style: dependency: transitive description: name: dart_style - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.1" dio: dependency: "direct main" description: name: dio - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.0.4" dio_log: dependency: "direct main" description: name: dio_log - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" drag_and_drop_lists: dependency: "direct main" description: name: drag_and_drop_lists - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.3.2+2" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.0" ffi: dependency: transitive description: name: ffi - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.2" file: dependency: transitive description: name: file - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.1.2" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.0" flip_card: dependency: "direct main" description: name: flip_card - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.6.0" flutter: @@ -255,49 +255,56 @@ packages: dependency: "direct dev" description: name: flutter_app_name - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.1.1" + flutter_colorpicker: + dependency: "direct main" + description: + name: flutter_colorpicker + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" flutter_highlight: dependency: "direct main" description: name: flutter_highlight - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.7.0" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.9.2" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.4" flutter_native_splash: dependency: "direct dev" description: name: flutter_native_splash - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.3" flutter_riverpod: dependency: "direct main" description: name: flutter_riverpod - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.3" flutter_slidable: dependency: "direct main" description: name: flutter_slidable - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.0" flutter_test: @@ -314,371 +321,371 @@ packages: dependency: "direct main" description: name: fluttertoast - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "8.0.8" frontend_server_client: dependency: transitive description: name: frontend_server_client - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.2" get_it: dependency: "direct main" description: name: get_it - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "7.2.0" glob: dependency: transitive description: name: glob - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" graphs: dependency: transitive description: name: graphs - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" highlight: dependency: transitive description: name: highlight - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.7.0" http: dependency: transitive description: name: http - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.13.4" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.0.0" image: dependency: transitive description: name: image - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.1" intl: dependency: "direct main" description: name: intl - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.17.0" io: dependency: transitive description: name: io - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.3" js: dependency: transitive description: name: js - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.6.3" json_annotation: dependency: transitive description: name: json_annotation - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.4.0" json_conversion: dependency: "direct dev" description: name: json_conversion - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.0.4" json_conversion_annotation: dependency: "direct main" description: name: json_conversion_annotation - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.0.4" lints: dependency: transitive description: name: lints - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.1" logger: dependency: "direct main" description: name: logger - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.0" logging: dependency: transitive description: name: logging - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.2" matcher: dependency: transitive description: name: matcher - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.12.11" meta: dependency: transitive description: name: meta - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.7.0" mime: dependency: transitive description: name: mime - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.1" move_to_background: dependency: "direct main" description: name: move_to_background - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.2" package_config: dependency: transitive description: name: package_config - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" package_info_plus: dependency: "direct main" description: name: package_info_plus - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.0" package_info_plus_linux: dependency: transitive description: name: package_info_plus_linux - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.3" package_info_plus_macos: dependency: transitive description: name: package_info_plus_macos - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.2" package_info_plus_web: dependency: transitive description: name: package_info_plus_web - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.4" package_info_plus_windows: dependency: transitive description: name: package_info_plus_windows - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.4" path: dependency: transitive description: name: path - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.8.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.5" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.3" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.5" pedantic: dependency: transitive description: name: pedantic - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.11.1" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.4.0" platform: dependency: transitive description: name: platform - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.2" pool: dependency: transitive description: name: pool - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.5.0" process: dependency: transitive description: name: process - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.2.4" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" pubspec_parse: dependency: transitive description: name: pubspec_parse - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.0" riverpod: dependency: transitive description: name: riverpod - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.3" shared_preferences: dependency: "direct main" description: name: shared_preferences - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.12" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.10" shared_preferences_ios: dependency: transitive description: name: shared_preferences_ios - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.9" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.4" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.3" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.4" shelf: dependency: transitive description: name: shelf - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.1" sky_engine: @@ -690,196 +697,196 @@ packages: dependency: transitive description: name: source_gen - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.3" source_span: dependency: transitive description: name: source_span - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.8.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.10.0" state_notifier: dependency: transitive description: name: state_notifier - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.7.2+1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.0" synchronized: dependency: "direct main" description: name: synchronized - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.0" test_api: dependency: transitive description: name: test_api - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.4.3" timing: dependency: transitive description: name: timing - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.0" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.0" universal_io: dependency: transitive description: name: universal_io - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.4" url_launcher: dependency: "direct main" description: name: url_launcher - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.0.18" url_launcher_android: dependency: transitive description: name: url_launcher_android - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.0.14" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.0.14" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.3" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.5" url_launcher_web: dependency: transitive description: name: url_launcher_web - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.6" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.1" watcher: dependency: transitive description: name: watcher - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" win32: dependency: transitive description: name: win32 - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.6" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.2.0" xml: dependency: transitive description: name: xml - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.3.1" yaml: dependency: transitive description: name: yaml - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.0" sdks: diff --git a/pubspec.yaml b/pubspec.yaml index 3df434f..6896455 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: qinglong_app description: A new Flutter project. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 1.0.3+4 +version: 1.0.4+5 environment: sdk: ">=2.15.1 <3.0.0" @@ -31,6 +31,7 @@ dependencies: flip_card: ^0.6.0 package_info_plus: ^1.3.0 url_launcher: ^6.0.18 + flutter_colorpicker: ^1.0.3 dev_dependencies: flutter_native_splash: ^1.3.3 diff --git a/test/widget_test.dart b/test/widget_test.dart index fd3f115..baa5890 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -6,8 +6,5 @@ // tree, read text, and verify that the values of widget properties are correct. void main() { - String time = "Wed Jan 12 2022 20:33:39 GMT+0800 (中国标准时间)"; - var result = DateTime.tryParse(time); - print(result); } diff --git a/version b/version new file mode 100644 index 0000000..a6a3a43 --- /dev/null +++ b/version @@ -0,0 +1 @@ +1.0.4 \ No newline at end of file