Merge branch 'dev'

This commit is contained in:
NewTab
2022-01-21 20:48:09 +08:00
41 changed files with 1536 additions and 421 deletions

View File

@@ -1,3 +1,13 @@
## 1.0.4
* 增加主题切换功能,各种颜色主题随意切换
* 修改无法停止任务的bug
* 修改脚本管理页面json解析失败的bug
* 修改变更配置文件没有及时刷新UI的bug
* 增加版本更新提醒功能(需要手机能联通github)
* 登录页面长按青龙logo可以呼出网络请求日志,再次长按可关闭
* 打开系统最近任务时展示APP的名称
* 优化使用体验
## 1.0.3
* 解决2.10.12及以下版本无法登录的问题

View File

@@ -12,8 +12,8 @@ iOS端暂无上架打算用户自行下载main分支源码编译安装
<p float="left">
<img src="./art/14.jpg" width="200" />
<img src="./art/1.jpg" width="200" />
<img src="./art/8.jpg" width="200" />
<img src="./art/9.jpg" width="200" />
</p>
<p float="left">
@@ -35,9 +35,9 @@ iOS端暂无上架打算用户自行下载main分支源码编译安装
<p float="left">
<img src="./art/13.jpg" width="200" />
<img src="./art/8.jpg" width="200" />
</p>
### 不支持的功能
>* 应用设置
>* 通知设置

BIN
art/14.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -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

View File

@@ -87,7 +87,7 @@ class Api {
static Future<HttpResponse<NullResponse>> stopTasks(List<String> crons) async {
return await Http.put<NullResponse>(
Url.runTasks,
Url.stopTasks,
crons,
);
}

View File

@@ -76,4 +76,6 @@ class Url {
}
return false;
}
static String checkUpdateUrl = "https://raw.githubusercontent.com/qinglong-app/qinglong_app/main/version";
}

View File

@@ -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<dynamic>? 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(

View File

@@ -4,3 +4,4 @@ String spUserName = "username";
String spPassWord = "password";
String spTheme = "dart_mode";
String spSecretLogined = "secret_logined";
String spCustomColor = "customColor";

View File

@@ -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<UserInfoViewModel>().primaryColor);
primaryColor = Color(getIt<UserInfoViewModel>().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<UserInfoViewModel>().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<MaterialState> 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<MaterialState> 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<MaterialState> 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<MaterialState> 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();

View File

@@ -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;
}

View File

