提交 67ef61b0 authored 作者: 王鹏飞's avatar 王鹏飞

Refactor HttpService and Access utility for improved permission handling and code clarity

- Updated HttpService to enhance header management and error handling. - Improved Access utility to include permission explanation dialogs before requesting permissions. - Modified AssetsPicker to check for permissions before accessing the camera or gallery. - Adjusted constants for server URLs and agreements for better maintainability. - Cleaned up pubspec.yaml and pubspec.lock by removing unused dependencies.
上级 883b112b
...@@ -51,30 +51,18 @@ android { ...@@ -51,30 +51,18 @@ android {
versionName flutterVersionName versionName flutterVersionName
} }
signingConfigs { signingConfigs {
// release {
// keyAlias keystoreProperties['keyAlias']
// keyPassword keystoreProperties['keyPassword']
// storeFile file(keystoreProperties['storeFile'])
// storePassword keystoreProperties['storePassword']
// }
release{ release{
keyAlias keystoreProperties['keyAlias'] keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword'] keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile']) storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword'] storePassword keystoreProperties['storePassword']
} }
debug{ // debug 使用默认签名
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
} }
buildTypes { buildTypes {
debug { debug {
signingConfig signingConfigs.release // 使用默认 debug 签名
} }
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
</queries> </queries>
<application <application
android:label="紫荆数智学堂" android:label="清控紫荆数智学堂"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
......
storePassword=123456 storePassword=zijing123456
keyPassword=zijing123456
keyPassword=123456
keyAlias=zijing keyAlias=zijing
storeFile=../zijing_release.jks
storeFile=/Users/apple/zijiing_key.jks
#storeFile=zijiing_key.jks
\ No newline at end of file
PODS: PODS:
- audio_session (0.0.1): - audio_session (0.0.1):
- Flutter - Flutter
- Bugly (2.6.1)
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter - Flutter
- ReachabilitySwift - ReachabilitySwift
...@@ -10,9 +9,6 @@ PODS: ...@@ -10,9 +9,6 @@ PODS:
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_app_update (0.0.1): - flutter_app_update (0.0.1):
- Flutter - Flutter
- flutter_bugly (0.0.1):
- Bugly
- Flutter
- flutter_inapp_purchase (0.0.1): - flutter_inapp_purchase (0.0.1):
- Flutter - Flutter
- flutter_inappwebview_ios (0.0.1): - flutter_inappwebview_ios (0.0.1):
...@@ -47,10 +43,6 @@ PODS: ...@@ -47,10 +43,6 @@ PODS:
- permission_handler_apple (9.1.1): - permission_handler_apple (9.1.1):
- Flutter - Flutter
- ReachabilitySwift (5.0.0) - ReachabilitySwift (5.0.0)
- screen_protector (1.2.1):
- Flutter
- ScreenProtectorKit (~> 1.3.1)
- ScreenProtectorKit (1.3.1)
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
...@@ -70,7 +62,6 @@ DEPENDENCIES: ...@@ -70,7 +62,6 @@ DEPENDENCIES:
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`) - flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
- flutter_bugly (from `.symlinks/plugins/flutter_bugly/ios`)
- flutter_inapp_purchase (from `.symlinks/plugins/flutter_inapp_purchase/ios`) - flutter_inapp_purchase (from `.symlinks/plugins/flutter_inapp_purchase/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`) - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_sound (from `.symlinks/plugins/flutter_sound/ios`) - flutter_sound (from `.symlinks/plugins/flutter_sound/ios`)
...@@ -81,18 +72,15 @@ DEPENDENCIES: ...@@ -81,18 +72,15 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- screen_protector (from `.symlinks/plugins/screen_protector/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`) - sqflite (from `.symlinks/plugins/sqflite/darwin`)
- tobias (from `.symlinks/plugins/tobias/ios`) - tobias (from `.symlinks/plugins/tobias/ios`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- Bugly
- flutter_sound_core - flutter_sound_core
- OrderedSet - OrderedSet
- ReachabilitySwift - ReachabilitySwift
- ScreenProtectorKit
- WechatOpenSDK-XCFramework - WechatOpenSDK-XCFramework
EXTERNAL SOURCES: EXTERNAL SOURCES:
...@@ -106,8 +94,6 @@ EXTERNAL SOURCES: ...@@ -106,8 +94,6 @@ EXTERNAL SOURCES:
:path: Flutter :path: Flutter
flutter_app_update: flutter_app_update:
:path: ".symlinks/plugins/flutter_app_update/ios" :path: ".symlinks/plugins/flutter_app_update/ios"
flutter_bugly:
:path: ".symlinks/plugins/flutter_bugly/ios"
flutter_inapp_purchase: flutter_inapp_purchase:
:path: ".symlinks/plugins/flutter_inapp_purchase/ios" :path: ".symlinks/plugins/flutter_inapp_purchase/ios"
flutter_inappwebview_ios: flutter_inappwebview_ios:
...@@ -128,8 +114,6 @@ EXTERNAL SOURCES: ...@@ -128,8 +114,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple: permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios" :path: ".symlinks/plugins/permission_handler_apple/ios"
screen_protector:
:path: ".symlinks/plugins/screen_protector/ios"
shared_preferences_foundation: shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin" :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite: sqflite:
...@@ -139,12 +123,10 @@ EXTERNAL SOURCES: ...@@ -139,12 +123,10 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
audio_session: 9bdd3bf46960d4322cb8c3cb6138295dcfe84eee audio_session: 9bdd3bf46960d4322cb8c3cb6138295dcfe84eee
Bugly: 217ac2ce5f0f2626d43dbaa4f70764c953a26a31
connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89 device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9 flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
flutter_bugly: 12197049262a692eab0bd2834d465a5647d920c2
flutter_inapp_purchase: 722da12971a50f306f37e62fc1aaf576b1cbecf6 flutter_inapp_purchase: 722da12971a50f306f37e62fc1aaf576b1cbecf6
flutter_inappwebview_ios: 25b61a1b550d1068e4ddaf490fc1d03c2ce6828d flutter_inappwebview_ios: 25b61a1b550d1068e4ddaf490fc1d03c2ce6828d
flutter_sound: 49be32081884d275fe91d48262f4b1fcd86e10d3 flutter_sound: 49be32081884d275fe91d48262f4b1fcd86e10d3
...@@ -158,8 +140,6 @@ SPEC CHECKSUMS: ...@@ -158,8 +140,6 @@ SPEC CHECKSUMS:
path_provider_foundation: 608fcb11be570ce83519b076ab6a1fffe2474f05 path_provider_foundation: 608fcb11be570ce83519b076ab6a1fffe2474f05
permission_handler_apple: 3787117e48f80715ff04a3830ca039283d6a4f29 permission_handler_apple: 3787117e48f80715ff04a3830ca039283d6a4f29
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
screen_protector: 3d90d44ac886b25335aebd93230b454aef45794a
ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4
shared_preferences_foundation: 0b09b969fb36da5551c0bc4a2dbd9d0ff9387478 shared_preferences_foundation: 0b09b969fb36da5551c0bc4a2dbd9d0ff9387478
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3 sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
tobias: 50b529c2501d277c83fef9976803a001eb58a057 tobias: 50b529c2501d277c83fef9976803a001eb58a057
......
...@@ -489,7 +489,7 @@ ...@@ -489,7 +489,7 @@
DEVELOPMENT_TEAM = MYN43C5WGE; DEVELOPMENT_TEAM = MYN43C5WGE;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "紫荆数智学堂"; INFOPLIST_KEY_CFBundleDisplayName = "清控紫荆数智学堂";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
...@@ -662,7 +662,7 @@ ...@@ -662,7 +662,7 @@
DEVELOPMENT_TEAM = MYN43C5WGE; DEVELOPMENT_TEAM = MYN43C5WGE;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "紫荆数智学堂"; INFOPLIST_KEY_CFBundleDisplayName = "清控紫荆数智学堂";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
...@@ -688,7 +688,7 @@ ...@@ -688,7 +688,7 @@
DEVELOPMENT_TEAM = MYN43C5WGE; DEVELOPMENT_TEAM = MYN43C5WGE;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "紫荆数智学堂"; INFOPLIST_KEY_CFBundleDisplayName = "清控紫荆数智学堂";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>紫荆数智学堂</string> <string>清控紫荆数智学堂</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
...@@ -47,15 +47,15 @@ ...@@ -47,15 +47,15 @@
<true/> <true/>
</dict> </dict>
<key>NSAppleMusicUsageDescription</key> <key>NSAppleMusicUsageDescription</key>
<string>紫荆数智学堂需要访问媒体</string> <string>清控紫荆数智学堂需要访问媒体</string>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>紫荆数智学堂需要访问相机</string> <string>清控紫荆数智学堂需要访问相机</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>紫荆数智学堂需要访问麦克风</string> <string>清控紫荆数智学堂需要访问麦克风</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>紫荆数智学堂需要访问照片</string> <string>清控紫荆数智学堂需要访问照片</string>
<key>NSSpeechRecognitionUsageDescription</key> <key>NSSpeechRecognitionUsageDescription</key>
<string>紫荆数智学堂需要语言识别</string> <string>清控紫荆数智学堂需要语言识别</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
......
...@@ -18,13 +18,6 @@ class Global { ...@@ -18,13 +18,6 @@ class Global {
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// 初始化数据库 // 初始化数据库
SqlManager.init(); SqlManager.init();
// 检测网络变化
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
Console.log('网络变化--------------------------------$result');
if (result != ConnectivityResult.none && UserStore.to.isLogin) {
upload();
}
});
// 监测app生命周期 // 监测app生命周期
// WidgetsBinding.instance?.addObserver(AppLifecycleObserver()); // WidgetsBinding.instance?.addObserver(AppLifecycleObserver());
...@@ -32,12 +25,20 @@ class Global { ...@@ -32,12 +25,20 @@ class Global {
// 配置存储 // 配置存储
Get.putAsync<StorageService>(() => StorageService().init()), Get.putAsync<StorageService>(() => StorageService().init()),
]).whenComplete(() { ]).whenComplete(() {
// 先注册依赖最广的用户状态,避免启动早期被其他模块提前读取。
Get.put<UserStore>(UserStore());
// 网络 // 网络
Get.put<HttpService>(HttpService()); Get.put<HttpService>(HttpService());
//配置基本设置 //配置基本设置
Get.put<ConfigStore>(ConfigStore()); Get.put<ConfigStore>(ConfigStore());
// });
Get.put<UserStore>(UserStore());
// UserStore 注册后再监听网络变化,避免回调触发时找不到实例。
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
Console.log('网络变化--------------------------------$result');
if (result != ConnectivityResult.none && UserStore.to.isLogin) {
upload();
}
}); });
} }
......
...@@ -6,7 +6,6 @@ import 'package:flutter_book/routes/index.dart'; ...@@ -6,7 +6,6 @@ import 'package:flutter_book/routes/index.dart';
import 'package:flutter_book/store/index.dart'; import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_bugly/flutter_bugly.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:oktoast/oktoast.dart'; import 'package:oktoast/oktoast.dart';
...@@ -19,17 +18,9 @@ void main() { ...@@ -19,17 +18,9 @@ void main() {
Future.wait([ Future.wait([
UserStore.to.profile(), UserStore.to.profile(),
]).whenComplete(() { ]).whenComplete(() {
FlutterBugly.postCatchedException(() { // 如果需要 ensureInitialized,请在这里运行。
// 如果需要 ensureInitialized,请在这里运行。 WidgetsFlutterBinding.ensureInitialized();
WidgetsFlutterBinding.ensureInitialized(); runApp(const MyApp());
runApp(const MyApp());
FlutterBugly.init(
androidAppId: "8b4da96535",
iOSAppId: "290703a371",
);
});
// runApp(const MyApp());
//FlutterNativeSplash.remove();
}); });
}); });
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light); SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
...@@ -46,7 +37,7 @@ class MyApp extends StatelessWidget { ...@@ -46,7 +37,7 @@ class MyApp extends StatelessWidget {
builder: (context, child) => GetBuilder<ConfigStore>( builder: (context, child) => GetBuilder<ConfigStore>(
builder: (config) => MaterialApp.router( builder: (config) => MaterialApp.router(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: '紫荆数智学堂', title: '清控紫荆数智学堂',
theme: AppTheme.light, theme: AppTheme.light,
darkTheme: AppTheme.dark, darkTheme: AppTheme.dark,
themeMode: ThemeMode.light, themeMode: ThemeMode.light,
......
...@@ -116,12 +116,114 @@ class LoginController extends GetxController { ...@@ -116,12 +116,114 @@ class LoginController extends GetxController {
update(); update();
} }
/// 显示用户协议弹框
Future<bool> _showAgreementDialog(BuildContext context) async {
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0.w),
),
contentPadding: EdgeInsets.zero,
title: Center(
child: Padding(
padding: EdgeInsets.only(top: 24.w),
child: Text(
'用户协议及隐私协议',
style: TextStyle(
fontSize: 16.w,
fontWeight: Fonts.medium,
color: Colours.c3,
),
),
),
),
content: Wrap(
children: [
Container(
margin: EdgeInsets.only(top: 20.w, bottom: 29.w),
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Center(
child: Text(
'已阅读并同意《用户协议》、《隐私政策》',
style: TextStyle(
fontSize: 14.w,
color: Colours.c6,
),
textAlign: TextAlign.center,
),
),
),
Container(
height: 1.w,
width: double.infinity,
color: Colours.cLine,
),
],
),
actionsPadding: EdgeInsets.zero,
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
width: 105.w,
height: 39.5.w,
color: Colours.cFF,
alignment: Alignment.center,
child: Text(
'不同意',
style: TextStyle(
fontSize: 15.w,
fontWeight: Fonts.medium,
color: Colours.c6,
),
),
),
),
Container(
height: 39.5.w,
width: 1,
color: Colours.cLine,
),
GestureDetector(
onTap: () {
Navigator.of(context).pop();
setAgree();
},
child: Container(
width: 105.w,
height: 39.5.w,
color: Colours.cFF,
alignment: Alignment.center,
child: Text(
'同意',
style: TextStyle(
fontSize: 15.w,
fontWeight: Fonts.boldSemi,
color: Colours.cAB1941,
),
),
),
),
],
),
],
);
},
);
return agree;
}
/// 登录 /// 登录
void onLogin(BuildContext context) async { void onLogin(BuildContext context) async {
Tools.unfocus(); Tools.unfocus();
if (!agree) { if (!agree) {
Toast.show('请先阅读并同意《用户协议》和《隐私政策》'); await _showAgreementDialog(context);
return; if (!agree) return;
} }
String type = '1'; String type = '1';
...@@ -163,8 +265,8 @@ class LoginController extends GetxController { ...@@ -163,8 +265,8 @@ class LoginController extends GetxController {
/// 发送验证码 /// 发送验证码
void sendCode() async { void sendCode() async {
if (!agree) { if (!agree) {
Toast.show('请先阅读并同意《用户协议》和《隐私政策》'); await _showAgreementDialog(Get.context!);
return; if (!agree) return;
} }
final result = final result =
await AccountAPI.sendCode(phone: phoneInput.text, type: 'login'); await AccountAPI.sendCode(phone: phoneInput.text, type: 'login');
......
...@@ -215,13 +215,13 @@ class _LoginPageState extends State<LoginPage> { ...@@ -215,13 +215,13 @@ class _LoginPageState extends State<LoginPage> {
GestureDetector( GestureDetector(
child: Text('《用户协议》',style: TextStyle(color: Colours.cBlue,fontSize:14.w,height: 1.4)), child: Text('《用户协议》',style: TextStyle(color: Colours.cBlue,fontSize:14.w,height: 1.4)),
onTap: (){ onTap: (){
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserAgreement','title':'用户协议'}); context.pushNamed(Routes.terms,queryParameters: {'url':kUserAgreement,'title':'用户协议'});
}, },
), ),
GestureDetector( GestureDetector(
child: Text('《隐私政策》',style: TextStyle(color: Colours.cBlue,fontSize:14.w,height: 1.4)), child: Text('《隐私政策》',style: TextStyle(color: Colours.cBlue,fontSize:14.w,height: 1.4)),
onTap: (){ onTap: (){
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserPriAgreement','title':'隐私政策'}); context.pushNamed(Routes.terms,queryParameters: {'url':kUserPriAgreement,'title':'隐私政策'});
}, },
), ),
], ],
......
差异被折叠。
...@@ -192,8 +192,11 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -192,8 +192,11 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
onTap: () async{ onTap: () async{
final assets = await AssetsPicker.image( final assets = await AssetsPicker.image(
context: context, context: context,
purpose: '用于从相册中选择图片添加到讨论中,仅用于上传和显示图片内容。',
); );
widget.controller.addDiscussInputImages(assets!.path); if (assets != null) {
widget.controller.addDiscussInputImages(assets.path);
}
}, },
child: SizedBox( child: SizedBox(
// color: Colors.red, // color: Colors.red,
......
library splash_page; library splash_page;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart'; import 'package:flutter_book/apis/index.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../routes/index.dart'; import '../../routes/index.dart';
import '../../services/index.dart';
import '../../utils/index.dart'; import '../../utils/index.dart';
part 'view.dart'; part 'view.dart';
\ No newline at end of file
...@@ -8,65 +8,172 @@ class SplashPage extends StatefulWidget { ...@@ -8,65 +8,172 @@ class SplashPage extends StatefulWidget {
} }
class _SplashPageState extends State<SplashPage> { class _SplashPageState extends State<SplashPage> {
bool _navigated = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
Future.wait([ WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(const Duration(seconds: 2)) _startLaunchFlow();
]).whenComplete(() async { });
final netStatus = await Tools.checkCurrentNetStatus(); }
if (netStatus){
final ads = await CommonAPI.list(type: '1'); Future<void> _startLaunchFlow() async {
if (ads.isNotEmpty) { await _showPrivacyNoticeIfNeeded();
context.pushReplacementNamed(Routes.ad,extra: ads); if (!mounted || _navigated) return;
}
else { await Future.delayed(const Duration(seconds: 2));
context.pushReplacementNamed(Routes.main); if (!mounted || _navigated) return;
}
} final netStatus = await Tools.checkCurrentNetStatus();
else { if (!mounted || _navigated) return;
context.pushReplacementNamed(Routes.main);
if (netStatus) {
final ads = await CommonAPI.list(type: '1');
if (!mounted || _navigated) return;
if (ads.isNotEmpty) {
_navigated = true;
context.pushReplacementNamed(Routes.ad, extra: ads);
return;
} }
}
}); _navigated = true;
context.pushReplacementNamed(Routes.main);
}
Future<void> _showPrivacyNoticeIfNeeded() async {
if (StorageService.to.getBool(kLocalPrivacyNoticeShown)) {
return;
}
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return AlertDialog(
scrollable: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
title: const Text(
'隐私声明',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colours.c3,
),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
style: const TextStyle(
fontSize: 14,
height: 1.6,
color: Colours.c6,
),
children: [
const TextSpan(
text: '欢迎使用清控紫荆数智学堂。为保障你的个人信息安全,我们会按照',
),
TextSpan(
text: '《用户协议》',
style: const TextStyle(color: Color(0xFF2A82D9)),
recognizer: TapGestureRecognizer()
..onTap = () {
context.pushNamed(
Routes.terms,
queryParameters: {
'url': kUserAgreement,
'title': '用户协议',
},
);
},
),
const TextSpan(text: '和'),
TextSpan(
text: '《隐私政策》',
style: const TextStyle(color: Color(0xFF2A82D9)),
recognizer: TapGestureRecognizer()
..onTap = () {
context.pushNamed(
Routes.terms,
queryParameters: {
'url': kUserPriAgreement,
'title': '隐私政策',
},
);
},
),
const TextSpan(
text: '处理账号信息、设备信息、学习记录等内容。',
),
],
),
),
],
),
),
actions: [
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () async {
await StorageService.to
.setBool(kLocalPrivacyNoticeShown, true);
if (dialogContext.mounted) {
Navigator.of(dialogContext).pop();
}
},
child: const Text('我知道了'),
),
),
],
);
},
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
// appBar: AppBar(), // appBar: AppBar(),
body: SizedBox( body: SizedBox(
height: double.infinity, height: double.infinity,
width: double.infinity, width: double.infinity,
child: Image.asset('assets/images/splash.png',fit: BoxFit.cover,), child: Image.asset(
) 'assets/images/splash.png',
// const Column( fit: BoxFit.cover,
// crossAxisAlignment: CrossAxisAlignment.stretch, ),
// mainAxisAlignment: MainAxisAlignment.center, )
// children: [ // const Column(
// Expanded( // crossAxisAlignment: CrossAxisAlignment.stretch,
// flex: 5, // mainAxisAlignment: MainAxisAlignment.center,
// child: Center( // children: [
// child: CustomEmpty( // Expanded(
// icon: CustomImage.asset( // flex: 5,
// url: 'assets/images/logo.png', // child: Center(
// fit: BoxFit.contain, // child: CustomEmpty(
// ), // icon: CustomImage.asset(
// title: Text('紫荆数智学堂'), // url: 'assets/images/logo.png',
// ), // fit: BoxFit.contain,
// ) // ),
// ), // title: Text('清控紫荆数智学堂'),
// // Expanded( // ),
// // flex: 2, // )
// // child: Center(child: CupertinoActivityIndicator(),), // ),
// // ) // // Expanded(
// ], // // flex: 2,
// ), // // child: Center(child: CupertinoActivityIndicator(),),
); // // )
// ],
// ),
);
} }
} }
...@@ -44,9 +44,16 @@ class _AboutPageState extends State<AboutPage> { ...@@ -44,9 +44,16 @@ class _AboutPageState extends State<AboutPage> {
child: const CustomImage.asset(url: 'assets/images/icon.png'), child: const CustomImage.asset(url: 'assets/images/icon.png'),
), ),
Gaps.vGaps15, Gaps.vGaps15,
Text('紫荆数智学堂',style: TextStyle(fontSize: 17.w,fontWeight: Fonts.medium,color: Colours.c3),), Text(
'清控紫荆数智学堂',
style: TextStyle(
fontSize: 17.w,
fontWeight: Fonts.medium,
color: Colours.c3),
),
Gaps.vGaps5, Gaps.vGaps5,
Text('V${packageInfo.version??''}',style: TextStyle(fontSize: 13.w,color: Colours.c9)), Text('V${packageInfo.version}',
style: TextStyle(fontSize: 13.w, color: Colours.c9)),
], ],
), ),
SafeArea( SafeArea(
...@@ -56,29 +63,50 @@ class _AboutPageState extends State<AboutPage> { ...@@ -56,29 +63,50 @@ class _AboutPageState extends State<AboutPage> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
GestureDetector( GestureDetector(
child: Text('《用户协议》',textAlign: TextAlign.right,style: TextStyle(fontSize: 10.w,color: const Color(0xFF2A82D9)),), child: Text(
onTap: (){ '《用户协议》',
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserAgreement','title':'用户协议'}); textAlign: TextAlign.right,
style: TextStyle(
fontSize: 10.w, color: const Color(0xFF2A82D9)),
),
onTap: () {
context.pushNamed(Routes.terms, queryParameters: {
'url': kUserAgreement,
'title': '用户协议'
});
}, },
), ),
Gaps.vGaps15, Gaps.vGaps15,
Container(width: 1.w,height: 10.w,color: const Color(0xFFC8C8C8),), Container(
width: 1.w,
height: 10.w,
color: const Color(0xFFC8C8C8),
),
Gaps.vGaps15, Gaps.vGaps15,
GestureDetector( GestureDetector(
child: Text('《隐私协议》',textAlign: TextAlign.left,style: TextStyle(fontSize: 10.w,color: const Color(0xFF2A82D9))), child: Text('《隐私协议》',
onTap:(){ textAlign: TextAlign.left,
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserPriAgreement','title':'隐私政策'}); style: TextStyle(
} , fontSize: 10.w, color: const Color(0xFF2A82D9))),
onTap: () {
context.pushNamed(Routes.terms, queryParameters: {
'url': kUserPriAgreement,
'title': '隐私政策'
});
},
) )
], ],
), ),
Gaps.hGaps10, Gaps.hGaps10,
Text('Copyright © 2024 Zijing Education. All rights reserved.\n清控紫荆(北京)教育科技股份有限公司\n京ICP证150431号',style: TextStyle(color: Colours.c9,fontSize:9.w),textAlign: TextAlign.center,), Text(
'Copyright © 2024 Zijing Education. All rights reserved.\n清控紫荆(北京)教育科技股份有限公司\n京ICP证150431号',
style: TextStyle(color: Colours.c9, fontSize: 9.w),
textAlign: TextAlign.center,
),
Gaps.vGaps25, Gaps.vGaps25,
], ],
), ),
) )
], ],
), ),
); );
...@@ -86,8 +114,8 @@ class _AboutPageState extends State<AboutPage> { ...@@ -86,8 +114,8 @@ class _AboutPageState extends State<AboutPage> {
void _getVersion() async { void _getVersion() async {
final info = await PackageInfo.fromPlatform(); final info = await PackageInfo.fromPlatform();
setState(() { setState(() {
packageInfo = info; packageInfo = info;
}); });
} }
} }
part of recharge; part of recharge;
class CoinRechargePage extends StatefulWidget { class CoinRechargePage extends StatefulWidget {
const CoinRechargePage({Key? key}) : super(key: key); const CoinRechargePage({Key? key}) : super(key: key);
...@@ -9,12 +7,13 @@ class CoinRechargePage extends StatefulWidget { ...@@ -9,12 +7,13 @@ class CoinRechargePage extends StatefulWidget {
State<CoinRechargePage> createState() => _CoinRechargePageState(); State<CoinRechargePage> createState() => _CoinRechargePageState();
} }
class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepAliveClientMixin { class _CoinRechargePageState extends State<CoinRechargePage>
with AutomaticKeepAliveClientMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<CoinRechargeController>( return GetBuilder<CoinRechargeController>(
init: CoinRechargeController(context: context), init: CoinRechargeController(context: context),
builder:(controller) => Column( builder: (controller) => Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Container( Container(
...@@ -27,14 +26,14 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA ...@@ -27,14 +26,14 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
const Row( const Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text('紫荆币充值',textAlign:TextAlign.center), Text('紫荆币充值', textAlign: TextAlign.center),
], ],
), ),
Positioned( Positioned(
right: 0.w, right: 0.w,
top: 0.w, top: 0.w,
child: GestureDetector( child: GestureDetector(
onTap: (){ onTap: () {
context.pop(); context.pop();
}, },
child: SizedBox( child: SizedBox(
...@@ -48,16 +47,15 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA ...@@ -48,16 +47,15 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
), ),
), ),
Container( Container(
margin: EdgeInsets.symmetric(horizontal: 20.w), margin: EdgeInsets.symmetric(horizontal: 20.w),
child: _buildAudioGridView(controller) child: _buildAudioGridView(controller)),
),
Gaps.vGaps15, Gaps.vGaps15,
_buildListView(controller), _buildListView(controller),
Gaps.vGaps15, Gaps.vGaps15,
Container( Container(
margin: EdgeInsets.symmetric(horizontal:15.w), margin: EdgeInsets.symmetric(horizontal: 15.w),
child: CustomGradientButton( child: CustomGradientButton(
text: '立即充值${controller.rechargeModel.priceName??''}', text: '立即充值${controller.rechargeModel.priceName ?? ''}',
isEnabled: true, isEnabled: true,
onPressed: () { onPressed: () {
controller.createRechargeOrder(); controller.createRechargeOrder();
...@@ -66,15 +64,25 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA ...@@ -66,15 +64,25 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
), ),
Gaps.vGaps15, Gaps.vGaps15,
GestureDetector( GestureDetector(
onTap: (){ onTap: () {
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserRechargeAgreement','title':'用户充值协议'}); context.pushNamed(Routes.terms, queryParameters: {
'url': kUserRechargeAgreement,
'title': '用户充值协议'
});
}, },
child: RichText(text: TextSpan( child: RichText(
children: [ text: TextSpan(children: [
TextSpan(text: '充值即代表同意',style: TextStyle(fontSize: 13.w,height: 1.5,color: Colours.c9)), TextSpan(
TextSpan(text: '《用户充值协议》',style: TextStyle(fontSize: 13.w,height: 1.5,color: const Color(0xFF2A82D9))), text: '充值即代表同意',
] style: TextStyle(
)), fontSize: 13.w, height: 1.5, color: Colours.c9)),
TextSpan(
text: '《用户充值协议》',
style: TextStyle(
fontSize: 13.w,
height: 1.5,
color: const Color(0xFF2A82D9))),
])),
), ),
Gaps.vGaps15 Gaps.vGaps15
], ],
...@@ -82,7 +90,7 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA ...@@ -82,7 +90,7 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
); );
} }
Widget _buildAudioGridView(CoinRechargeController controller){ Widget _buildAudioGridView(CoinRechargeController controller) {
return GridView.builder( return GridView.builder(
// padding: const EdgeInsets.only(left: 13,top: 10), // padding: const EdgeInsets.only(left: 13,top: 10),
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
...@@ -91,26 +99,41 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA ...@@ -91,26 +99,41 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
crossAxisCount: 3, crossAxisCount: 3,
crossAxisSpacing: 10.w, crossAxisSpacing: 10.w,
mainAxisSpacing: 10.w, mainAxisSpacing: 10.w,
childAspectRatio: 2 childAspectRatio: 2),
),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
CoinModel model = controller.data[index]; CoinModel model = controller.data[index];
return GestureDetector( return GestureDetector(
onTap: (){ onTap: () {
controller.choose(model); controller.choose(model);
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: model.selected?AppTheme.primary.withOpacity(0.1):Colors.white, color: model.selected
borderRadius: BorderRadius.circular(8.w), ? AppTheme.primary.withOpacity(0.1)
border: Border.all(width: 0.5.w,color: model.selected?AppTheme.primary:const Color(0xFFDADADA)) : Colors.white,
), borderRadius: BorderRadius.circular(8.w),
border: Border.all(
width: 0.5.w,
color: model.selected
? AppTheme.primary
: const Color(0xFFDADADA))),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text(model.beanName??'',style: TextStyle(color: model.selected?AppTheme.primary:Colours.c3,fontSize: 14.w,height: 1.5),), Text(
Text(model.priceName??'',style: TextStyle(color: model.selected?AppTheme.primary:Colours.c9,fontSize: 11.w,height: 1.5),), model.beanName ?? '',
style: TextStyle(
color: model.selected ? AppTheme.primary : Colours.c3,
fontSize: 14.w,
height: 1.5),
),
Text(
model.priceName ?? '',
style: TextStyle(
color: model.selected ? AppTheme.primary : Colours.c9,
fontSize: 11.w,
height: 1.5),
),
], ],
), ),
), ),
...@@ -119,18 +142,21 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA ...@@ -119,18 +142,21 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
itemCount: controller.data.length, itemCount: controller.data.length,
); );
} }
Widget _buildListView(CoinRechargeController controller){
Widget _buildListView(CoinRechargeController controller) {
return ListView.builder( return ListView.builder(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 20.w), padding: EdgeInsets.symmetric(horizontal: 20.w),
shrinkWrap:true, shrinkWrap: true,
itemBuilder: (BuildContext context, int index){ itemBuilder: (BuildContext context, int index) {
PayModel model = controller.pays[index]; PayModel model = controller.pays[index];
return GestureDetector( return GestureDetector(
onTap: (){ onTap: () {
controller.setPayModel(model); controller.setPayModel(model);
}, },
child: BuildPayWay(model:model,)); child: BuildPayWay(
model: model,
));
}, },
itemCount: controller.pays.length, itemCount: controller.pays.length,
); );
......
...@@ -167,8 +167,11 @@ class _UserEditNotePageState extends State<UserEditNotePage> { ...@@ -167,8 +167,11 @@ class _UserEditNotePageState extends State<UserEditNotePage> {
onTap: () async{ onTap: () async{
final assets = await AssetsPicker.image( final assets = await AssetsPicker.image(
context: context, context: context,
purpose: '用于从相册中选择图片添加到笔记中,仅用于上传和显示图片内容。',
); );
controller.addImage(assets!.path); if (assets != null) {
controller.addImage(assets.path);
}
}, },
child: SizedBox( child: SizedBox(
// color: Colors.red, // color: Colors.red,
......
...@@ -168,9 +168,12 @@ class _UserInfoPageState extends State<UserInfoPage> { ...@@ -168,9 +168,12 @@ class _UserInfoPageState extends State<UserInfoPage> {
Console.log("点击拍照"); Console.log("点击拍照");
final assets = await AssetsPicker.camera( final assets = await AssetsPicker.camera(
context: context, context: context,
purpose: '用于使用相机拍照设置用户头像,仅用于上传和显示您的头像图片。',
); );
controller.upload(path: assets!.path); if (assets != null) {
controller.show(); controller.upload(path: assets.path);
controller.show();
}
}, },
child: Container( child: Container(
height: 45.w, height: 45.w,
...@@ -202,9 +205,12 @@ class _UserInfoPageState extends State<UserInfoPage> { ...@@ -202,9 +205,12 @@ class _UserInfoPageState extends State<UserInfoPage> {
Console.log("点击选择现有图片"); Console.log("点击选择现有图片");
final assets = await AssetsPicker.image( final assets = await AssetsPicker.image(
context: context, context: context,
purpose: '用于从相册中选择图片设置用户头像,仅用于上传和显示您的头像图片。',
); );
controller.upload(path: assets!.path); if (assets != null) {
controller.show(); controller.upload(path: assets.path);
controller.show();
}
}, },
child: Container( child: Container(
height: 45.w, height: 45.w,
......
...@@ -48,6 +48,7 @@ import 'package:flutter_book/pages/user_set/index.dart'; ...@@ -48,6 +48,7 @@ import 'package:flutter_book/pages/user_set/index.dart';
import 'package:flutter_book/pages/user_wrong/index.dart'; import 'package:flutter_book/pages/user_wrong/index.dart';
import 'package:flutter_book/pages/user_wrong_des/index.dart'; import 'package:flutter_book/pages/user_wrong_des/index.dart';
import 'package:flutter_book/pages/version_des/index.dart'; import 'package:flutter_book/pages/version_des/index.dart';
import 'package:get/get.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../models/index.dart'; import '../models/index.dart';
...@@ -67,7 +68,6 @@ import '../pages/user_terms/index.dart'; ...@@ -67,7 +68,6 @@ import '../pages/user_terms/index.dart';
import '../pages/version/index.dart'; import '../pages/version/index.dart';
import '../store/index.dart'; import '../store/index.dart';
part 'observers.dart'; part 'observers.dart';
part 'routes.dart'; part 'routes.dart';
part 'transitions.dart'; part 'transitions.dart';
\ No newline at end of file
差异被折叠。
差异被折叠。
part of utils; part of utils;
abstract class Access { abstract class Access {
/// 图片权限 /// 图片权限
static Future<bool> photos() async { /// [showExplanation] 是否在申请权限前显示说明对话框
/// [purpose] 权限使用目的说明
/// [context] 上下文,用于显示对话框
static Future<bool> photos({
bool showExplanation = false,
String purpose = '',
BuildContext? context,
}) async {
// 如果需要在申请权限前显示说明,且提供了上下文
if (showExplanation && context != null && purpose.isNotEmpty) {
final bool? shouldRequest = await _showPermissionExplanationDialog(
context: context,
permissionName: Platform.isIOS ? '相册权限' : '存储权限',
purpose: purpose,
);
// 用户取消,不申请权限
if (shouldRequest != true) {
return false;
}
}
if (Platform.isIOS) { if (Platform.isIOS) {
final result = await [Permission.photos].request(); final result = await [Permission.photos].request();
return result[Permission.photos] == PermissionStatus.granted || return result[Permission.photos] == PermissionStatus.granted ||
...@@ -17,11 +37,146 @@ abstract class Access { ...@@ -17,11 +37,146 @@ abstract class Access {
} }
/// 相机权限 /// 相机权限
static Future<bool> camera() async { /// [showExplanation] 是否在申请权限前显示说明对话框
/// [purpose] 权限使用目的说明
/// [context] 上下文,用于显示对话框
static Future<bool> camera({
bool showExplanation = false,
String purpose = '',
BuildContext? context,
}) async {
// 如果需要在申请权限前显示说明,且提供了上下文
if (showExplanation && context != null && purpose.isNotEmpty) {
final bool? shouldRequest = await _showPermissionExplanationDialog(
context: context,
permissionName: '相机权限',
purpose: purpose,
);
// 用户取消,不申请权限
if (shouldRequest != true) {
return false;
}
}
final result = await [Permission.camera].request(); final result = await [Permission.camera].request();
return result[Permission.camera] == PermissionStatus.granted; return result[Permission.camera] == PermissionStatus.granted;
} }
/// 显示权限使用目的说明对话框
/// 对话框不会自动消失,需要用户主动确认
static Future<bool?> _showPermissionExplanationDialog({
required BuildContext context,
required String permissionName,
required String purpose,
}) async {
return await showDialog<bool>(
context: context,
barrierDismissible: false, // 不允许点击外部关闭
builder: (BuildContext dialogContext) {
return WillPopScope(
onWillPop: () async => false, // 不允许返回键关闭
child: AlertDialog(
insetPadding: EdgeInsets.symmetric(horizontal: 40.w),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.w),
),
title: Text(
'权限申请说明',
style: TextStyle(
fontSize: 18.w,
color: Colours.c3,
fontWeight: Fonts.boldSemi,
),
textAlign: TextAlign.center,
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'权限名称:',
style: TextStyle(
fontSize: 15.w,
color: Colours.c3,
fontWeight: Fonts.medium,
),
),
SizedBox(height: 5.w),
Text(
permissionName,
style: TextStyle(
fontSize: 15.w,
color: Colours.c6,
),
),
SizedBox(height: 15.w),
Text(
'使用目的:',
style: TextStyle(
fontSize: 15.w,
color: Colours.c3,
fontWeight: Fonts.medium,
),
),
SizedBox(height: 5.w),
Text(
purpose,
style: TextStyle(
fontSize: 15.w,
color: Colours.c6,
height: 1.5,
),
),
],
),
actions: <Widget>[
Row(
children: [
Expanded(
child: TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(false);
},
child: Text(
'取消',
style: TextStyle(
fontSize: 16.w,
color: Colours.c6,
fontWeight: Fonts.medium,
),
),
),
),
Container(
width: 1,
height: 40.w,
color: Colours.cLine,
),
Expanded(
child: TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(true);
},
child: Text(
'确定',
style: TextStyle(
fontSize: 16.w,
color: Colours.cAB1941,
fontWeight: Fonts.boldSemi,
),
),
),
),
],
),
],
),
);
},
);
}
/// 打开设置 /// 打开设置
static Future<void> setting() async => await openAppSettings(); static Future<void> setting() async => await openAppSettings();
...@@ -36,5 +191,4 @@ abstract class Access { ...@@ -36,5 +191,4 @@ abstract class Access {
final result = await [Permission.microphone].request(); final result = await [Permission.microphone].request();
return result[Permission.microphone] == PermissionStatus.granted; return result[Permission.microphone] == PermissionStatus.granted;
} }
} }
...@@ -4,22 +4,47 @@ abstract class AssetsPicker { ...@@ -4,22 +4,47 @@ abstract class AssetsPicker {
static final ImagePicker _imagePicker = ImagePicker(); static final ImagePicker _imagePicker = ImagePicker();
/// 获取图库 /// 获取图库
/// [purpose] 权限使用目的说明,用于在申请权限前告知用户
static Future<XFile?> image({ static Future<XFile?> image({
required BuildContext context, required BuildContext context,
ImageSource source = ImageSource.gallery, ImageSource source = ImageSource.gallery,
double maxWidth = 1024, double maxWidth = 1024,
double maxHeight = 1024, double maxHeight = 1024,
String purpose = '用于从相册中选择图片,用于设置头像、上传图片等功能。',
}) async { }) async {
if (!(await Access.photos())) { // 先检查权限状态
if (context.mounted) { bool hasPermission = false;
// CustomDialog.showAccess( if (Platform.isIOS) {
// context: context, final status = await Permission.photos.status;
// content: const Text('获取相册权限'), hasPermission = status == PermissionStatus.granted || status == PermissionStatus.limited;
// ); } else if (Platform.isAndroid) {
_showSettingDialog(context,'获取相册权限'); final status = await Permission.storage.status;
hasPermission = status == PermissionStatus.granted;
}
// 如果没有权限,先显示说明对话框,然后申请权限
if (!hasPermission) {
final granted = await Access.photos(
showExplanation: true,
purpose: purpose,
context: context,
);
if (!granted) {
// 用户拒绝或取消,检查是否需要跳转设置
if (context.mounted) {
final status = Platform.isIOS
? await Permission.photos.status
: await Permission.storage.status;
if (status.isPermanentlyDenied) {
_showSettingDialog(context, '获取相册权限');
}
}
return null;
} }
return null;
} }
return _imagePicker.pickImage( return _imagePicker.pickImage(
source: source, source: source,
// maxWidth: maxWidth, // maxWidth: maxWidth,
...@@ -27,23 +52,41 @@ abstract class AssetsPicker { ...@@ -27,23 +52,41 @@ abstract class AssetsPicker {
// imageQuality: 100 // imageQuality: 100
); );
} }
/// 拍照 /// 拍照
/// [purpose] 权限使用目的说明,用于在申请权限前告知用户
static Future<XFile?> camera({ static Future<XFile?> camera({
required BuildContext context, required BuildContext context,
ImageSource source = ImageSource.camera, ImageSource source = ImageSource.camera,
double maxWidth = 512, double maxWidth = 512,
double maxHeight = 512, double maxHeight = 512,
String purpose = '用于使用相机拍照,用于设置头像、上传图片等功能。',
}) async { }) async {
if (!(await Access.photos())) { // 先检查权限状态
if (context.mounted) { final cameraStatus = await Permission.camera.status;
// CustomDialog.showAccess( bool hasPermission = cameraStatus == PermissionStatus.granted;
// context: context,
// content: const Text('获取拍照权限'), // 如果没有相机权限,先显示说明对话框,然后申请权限
// ); if (!hasPermission) {
_showSettingDialog(context,'获取拍照权限'); final granted = await Access.camera(
showExplanation: true,
purpose: purpose,
context: context,
);
if (!granted) {
// 用户拒绝或取消,检查是否需要跳转设置
if (context.mounted) {
final status = await Permission.camera.status;
if (status.isPermanentlyDenied) {
_showSettingDialog(context, '获取相机权限');
}
}
return null;
} }
return null;
} }
return _imagePicker.pickImage( return _imagePicker.pickImage(
source: source, source: source,
// maxWidth: maxWidth, // maxWidth: maxWidth,
......
part of utils; part of utils;
// 服务器地址 // 服务器地址
// const String kServerUrl = 'http://192.168.11.88:81'; const String kServerUrl = 'https://project-api.ezijing.com/api/book';
// const String kServerUrl = 'http://8.141.148.247:7421'; // const String kServerUrl = 'http://172.16.51.175:7421';
const String kServerUrl = 'https://ebook-app.ezijing.com'; const String kHtmlBaseServer = 'https://webapp-pub.ezijing.com/book-app';
const String kHtmlBaseServer = 'http://150.158.138.40:9200';
const String kLocalToken = 'local_token'; const String kLocalToken = 'local_token';
const String kLocalAccessToken = 'local_access_token'; const String kLocalAccessToken = 'local_access_token';
const String kLocalAccount = 'local_account'; const String kLocalAccount = 'local_account';
const String kLocalPassword = 'local_password'; const String kLocalPassword = 'local_password';
const String kLocalUserInfo = 'local_user_info'; const String kLocalUserInfo = 'local_user_info';
const String kLocalPrivacyNoticeShown = 'local_privacy_notice_shown';
const String kSearchHistory = 'search_history'; const String kSearchHistory = 'search_history';
const String kFailOrder = 'failOrder'; const String kFailOrder = 'failOrder';
const String kNoteTable = 'members_book_notes'; const String kNoteTable = 'members_book_notes';
const String kReadTable = 'read_history'; const String kReadTable = 'read_history';
const String kUserAgreement = '/html/agreement/ser_agreement.html'; const String kUserAgreement = '$kHtmlBaseServer/agreement/ser_agreement.html';
const String kUserPriAgreement = '/html/agreement/pri_agreement.html'; const String kUserPriAgreement =
const String kUserRechargeAgreement = '/html/agreement/rec_agreement.html'; '$kHtmlBaseServer/agreement/pri_agreement.html';
const String kUserRechargeAgreement =
'$kHtmlBaseServer/agreement/rec_agreement.html';
// 错题详情页 html // 错题详情页 html
const String kUserWrongDes = '$kHtmlBaseServer/evaluating_wrong.html'; const String kUserWrongDes = '$kHtmlBaseServer/evaluating_wrong.html';
// 阅读页 html // 阅读页 html
String kReadTestUnderLineBook = '$kHtmlBaseServer/read_unline.html?t=${DateTime.now().millisecondsSinceEpoch}'; String kReadTestUnderLineBook =
'$kHtmlBaseServer/read_unline.html?t=${DateTime.now().millisecondsSinceEpoch}';
const String kReadBook = '$kHtmlBaseServer/read.html'; const String kReadBook = '$kHtmlBaseServer/read.html';
// 答题页 // 答题页
const String kAnswer = '$kHtmlBaseServer/evaluating.html'; const String kAnswer = '$kHtmlBaseServer/evaluating.html';
...@@ -33,18 +36,17 @@ const String kReadInfo = '$kHtmlBaseServer/read_info.html'; ...@@ -33,18 +36,17 @@ const String kReadInfo = '$kHtmlBaseServer/read_info.html';
// 阅读页 图片预览 // 阅读页 图片预览
const String kScaleImage = '$kHtmlBaseServer/read_img.html'; const String kScaleImage = '$kHtmlBaseServer/read_img.html';
abstract class C { abstract class C {
static const String localAccount = 'storage_account'; static const String localAccount = 'storage_account';
static const String localThemeMode = 'storage_theme_mode'; static const String localThemeMode = 'storage_theme_mode';
static const String localLanguage = 'storage_lang'; static const String localLanguage = 'storage_lang';
/// 自动主题 /// 自动主题
static const String themeSystem = '0'; static const String themeSystem = '0';
/// 亮色主题 /// 亮色主题
static const String themeLight = '1'; static const String themeLight = '1';
/// 暗色主题 /// 暗色主题
static const String themeDark = '2'; static const String themeDark = '2';
}
}
\ No newline at end of file
...@@ -49,14 +49,6 @@ packages: ...@@ -49,14 +49,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.1.18" version: "0.1.18"
azlistview:
dependency: "direct main"
description:
name: azlistview
sha256: "93e865f11777a271b439f0d6b00799c0797e9daeec2e082a2e01373809c4b90d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
badges: badges:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -262,14 +254,6 @@ packages: ...@@ -262,14 +254,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.0.4" version: "3.0.4"
flutter_bugly:
dependency: "direct main"
description:
name: flutter_bugly
sha256: a2d13cb35bedb907cb020ea4862264dcc9044675a49a8313bb3ab2e61af79c77
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.4"
flutter_cache_manager: flutter_cache_manager:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -549,14 +533,6 @@ packages: ...@@ -549,14 +533,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.18.0" version: "0.18.0"
ionicons:
dependency: "direct main"
description:
name: ionicons
sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.2"
js: js:
dependency: transitive dependency: transitive
description: description:
...@@ -837,14 +813,6 @@ packages: ...@@ -837,14 +813,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.1.2" version: "6.1.2"
pull_to_refresh_flutter3:
dependency: "direct main"
description:
name: pull_to_refresh_flutter3
sha256: "223a6241067162dc15cf8c46c05af998ce7aa85e0703d8f696101eb1b5629d76"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
rational: rational:
dependency: transitive dependency: transitive
description: description:
...@@ -869,22 +837,6 @@ packages: ...@@ -869,22 +837,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.26.0" version: "0.26.0"
screen_protector:
dependency: "direct main"
description:
name: screen_protector
sha256: "541bdcd341de1e38026b5b94cc2a74cd95299d2c51150735165c4b445fa0209a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.2"
scrollable_positioned_list:
dependency: transitive
description:
name: scrollable_positioned_list
sha256: "9566352ab9ba05794ee6c8864f154afba5d36c5637d0e3e32c615ba4ceb92772"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.3"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
......
name: flutter_book name: flutter_book
description: 紫荆数智学堂 description: 清控紫荆数智学堂
# The following line prevents the package from being accidentally published to # The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages. # pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
...@@ -39,7 +39,6 @@ dependencies: ...@@ -39,7 +39,6 @@ dependencies:
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
...@@ -53,11 +52,8 @@ dependencies: ...@@ -53,11 +52,8 @@ dependencies:
shared_preferences: 2.1.1 shared_preferences: 2.1.1
# 屏幕适配 # 屏幕适配
flutter_screenutil: 5.8.2 flutter_screenutil: 5.8.2
pull_to_refresh_flutter3: 2.0.1
# 加载图片 # 加载图片
extended_image: 8.0.2 extended_image: 8.0.2
# 图标库
ionicons: 0.2.2
# 网络 # 网络
dio: 5.3.3 dio: 5.3.3
crypto: 3.0.3 crypto: 3.0.3
...@@ -97,8 +93,6 @@ dependencies: ...@@ -97,8 +93,6 @@ dependencies:
package_info_plus: ^4.2.0 package_info_plus: ^4.2.0
# 版本更新 # 版本更新
flutter_app_update: ^3.0.4 flutter_app_update: ^3.0.4
# 能指定滑动位置的listView
azlistview: ^2.0.0
# 支付宝 # 支付宝
tobias: ^3.3.0 tobias: ^3.3.0
# 内购 # 内购
...@@ -120,12 +114,8 @@ dependencies: ...@@ -120,12 +114,8 @@ dependencies:
# 安卓 # 安卓
android_id: ^0.3.6 android_id: ^0.3.6
# 防止截屏 # 防止截屏
# secure_application: ^3.8.0 # secure_application: ^3.8.0
screen_protector: ^1.4.2 # sentry_flutter: ^7.19.0
# sentry_flutter: ^7.19.0
flutter_bugly: ^0.4.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
...@@ -143,7 +133,6 @@ dev_dependencies: ...@@ -143,7 +133,6 @@ dev_dependencies:
# The following section is specific to Flutter packages. # The following section is specific to Flutter packages.
flutter: flutter:
# The following line ensures that the Material Icons font is # The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in # included with your application, so that you can use the icons in
# the material Icons class. # the material Icons class.
...@@ -151,14 +140,14 @@ flutter: ...@@ -151,14 +140,14 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/images/ - assets/images/
- assets/html/ - assets/html/
- assets/html/assets/css/ - assets/html/assets/css/
- assets/html/assets/fonts/ - assets/html/assets/fonts/
- assets/html/assets/images/ - assets/html/assets/images/
- assets/html/assets/js/ - assets/html/assets/js/
- assets/html/src/assets/images/ - assets/html/src/assets/images/
- assets/html/src/assets/images/editor/ - assets/html/src/assets/images/editor/
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware # https://flutter.dev/assets-and-images/#resolution-aware
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论