添加关于页面

This commit is contained in:
jyuesong
2022-01-20 14:05:32 +08:00
parent 130459d646
commit a97aa1f347
10 changed files with 526 additions and 219 deletions

View File

@@ -3,7 +3,7 @@
* 支持修改用户名密码(仅限使用用户名密码登录用户)
* 支持脚本的编辑删除
* 支持client_id登录之后只启用部分模块
* 支持两步验证
## 1.0.1

View File

@@ -1,31 +1,37 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="work.master.qinglongapp">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="青龙"
<uses-permission android:name="android.permission.INTERNET" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
<application
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:label="青龙">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.

View File

@@ -28,6 +28,11 @@
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
<string>http</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>

View File

@@ -6,6 +6,7 @@ import 'package:qinglong_app/module/env/env_bean.dart';
import 'package:qinglong_app/module/env/env_detail_page.dart';
import 'package:qinglong_app/module/home/home_page.dart';
import 'package:qinglong_app/module/login/login_page.dart';
import 'package:qinglong_app/module/others/about_page.dart';
import 'package:qinglong_app/module/others/dependencies/add_dependency_page.dart';
import 'package:qinglong_app/module/others/dependencies/dependency_page.dart';
import 'package:qinglong_app/module/others/login_log/login_log_page.dart';
@@ -37,6 +38,7 @@ class Routes {
static const String routeScriptAdd = "/script/add";
static const String routeDependency = "/Dependency";
static const String routeUpdatePassword = "/updatePassword";
static const String routeAbout = "/about";
static Route<dynamic>? generateRoute(RouteSettings settings) {
switch (settings.name) {
@@ -116,6 +118,10 @@ class Routes {
return CupertinoPageRoute(
builder: (context) => const UpdatePasswordPage(),
);
case routeAbout:
return CupertinoPageRoute(
builder: (context) => const AboutPage(),
);
case routeScriptUpdate:
return CupertinoPageRoute(
builder: (context) => ScriptEditPage(

View File

@@ -54,22 +54,25 @@ class QlAppState extends ConsumerState<QlApp> {
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: MaterialApp(
locale: const Locale('zh', 'cn'),
navigatorKey: navigatorState,
theme: ref.watch<ThemeViewModel>(themeProvider).currentTheme,
onGenerateRoute: (setting) {
return Routes.generateRoute(setting);
},
home: Builder(
builder: (context) {
if (!kReleaseMode) {
showDebugBtn(context, btnColor: Colors.blue);
}
return getIt<UserInfoViewModel>().isLogined() ? const HomePage() : const LoginPage();
child: MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window).copyWith(textScaleFactor: 1,),
child: MaterialApp(
locale: const Locale('zh', 'cn'),
navigatorKey: navigatorState,
theme: ref.watch<ThemeViewModel>(themeProvider).currentTheme,
onGenerateRoute: (setting) {
return Routes.generateRoute(setting);
},
home: Builder(
builder: (context) {
if (!kReleaseMode) {
showDebugBtn(context, btnColor: Colors.blue);
}
return getIt<UserInfoViewModel>().isLogined() ? const HomePage() : const LoginPage();
},
),
// home: LoginPage(),
),
// home: LoginPage(),
),
);
}

View File

@@ -81,194 +81,203 @@ class _LoginPageState extends ConsumerState<LoginPage> {
Scaffold(
backgroundColor: Colors.transparent,
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 40,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).size.height / 10,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 40,
),
SizedBox(
height: 50,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
child: Image.asset(
"assets/images/login_tip.png",
height: 45,
),
),
const Positioned(
top: 10,
left: 0,
child: Text(
"登录",
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
),
Positioned(
top: 5,
right: 0,
child: Image.asset(
"assets/images/ql.png",
height: 45,
),
),
],
),
),
SizedBox(
height: MediaQuery.of(context).size.height / 15,
),
const Text(
"域名:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _hostController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "http://1.1.1.1:5700",
),
autofocus: false,
),
FlipCard(
key: cardKey,
onFlipDone: (back) {
useSecretLogin = back;
setState(() {});
},
direction: FlipDirection.HORIZONTAL,
front: SizedBox(
height: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 15,
),
const Text(
"用户名:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _userNameController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入用户名",
),
autofocus: false,
),
const SizedBox(
height: 15,
),
const Text(
"密码:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入密码",
),
autofocus: false,
),
const SizedBox(
height: 10,
),
],
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).size.height / 10,
),
),
back: SizedBox(
height: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 15,
),
const Text(
"client_id:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
SizedBox(
height: 50,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
child: Image.asset(
"assets/images/login_tip.png",
height: 45,
),
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _cIdController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入client_id",
const Positioned(
top: 10,
left: 0,
child: Text(
"登录",
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
),
autofocus: false,
),
const SizedBox(
height: 15,
),
const Text(
"client_secret:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
Positioned(
top: 5,
right: 0,
child: Image.asset(
"assets/images/ql.png",
height: 45,
),
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _cSecretController,
obscureText: true,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入client_secret",
),
autofocus: false,
),
const SizedBox(
height: 10,
),
],
],
),
),
),
SizedBox(
height: MediaQuery.of(context).size.height / 15,
),
const Text(
"域名:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _hostController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "http://1.1.1.1:5700",
),
autofocus: false,
),
FlipCard(
key: cardKey,
onFlipDone: (back) {
useSecretLogin = back;
setState(() {});
},
direction: FlipDirection.HORIZONTAL,
front: SizedBox(
height: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 15,
),
const Text(
"用户名:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _userNameController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入用户名",
),
autofocus: false,
),
const SizedBox(
height: 15,
),
const Text(
"密码:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入密码",
),
autofocus: false,
),
const SizedBox(
height: 10,
),
],
),
),
back: SizedBox(
height: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 15,
),
const Text(
"client_id:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _cIdController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入client_id",
),
autofocus: false,
),
const SizedBox(
height: 15,
),
const Text(
"client_secret:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
TextField(
onChanged: (_) {
setState(() {});
},
controller: _cSecretController,
obscureText: true,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入client_secret",
),
autofocus: false,
),
const SizedBox(
height: 10,
),
],
),
),
),
],
),
Row(
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25,),
child: Row(
children: [
Checkbox(
value: rememberPassword,
@@ -298,12 +307,16 @@ class _LoginPageState extends ConsumerState<LoginPage> {
),
),
),
SizedBox(width: 10,),
],
),
const SizedBox(
height: 30,
),
SizedBox(
),
const SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40,),
child: SizedBox(
width: MediaQuery.of(context).size.width - 80,
child: IgnorePointer(
ignoring: !canClickLoginBtn(),
@@ -334,8 +347,8 @@ class _LoginPageState extends ConsumerState<LoginPage> {
),
),
),
],
),
),
],
),
),
),