@@ -57,7 +57,8 @@ class QlAppState extends ConsumerState<QlApp> {
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<ThemeViewModel>(themeProvider).currentTheme,
onGenerateRoute: (setting) {
@@ -65,9 +66,6 @@ class QlAppState extends ConsumerState<QlApp> {
},
home: Builder(
builder: (context) {
if (!kReleaseMode) {
showDebugBtn(context, btnColor: Colors.blue);
}
return getIt<UserInfoViewModel>().isLogined() ? const HomePage() : const LoginPage();
},
),

View File

@@ -1 +0,0 @@

View File

@@ -49,7 +49,7 @@ class _ConfigEditPageState extends ConsumerState<ConfigEditPage> {
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<ConfigEditPage> {
child: TextField(
focusNode: node,
style: TextStyle(
color: ref.read(themeProvider).themeColor.descColor(),
color: ref.read(themeProvider).themeColor.titleColor(),
fontSize: 14,
),
controller: _controller,

View File

@@ -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<ConfigPage>
Widget build(BuildContext context) {
return BaseStateWidget<ConfigViewModel>(
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<ConfigPage>
"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(() {});
}
});
}
}

View File

@@ -15,7 +15,7 @@ class EnvBean {
EnvBean.fromJson(Map<String, dynamic> 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'];

View File

@@ -249,7 +249,7 @@ class _TaskDetailPageState extends ConsumerState<EnvDetailPage> {
child: Text(
"确定",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
),
),
onPressed: () async {

View File

@@ -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: () {

View File

@@ -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<HomePage> {
initTitles();
_title = titles[0].title;
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
update();
});
}
@override
@@ -185,6 +189,16 @@ class _HomePageState extends ConsumerState<HomePage> {
),
);
}
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 {

View File

@@ -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'];
}

View File

@@ -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<LoginPage> {
final TextEditingController _hostController = TextEditingController(text: getIt<UserInfoViewModel>().host);
final TextEditingController _hostController =
TextEditingController(text: getIt<UserInfoViewModel>().host);
final TextEditingController _userNameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _cIdController = TextEditingController();
@@ -38,7 +41,8 @@ class _LoginPageState extends ConsumerState<LoginPage> {
super.initState();
useSecretLogin = getIt<UserInfoViewModel>().useSecretLogined;
if (getIt<UserInfoViewModel>().userName != null && getIt<UserInfoViewModel>().userName!.isNotEmpty) {
if (getIt<UserInfoViewModel>().userName != null &&
getIt<UserInfoViewModel>().userName!.isNotEmpty) {
if (getIt<UserInfoViewModel>().useSecretLogined) {
_cIdController.text = getIt<UserInfoViewModel>().userName!;
} else {
@@ -48,7 +52,8 @@ class _LoginPageState extends ConsumerState<LoginPage> {
} else {
rememberPassword = false;
}
if (getIt<UserInfoViewModel>().passWord != null && getIt<UserInfoViewModel>().passWord!.isNotEmpty) {
if (getIt<UserInfoViewModel>().passWord != null &&
getIt<UserInfoViewModel>().passWord!.isNotEmpty) {
if (getIt<UserInfoViewModel>().useSecretLogined) {
_cSecretController.text = getIt<UserInfoViewModel>().passWord!;
} else {
@@ -57,6 +62,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
}
getIt<UserInfoViewModel>().updateToken("");
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
update();
if (useSecretLogin) {
cardKey.currentState?.toggleCard();
}
@@ -107,9 +113,28 @@ class _LoginPageState extends ConsumerState<LoginPage> {
),
),
),
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<LoginPage> {
),
FlipCard(
key: cardKey,
flipOnTouch: false,
onFlipDone: (back) {
useSecretLogin = back;
setState(() {});
@@ -164,7 +190,8 @@ class _LoginPageState extends ConsumerState<LoginPage> {
},
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<LoginPage> {
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<LoginPage> {
},
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<LoginPage> {
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<LoginPage> {
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<LoginPage> {
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<LoginPage> {
onPressed: () {
Http.pushedLoginPage = false;
Utils.hideKeyBoard(context);
getIt<UserInfoViewModel>().updateHost(_hostController.text);
getIt<UserInfoViewModel>()
.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<LoginPage> {
loginSuccess(response, userName, password);
} else if (loginByUserName() && response.code == 401) {
//可能用户使用的是老版本qinglong
HttpResponse<LoginBean> oldResponse = await Api.loginOld(userName, password);
HttpResponse<LoginBean> oldResponse =
await Api.loginOld(userName, password);
if (oldResponse.success) {
loginSuccess(oldResponse, userName, password);
} else {
@@ -398,11 +438,14 @@ class _LoginPageState extends ConsumerState<LoginPage> {
setState(() {});
}
void loginSuccess(HttpResponse<LoginBean> response, String userName, String password) {
void loginSuccess(
HttpResponse<LoginBean> response, String userName, String password) {
getIt<UserInfoViewModel>().updateToken(response.bean?.token ?? "");
getIt<UserInfoViewModel>().useSecretLogin(!loginByUserName());
if (rememberPassword) {
getIt<UserInfoViewModel>().updateUserName(userName, password);
} else {
getIt<UserInfoViewModel>().updateUserName("", "");
}
Navigator.of(context).pushReplacementNamed(Routes.routeHomePage);
}
@@ -412,9 +455,11 @@ class _LoginPageState extends ConsumerState<LoginPage> {
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<LoginPage> {
child: Text(
"确定",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
),
),
onPressed: () async {
Navigator.of(context).pop(true);
HttpResponse<LoginBean> response = await Api.loginTwo(userName, password, twoFact);
HttpResponse<LoginBean> response =
await Api.loginTwo(userName, password, twoFact);
if (response.success) {
loginSuccess(response, userName, password);
} else {
@@ -481,4 +527,15 @@ class _LoginPageState extends ConsumerState<LoginPage> {
}
});
}
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();
}
}
}

View File

@@ -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<AboutPage> {
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<AboutPage> {
child: Text(
"Telegram频道",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
fontSize: 16,
),
),
@@ -92,7 +107,7 @@ class _AboutPageState extends ConsumerState<AboutPage> {
child: Text(
"版本更新",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
fontSize: 16,
),
),
@@ -107,7 +122,7 @@ class _AboutPageState extends ConsumerState<AboutPage> {
child: Text(
"项目地址",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
fontSize: 16,
),
),

View File

@@ -37,10 +37,6 @@ class _AddDependencyPageState extends ConsumerState<AddDependencyPage> {
title: "新增依赖",
actions: [
InkWell(
onLongPress: () {
showDebugBtn(context, btnColor: Colors.blue);
WidgetsBinding.instance?.endOfFrame;
},
onTap: () {
submit();
},

View File

@@ -24,10 +24,10 @@ class DependencyBean {
DependencyBean.fromJson(Map<String, dynamic> 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<String>();
remark = json['remark'];

View File

@@ -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: () {

View File

@@ -13,7 +13,7 @@ class LoginLogBean {
{this.timestamp, this.address, this.ip, this.platform, this.status});
LoginLogBean.fromJson(Map<String, dynamic> json) {
timestamp = json['timestamp'];
timestamp = int.tryParse(json['timestamp'].toString());
address = json['address'];
ip = json['ip'];
platform = json['platform'];

View File

@@ -86,7 +86,7 @@ class _LoginLogPageState extends ConsumerState<LoginLogPage> with LazyLoadState<
trailing: item.status == 0
? Icon(
CupertinoIcons.checkmark_circle,
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
size: 16,
)
: const Icon(

View File

@@ -59,7 +59,10 @@ class _OtherPageState extends ConsumerState<OtherPage> {
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<OtherPage> {
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<OtherPage> {
);
},
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<OtherPage> {
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<OtherPage> {
},
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<OtherPage> {
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<OtherPage> {
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<OtherPage> {
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<OtherPage> {
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<OtherPage> {
child: Text(
"确定",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
),
),
onPressed: () {
getIt<UserInfoViewModel>().updateToken("");
Navigator.of(context).pushReplacementNamed(Routes.routeLogin);
Navigator.of(context)
.pushReplacementNamed(Routes.routeLogin);
},
),
],

View File

@@ -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<ScriptChildren>? 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<String, dynamic> json) {
title = json['title'];
value = json['value'];
key = json['key'];
mtime = json['mtime'];
disabled = json['disabled'];
if (json['children'] != null) {
children = <ScriptChildren>[];
@@ -36,8 +26,6 @@ class ScriptBean {
final Map<String, dynamic> data = new Map<String, dynamic>();
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<String, dynamic> json) {
title = json['title'];
value = json['value'];
key = json['key'];
mtime = json['mtime'];
parent = json['parent'];
}
@@ -71,8 +55,6 @@ class ScriptChildren {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['title'] = this.title;
data['value'] = this.value;
data['key'] = this.key;
data['mtime'] = this.mtime;
data['parent'] = this.parent;
return data;
}

View File

@@ -98,7 +98,7 @@ class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage> with LazyLo
child: Text(
"确定",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
),
),
onPressed: () async {

View File

@@ -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<ThemePage> {
late Color _primaryColor;
List<Color> 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(
"上午1000",
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();
}
}

View File

@@ -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) {

View File

@@ -385,7 +385,7 @@ class _TaskDetailPageState extends ConsumerState<TaskDetailPage> {
child: Text(
"确定",
style: TextStyle(
color: primaryColor,
color: ref.watch(themeProvider).primaryColor,
),
),
onPressed: () async {

View File

@@ -102,9 +102,17 @@ class _TaskPageState extends State<TaskPage> {
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: () {

View File

@@ -48,6 +48,7 @@ class TaskViewModel extends BaseViewModel {
HttpResponse<NullResponse> 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<NullResponse> result = await Api.stopTasks([cron]);
if (result.success) {
list.firstWhere((element) => element.sId == cron).status = 1;
sortList();
notifyListeners();
} else {
failToast(result.message, notify: true);

395
lib/utils/update_utils.dart Normal file
View File

@@ -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<String?> 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<bool> show() {
try {
if (isShowing()) {
return Future<bool>.value(false);
}
showDialog<bool>(
context: _context,
barrierDismissible: false,
builder: (BuildContext context) {
return WillPopScope(onWillPop: () => Future<bool>.value(false), child: _widget);
});
_isShowing = true;
return Future<bool>.value(true);
} catch (err) {
_isShowing = false;
return Future<bool>.value(false);
}
}
/// 隐藏弹窗
Future<bool> dismiss() {
try {
if (_isShowing) {
_isShowing = false;
Navigator.pop(_context);
return Future<bool>.value(true);
} else {
return Future<bool>.value(false);
}
} catch (err) {
return Future<bool>.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<UpdateWidget> {
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: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
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;
}
}

View File

@@ -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:

View File

@@ -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

View File

@@ -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);
}

1
version Normal file
View File

@@ -0,0 +1 @@
1.0.4