View File

@@ -0,0 +1,128 @@
import 'package:flutter/material.dart';
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:url_launcher/url_launcher.dart';
import '../../main.dart';
/// @author NewTab
class AboutPage extends ConsumerStatefulWidget {
const AboutPage({Key? key}) : super(key: key);
@override
ConsumerState createState() => _AboutPageState();
}
class _AboutPageState extends ConsumerState<AboutPage> {
String desc = "";
@override
void initState() {
super.initState();
getInfo();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: QlAppBar(
canBack: true,
title: "关于",
),
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: MediaQuery.of(context).size.height / 10,
),
Image.asset(
"assets/images/ql.png",
height: 50,
),
const SizedBox(
height: 30,
),
Text(
"青龙",
style: TextStyle(
fontWeight: FontWeight.w600,
color: ref.watch(themeProvider).themeColor.titleColor(),
fontSize: 20,
),
),
const SizedBox(
height: 15,
),
Text(
"Version ${desc}",
style: TextStyle(
color: ref.watch(themeProvider).themeColor.descColor(),
fontSize: 16,
),
),
const Spacer(),
Row(
children: [
const Spacer(),
GestureDetector(
onTap: () {
_launchURL("https://github.com/qinglong-app/qinglong_app/releases");
},
child: Text(
"版本更新",
style: TextStyle(
color: primaryColor,
fontSize: 16,
),
),
),
const SizedBox(
width: 15,
),
GestureDetector(
onTap: () {
_launchURL("https://github.com/qinglong-app/qinglong_app");
},
child: Text(
"项目地址",
style: TextStyle(
color: primaryColor,
fontSize: 16,
),
),
),
const Spacer(),
],
),
const SizedBox(
height: 15,
),
],
),
),
);
}
void _launchURL(String _url) async {
try {
await launch(_url);
} catch (e) {
logger.e(e);
}
}
void getInfo() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version;
desc = version;
setState(() {});
}
}

View File

@@ -252,6 +252,7 @@ class _OtherPageState extends ConsumerState<OtherPage> {
padding: const EdgeInsets.only(
left: 15,
right: 15,
top: 5,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -266,13 +267,51 @@ class _OtherPageState extends ConsumerState<OtherPage> {
),
const Spacer(),
CupertinoSwitch(
value: ref.watch(themeProvider).isInDartMode(),
onChanged: (open) {
ref.watch(themeProvider).changeThemeReal(open);
}),
value: ref.watch(themeProvider).isInDartMode(),
onChanged: (open) {
ref.watch(themeProvider).changeThemeReal(open);
},
),
],
),
),
const Divider(
indent: 15,
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pushNamed(
Routes.routeAbout,
);
},
child: Padding(
padding: const EdgeInsets.only(
left: 15,
right: 15,
top: 5,
bottom: 15,
),
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,
),
],
),
),
),
],
),
),

View File

@@ -352,6 +352,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.13.4"
http_multi_server:
dependency: transitive
description:
@@ -471,6 +478,48 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
package_info_plus_linux:
dependency: transitive
description:
name: package_info_plus_linux
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
package_info_plus_macos:
dependency: transitive
description:
name: package_info_plus_macos
url: "https://pub.flutter-io.cn"
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"
source: hosted
version: "1.0.2"
package_info_plus_web:
dependency: transitive
description:
name: package_info_plus_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
package_info_plus_windows:
dependency: transitive
description:
name: package_info_plus_windows
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
path:
dependency: transitive
description:
@@ -728,6 +777,62 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.4"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.18"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.14"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.14"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.5"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.6"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
vector_math:
dependency: transitive
description:

View File

@@ -28,6 +28,8 @@ dependencies:
fluttertoast: ^8.0.8
move_to_background: ^1.0.2
flip_card: ^0.6.0
package_info_plus: ^1.3.0
url_launcher: ^6.0.18
dev_dependencies:
flutter_native_splash: ^1.3.3