提交 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 {
versionName flutterVersionName
}
signingConfigs {
// release {
// keyAlias keystoreProperties['keyAlias']
// keyPassword keystoreProperties['keyPassword']
// storeFile file(keystoreProperties['storeFile'])
// storePassword keystoreProperties['storePassword']
// }
release{
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
debug{
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
// debug 使用默认签名
}
buildTypes {
debug {
signingConfig signingConfigs.release
// 使用默认 debug 签名
}
release {
// TODO: Add your own signing config for the release build.
......
......@@ -14,7 +14,7 @@
</queries>
<application
android:label="紫荆数智学堂"
android:label="清控紫荆数智学堂"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
......
storePassword=123456
keyPassword=123456
storePassword=zijing123456
keyPassword=zijing123456
keyAlias=zijing
storeFile=/Users/apple/zijiing_key.jks
#storeFile=zijiing_key.jks
\ No newline at end of file
storeFile=../zijing_release.jks
PODS:
- audio_session (0.0.1):
- Flutter
- Bugly (2.6.1)
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
......@@ -10,9 +9,6 @@ PODS:
- Flutter (1.0.0)
- flutter_app_update (0.0.1):
- Flutter
- flutter_bugly (0.0.1):
- Bugly
- Flutter
- flutter_inapp_purchase (0.0.1):
- Flutter
- flutter_inappwebview_ios (0.0.1):
......@@ -47,10 +43,6 @@ PODS:
- permission_handler_apple (9.1.1):
- Flutter
- ReachabilitySwift (5.0.0)
- screen_protector (1.2.1):
- Flutter
- ScreenProtectorKit (~> 1.3.1)
- ScreenProtectorKit (1.3.1)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
......@@ -70,7 +62,6 @@ DEPENDENCIES:
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- 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_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_sound (from `.symlinks/plugins/flutter_sound/ios`)
......@@ -81,18 +72,15 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- 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`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- tobias (from `.symlinks/plugins/tobias/ios`)
SPEC REPOS:
trunk:
- Bugly
- flutter_sound_core
- OrderedSet
- ReachabilitySwift
- ScreenProtectorKit
- WechatOpenSDK-XCFramework
EXTERNAL SOURCES:
......@@ -106,8 +94,6 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_app_update:
:path: ".symlinks/plugins/flutter_app_update/ios"
flutter_bugly:
:path: ".symlinks/plugins/flutter_bugly/ios"
flutter_inapp_purchase:
:path: ".symlinks/plugins/flutter_inapp_purchase/ios"
flutter_inappwebview_ios:
......@@ -128,8 +114,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
screen_protector:
:path: ".symlinks/plugins/screen_protector/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite:
......@@ -139,12 +123,10 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
audio_session: 9bdd3bf46960d4322cb8c3cb6138295dcfe84eee
Bugly: 217ac2ce5f0f2626d43dbaa4f70764c953a26a31
connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
flutter_bugly: 12197049262a692eab0bd2834d465a5647d920c2
flutter_inapp_purchase: 722da12971a50f306f37e62fc1aaf576b1cbecf6
flutter_inappwebview_ios: 25b61a1b550d1068e4ddaf490fc1d03c2ce6828d
flutter_sound: 49be32081884d275fe91d48262f4b1fcd86e10d3
......@@ -158,8 +140,6 @@ SPEC CHECKSUMS:
path_provider_foundation: 608fcb11be570ce83519b076ab6a1fffe2474f05
permission_handler_apple: 3787117e48f80715ff04a3830ca039283d6a4f29
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
screen_protector: 3d90d44ac886b25335aebd93230b454aef45794a
ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4
shared_preferences_foundation: 0b09b969fb36da5551c0bc4a2dbd9d0ff9387478
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
tobias: 50b529c2501d277c83fef9976803a001eb58a057
......
......@@ -489,7 +489,7 @@
DEVELOPMENT_TEAM = MYN43C5WGE;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "紫荆数智学堂";
INFOPLIST_KEY_CFBundleDisplayName = "清控紫荆数智学堂";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
......@@ -662,7 +662,7 @@
DEVELOPMENT_TEAM = MYN43C5WGE;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "紫荆数智学堂";
INFOPLIST_KEY_CFBundleDisplayName = "清控紫荆数智学堂";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
......@@ -688,7 +688,7 @@
DEVELOPMENT_TEAM = MYN43C5WGE;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "紫荆数智学堂";
INFOPLIST_KEY_CFBundleDisplayName = "清控紫荆数智学堂";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
......
......@@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>紫荆数智学堂</string>
<string>清控紫荆数智学堂</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
......@@ -47,15 +47,15 @@
<true/>
</dict>
<key>NSAppleMusicUsageDescription</key>
<string>紫荆数智学堂需要访问媒体</string>
<string>清控紫荆数智学堂需要访问媒体</string>
<key>NSCameraUsageDescription</key>
<string>紫荆数智学堂需要访问相机</string>
<string>清控紫荆数智学堂需要访问相机</string>
<key>NSMicrophoneUsageDescription</key>
<string>紫荆数智学堂需要访问麦克风</string>
<string>清控紫荆数智学堂需要访问麦克风</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>紫荆数智学堂需要访问照片</string>
<string>清控紫荆数智学堂需要访问照片</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>紫荆数智学堂需要语言识别</string>
<string>清控紫荆数智学堂需要语言识别</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
......
......@@ -18,13 +18,6 @@ class Global {
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// 初始化数据库
SqlManager.init();
// 检测网络变化
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
Console.log('网络变化--------------------------------$result');
if (result != ConnectivityResult.none && UserStore.to.isLogin) {
upload();
}
});
// 监测app生命周期
// WidgetsBinding.instance?.addObserver(AppLifecycleObserver());
......@@ -32,12 +25,20 @@ class Global {
// 配置存储
Get.putAsync<StorageService>(() => StorageService().init()),
]).whenComplete(() {
// 先注册依赖最广的用户状态,避免启动早期被其他模块提前读取。
Get.put<UserStore>(UserStore());
// 网络
Get.put<HttpService>(HttpService());
//配置基本设置
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';
import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_bugly/flutter_bugly.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:oktoast/oktoast.dart';
......@@ -19,17 +18,9 @@ void main() {
Future.wait([
UserStore.to.profile(),
]).whenComplete(() {
FlutterBugly.postCatchedException(() {
// 如果需要 ensureInitialized,请在这里运行。
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
FlutterBugly.init(
androidAppId: "8b4da96535",
iOSAppId: "290703a371",
);
});
// runApp(const MyApp());
//FlutterNativeSplash.remove();
});
});
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
......@@ -46,7 +37,7 @@ class MyApp extends StatelessWidget {
builder: (context, child) => GetBuilder<ConfigStore>(
builder: (config) => MaterialApp.router(
debugShowCheckedModeBanner: false,
title: '紫荆数智学堂',
title: '清控紫荆数智学堂',
theme: AppTheme.light,
darkTheme: AppTheme.dark,
themeMode: ThemeMode.light,
......
......@@ -116,12 +116,114 @@ class LoginController extends GetxController {
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 {
Tools.unfocus();
if (!agree) {
Toast.show('请先阅读并同意《用户协议》和《隐私政策》');
return;
await _showAgreementDialog(context);
if (!agree) return;
}
String type = '1';
......@@ -163,8 +265,8 @@ class LoginController extends GetxController {
/// 发送验证码
void sendCode() async {
if (!agree) {
Toast.show('请先阅读并同意《用户协议》和《隐私政策》');
return;
await _showAgreementDialog(Get.context!);
if (!agree) return;
}
final result =
await AccountAPI.sendCode(phone: phoneInput.text, type: 'login');
......
......@@ -215,13 +215,13 @@ class _LoginPageState extends State<LoginPage> {
GestureDetector(
child: Text('《用户协议》',style: TextStyle(color: Colours.cBlue,fontSize:14.w,height: 1.4)),
onTap: (){
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserAgreement','title':'用户协议'});
context.pushNamed(Routes.terms,queryParameters: {'url':kUserAgreement,'title':'用户协议'});
},
),
GestureDetector(
child: Text('《隐私政策》',style: TextStyle(color: Colours.cBlue,fontSize:14.w,height: 1.4)),
onTap: (){
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserPriAgreement','title':'隐私政策'});
context.pushNamed(Routes.terms,queryParameters: {'url':kUserPriAgreement,'title':'隐私政策'});
},
),
],
......
part of mine;
class MinePage extends StatefulWidget {
const MinePage({Key? key}) : super(key: key);
......@@ -13,7 +12,7 @@ class _MinePageState extends State<MinePage> {
Widget build(BuildContext context) {
return GetBuilder<MineController>(
init: MineController(),
builder:(controller) => Stack(
builder: (controller) => Stack(
children: [
Image.asset(
'assets/images/mine_bg.png',
......@@ -21,7 +20,7 @@ class _MinePageState extends State<MinePage> {
width: double.infinity,
),
Scaffold(
backgroundColor:Colors.transparent,
backgroundColor: Colors.transparent,
appBar: CustomAppBar(
backgroundColor: Colors.transparent,
actions: [
......@@ -42,15 +41,21 @@ class _MinePageState extends State<MinePage> {
onPressed: () => context.pushNamed(Routes.set),
),
GestureDetector(
onTap: () async{
onTap: () async {
context.pushNamed(Routes.msgs).then((value) {
controller.getNums();
});
},
child: badges.Badge(
position: badges.BadgePosition.topEnd(top: -5.w, end: 0),
showBadge: controller.num == 0?false:true,
badgeContent: Text(controller.num.toString(),style: TextStyle(fontSize: 9.w,color: Colors.white,fontWeight: Fonts.bold),),
showBadge: controller.num == 0 ? false : true,
badgeContent: Text(
controller.num.toString(),
style: TextStyle(
fontSize: 9.w,
color: Colors.white,
fontWeight: Fonts.bold),
),
badgeStyle: const badges.BadgeStyle(
badgeColor: AppTheme.primary,
shape: badges.BadgeShape.circle,
......@@ -75,39 +80,52 @@ class _MinePageState extends State<MinePage> {
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
child: BuildUser(userInfo:controller.userInfo,onTap: () async{
context.pushNamed(Routes.userInfo,extra: controller.userInfo).then((value){
child: BuildUser(
userInfo: controller.userInfo,
onTap: () async {
context
.pushNamed(Routes.userInfo,
extra: controller.userInfo)
.then((value) {
controller.getInfo();
});
},),
},
),
),
Gaps.vGaps10,
Container(
margin: EdgeInsets.symmetric(horizontal: 2.2.w),
child: BuildRead(items: controller.reads,onTap: (ReadModel model) async{
context.pushNamed(model.link??'').then((value){
child: BuildRead(
items: controller.reads,
onTap: (ReadModel model) async {
context.pushNamed(model.link ?? '').then((value) {
controller.getInfo();
});
}),
),
controller.ads.isNotEmpty?Gaps.vGaps5:const SizedBox(),
controller.ads.isNotEmpty?
Container(
controller.ads.isNotEmpty ? Gaps.vGaps5 : const SizedBox(),
controller.ads.isNotEmpty
? Container(
color: Colors.transparent,
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: BuildBanner(
items:controller.ads,
onTap:(adModel){
context.pushNamed(Routes.adDetail,extra: adModel);
} ,
),
):const SizedBox(),
controller.ads.isNotEmpty?Gaps.vGaps15:Gaps.vGaps5,
BuildAccount(items:controller.accounts,onTap: (ReadModel model) async{
context.pushNamed(model.link??'').then((value){
items: controller.ads,
onTap: (adModel) {
context.pushNamed(Routes.adDetail,
extra: adModel);
},
),
)
: const SizedBox(),
controller.ads.isNotEmpty ? Gaps.vGaps15 : Gaps.vGaps5,
BuildAccount(
items: controller.accounts,
onTap: (ReadModel model) async {
context.pushNamed(model.link ?? '').then((value) {
controller.getInfo();
});
},),
},
),
Gaps.vGaps10,
Container(
margin: EdgeInsets.symmetric(horizontal: AppTheme.margin),
......@@ -128,31 +146,56 @@ class _MinePageState extends State<MinePage> {
child: Column(
children: [
GestureDetector(
onTap:() async {
context.pushNamed(Routes.security,extra: controller.userInfo).then((value){
onTap: () async {
context
.pushNamed(Routes.security,
extra: controller.userInfo)
.then((value) {
controller.getInfo();
});
}, child: _buildItem('账户安全')
},
child: _buildItem('账户安全')),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
Container(color: Colours.cLine,margin: EdgeInsets.symmetric(horizontal: 15.w),height: 1.w,),
// _buildItem('意见反馈'),
GestureDetector(
onTap:(){
onTap: () {
context.pushNamed(Routes.feedback);
}, child: _buildItem('意见反馈')
},
child: _buildItem('意见反馈')),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
Container(color: Colours.cLine,margin: EdgeInsets.symmetric(horizontal: 15.w),height: 1.w,),
GestureDetector(
onTap: (){
onTap: () {
context.pushNamed(Routes.helpCenter);
}, child: _buildItem('帮助中心',)
},
child: _buildItem(
'帮助中心',
)),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
Container(color: Colours.cLine,margin: EdgeInsets.symmetric(horizontal: 15.w),height: 1.w,),
GestureDetector(
onTap: (){
onTap: () {
context.pushNamed(Routes.about);
}, child: _buildItem('关于我们',)
},
child: _buildItem(
'关于我们',
)),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
_buildContactItem(),
],
),
),
......@@ -169,14 +212,20 @@ class _MinePageState extends State<MinePage> {
Widget _buildItem(String title) {
return Container(
padding: EdgeInsets.only(left: 18.w,right: 15.w),
padding: EdgeInsets.only(left: 18.w, right: 15.w),
color: Colors.white,
height: 42.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(title,style: TextStyle(fontSize: 14.w,color: Colours.c3,),),
Text(
title,
style: TextStyle(
fontSize: 14.w,
color: Colours.c3,
),
),
SizedBox(
width: 5.w,
height: 8.w,
......@@ -187,4 +236,20 @@ class _MinePageState extends State<MinePage> {
);
}
Widget _buildContactItem() {
return Container(
padding: EdgeInsets.only(left: 18.w, right: 15.w),
color: Colors.white,
height: 42.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('联系电话', style: TextStyle(fontSize: 14.w, color: Colours.c3)),
Text('010-62793909',
style: TextStyle(fontSize: 14.w, color: Colours.c3)),
],
),
);
}
}
......@@ -6,37 +6,45 @@ class ReadPage extends StatefulWidget {
final String chapterName;
final String noteId;
final BookDetailModel bookDetailModel;
const ReadPage({
Key? key,
const ReadPage(
{Key? key,
required this.bookId,
required this.chapterId,
required this.chapterName,
required this.bookDetailModel,
required this.noteId
}) : super(key: key);
required this.noteId})
: super(key: key);
@override
State<ReadPage> createState() => _ReadPageState();
}
class _ReadPageState extends State<ReadPage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return GetBuilder(
init: ReadController(bookId: widget.bookId, chapterId: widget.chapterId,chapterName: widget.chapterName,bookDetailModel: widget.bookDetailModel,noteId: widget.noteId),
init: ReadController(
bookId: widget.bookId,
chapterId: widget.chapterId,
chapterName: widget.chapterName,
bookDetailModel: widget.bookDetailModel,
noteId: widget.noteId),
builder: (readController) => WillPopScope(
onWillPop: () async {
PopBackModel backModel = PopBackModel(chapterId: readController.chapterId,back: true,chapterName: readController.chapterId);
PopBackModel backModel = PopBackModel(
chapterId: readController.chapterId,
back: true,
chapterName: readController.chapterId);
context.pop(backModel);
CustomToast.dismiss();
return false;
},
child:Scaffold(
child: Scaffold(
appBar: CustomAppBar(
titleSpacing: 0,
title: Align(
......@@ -44,65 +52,72 @@ class _ReadPageState extends State<ReadPage> {
child: Text(readController.chapterName),
),
centerTitle: false,
actions: [
GestureDetector(
onTap: () {
readController.getBookDown();
},
child: Text(
readController.existDownFile == true?'':'离线阅读',
style: TextStyle(
fontSize: 14.w, color: Colours.c3),
))
],
// actions: [
// GestureDetector(
// onTap: () {
// readController.getBookDown();
// },
// child: Text(
// readController.existDownFile == true ? '' : '离线阅读',
// style: TextStyle(fontSize: 14.w, color: Colours.c3),
// ))
// ],
),
resizeToAvoidBottomInset: false,
floatingActionButton: readController.show&& !readController.toolModel.selected?GestureDetector(
onTap: (){
floatingActionButton:
readController.show && !readController.toolModel.selected
? GestureDetector(
onTap: () {
readController.setShowChat(true);
readController.setChatType(0);
},
child: Image.asset('assets/images/chat.png'),
):null,
)
: null,
// floatingActionButtonAnimator: const NoAnimationFabAnimator(),
floatingActionButtonLocation:MyFloatingActionButtonLocation(),
floatingActionButtonLocation: MyFloatingActionButtonLocation(),
body: Container(
color: Colors.white,
child: Stack(
children: [
InAppWebView(
initialFile: readController.existDownFile && !readController.netStatus?'assets/html/read_unline.html':'assets/html/read.html',
initialFile:
readController.existDownFile && !readController.netStatus
? 'assets/html/read_unline.html'
: 'assets/html/read.html',
// initialUrlRequest:URLRequest(
// url:WebUri.uri(Uri.parse(kReadTestUnderLineBook))
// // url: readController.existDownFile && !readController.netStatus? WebUri.uri(Uri.parse(kReadTestUnderLineBook)): WebUri.uri(Uri.parse(kReadBook))
// ),
initialSettings:InAppWebViewSettings(
clearCache:true,
initialSettings: InAppWebViewSettings(
clearCache: true,
// http的请求也不做限制
mixedContentMode:MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW
),
mixedContentMode:
MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW),
contextMenu: ContextMenu(
settings: ContextMenuSettings(
hideDefaultSystemContextMenuItems: true,
)
),
onWebViewCreated: (InAppWebViewController controller){
)),
onWebViewCreated: (InAppWebViewController controller) {
// CustomToast.loading();
readController.webViewController = controller;
},
onConsoleMessage: (controller, consoleMessage) {
// 接收从 WebView 发送的消息
Console.log("Received message from WebView-----------------------------: ${consoleMessage.message}");
Console.log(
"Received message from WebView-----------------------------: ${consoleMessage.message}");
},
onLoadStop: (controller, url) {
if(readController.existDownFile && !readController.netStatus){
if (readController.existDownFile &&
!readController.netStatus) {
Console.log('-----------------加载离线数据---------------');
readController.getOffLineInfo();
}
else {
String str = '$kServerUrl,${readController.bookId},${readController.chapterId},${UserStore.to.token},${readController.noteId},${readController.sModel.bookId =='0'?'':readController.sModel.combinedContent}';
} else {
String str =
'$kServerUrl,${readController.bookId},${readController.chapterId},${UserStore.to.token},${readController.noteId},${readController.sModel.bookId == '0' ? '' : readController.sModel.combinedContent}';
Console.log('-----------------加载在线数据---------------$str');
controller.evaluateJavascript(source: 'callbackInFlutterComponent("$str");');
controller.evaluateJavascript(
source: 'callbackInFlutterComponent("$str");');
}
// 添加单击事件
......@@ -112,14 +127,18 @@ class _ReadPageState extends State<ReadPage> {
});
''');
// 监听js单击回调
controller.addJavaScriptHandler(handlerName: 'onTap', callback: (args){
controller.addJavaScriptHandler(
handlerName: 'onTap',
callback: (args) {
readController.setShow(readController.show);
});
// 监听笔记回调
controller.addJavaScriptHandler(handlerName: 'noteCallBack', callback: (args){
Console.log('监听笔记回调------------------------------------------------$args');
controller.addJavaScriptHandler(
handlerName: 'noteCallBack',
callback: (args) {
Console.log(
'监听笔记回调------------------------------------------------$args');
readController.noteTitle = args.first;
readController.setShowChat(true);
readController.setChatType(1);
......@@ -127,34 +146,51 @@ class _ReadPageState extends State<ReadPage> {
});
// 监听百科回调
controller.addJavaScriptHandler(handlerName: 'baikeCallBack', callback: (args){
Console.log('监听百科回调------------------------------------------------$args');
context.pushNamed(Routes.baiKe,queryParameters: {'keyword':args.first});
controller.addJavaScriptHandler(
handlerName: 'baikeCallBack',
callback: (args) {
Console.log(
'监听百科回调------------------------------------------------$args');
context.pushNamed(Routes.baiKe,
queryParameters: {'keyword': args.first});
});
// 监听字典回调
controller.addJavaScriptHandler(handlerName: 'dictCallBack', callback: (args){
Console.log('监听字典回调------------------------------------------------$args');
context.pushNamed(Routes.baiDict,queryParameters: {'keyword':args.first});
controller.addJavaScriptHandler(
handlerName: 'dictCallBack',
callback: (args) {
Console.log(
'监听字典回调------------------------------------------------$args');
context.pushNamed(Routes.baiDict,
queryParameters: {'keyword': args.first});
});
// 监听朗读回调
controller.addJavaScriptHandler(handlerName: 'readCallBack', callback: (args){
Console.log('监听朗读回调------------------------------------------------$args');
controller.addJavaScriptHandler(
handlerName: 'readCallBack',
callback: (args) {
Console.log(
'监听朗读回调------------------------------------------------$args');
readController.speak(args.first.toString());
});
// 监听讨论回调
controller.addJavaScriptHandler(handlerName: 'discussCallBack', callback: (args){
Console.log('监听讨论回调------------------------------------------------$args');
controller.addJavaScriptHandler(
handlerName: 'discussCallBack',
callback: (args) {
Console.log(
'监听讨论回调------------------------------------------------$args');
readController.setShowChat(true);
readController.setChatType(0);
readController.noteTitle = args.first.toString();
});
// 答题和答题结果页回调
controller.addJavaScriptHandler(handlerName: 'answerResultCallBack', callback: (args){
Console.log('监听答题回调------------------------------------------------$args');
controller.addJavaScriptHandler(
handlerName: 'answerResultCallBack',
callback: (args) {
Console.log(
'监听答题回调------------------------------------------------$args');
String chapterId = args.first[0].toString();
String position = args.first[1].toString();
......@@ -164,37 +200,42 @@ class _ReadPageState extends State<ReadPage> {
String title = args.first[3].toString();
String url = '';
// 未答题
if(type == '0'){
if (type == '0') {
url = kAnswer;
}
else {
} else {
url = kAnswerResult;
}
Map<String,String> params = {
'chapter_id':chapterId,
'position':position,
'url':url,
'book_id':readController.bookId,
'token':UserStore.to.token,
'title':title
Map<String, String> params = {
'chapter_id': chapterId,
'position': position,
'url': url,
'book_id': readController.bookId,
'token': UserStore.to.token,
'title': title
};
Console.log('监听答题回调---------------给页面传参---------------------------------$params');
Console.log(
'监听答题回调---------------给页面传参---------------------------------$params');
// 跳转知识测评界面
context.pushNamed(Routes.answer,queryParameters: params);
context.pushNamed(Routes.answer,
queryParameters: params);
});
// 监听 上一节 下一节
controller.addJavaScriptHandler(handlerName: 'loadChapterCallBack', callback: (args){
Console.log('监听 上一节 下一节------------------------------------------------$args');
controller.addJavaScriptHandler(
handlerName: 'loadChapterCallBack',
callback: (args) {
Console.log(
'监听 上一节 下一节------------------------------------------------$args');
String chapterId = args.first[0].toString();
String chapterName = args.first[1].toString();
ChapterModel chapterModel = ChapterModel(id: int.parse(chapterId), name: chapterName);
ChapterModel chapterModel = ChapterModel(
id: int.parse(chapterId), name: chapterName);
// readController.setChapterInfo(id: chapterId, name: chapterName ?? '');
readController.selectChapter(chapterModel);
if(readController.existDownFile && !readController.netStatus){
if (readController.existDownFile &&
!readController.netStatus) {
String direction = '';
if(args.first.length == 3){
if (args.first.length == 3) {
direction = args.first[2].toString();
}
readController.getOffLineInfo(direction: direction);
......@@ -207,33 +248,43 @@ class _ReadPageState extends State<ReadPage> {
// });
// 阅读页内容中的 外部链接
controller.addJavaScriptHandler(handlerName: 'openLinkCallback', callback: (args){
Console.log('监听外部链接------------------------------------------------$args');
context.pushNamed(Routes.link,queryParameters: {'url': args.first.toString()});
controller.addJavaScriptHandler(
handlerName: 'openLinkCallback',
callback: (args) {
Console.log(
'监听外部链接------------------------------------------------$args');
context.pushNamed(Routes.link,
queryParameters: {'url': args.first.toString()});
});
// 画廊 扩展于都
controller.addJavaScriptHandler(handlerName: 'readInfoCallback', callback: (args){
controller.addJavaScriptHandler(
handlerName: 'readInfoCallback',
callback: (args) {
String position = args.first[0].toString();
String type = args.first[1].toString();
String title = args.first[2].toString();
Map<String,String> params = {
'chapter_id':readController.chapterId,
'position':position,
'book_id':readController.bookId,
'token':UserStore.to.token,
'title':title,
'base_url':kServerUrl,
'type':type
Map<String, String> params = {
'chapter_id': readController.chapterId,
'position': position,
'book_id': readController.bookId,
'token': UserStore.to.token,
'title': title,
'base_url': kServerUrl,
'type': type
};
Console.log('监听画廊 扩展于都---------------给页面传参---------------------------------$params');
context.pushNamed(Routes.readInfo,queryParameters: params);
Console.log(
'监听画廊 扩展于都---------------给页面传参---------------------------------$params');
context.pushNamed(Routes.readInfo,
queryParameters: params);
});
// 购买回调
controller.addJavaScriptHandler(handlerName: 'payCallback', callback: (args){
controller.addJavaScriptHandler(
handlerName: 'payCallback',
callback: (args) {
List<CourseModel> buy = [];
CourseModel model= CourseModel(
CourseModel model = CourseModel(
bookId: readController.bookDetailModel.bookId,
price: readController.bookDetailModel.price,
vipPrice: readController.bookDetailModel.vipPrice,
......@@ -241,100 +292,119 @@ class _ReadPageState extends State<ReadPage> {
bookName: readController.bookDetailModel.bookName,
cartId: 0,
status: 1,
selected: true
);
selected: true);
buy.add(model);
context.pushNamed(Routes.bookPay,extra: buy).then((value){
context
.pushNamed(Routes.bookPay, extra: buy)
.then((value) {
controller.reload();
});
});
// 图片预览
controller.addJavaScriptHandler(handlerName: 'scaleImageCallback', callback: (args){
controller.addJavaScriptHandler(
handlerName: 'scaleImageCallback',
callback: (args) {
String url = args.first[0].toString();
String title = args.first[1].toString();
Map<String,String> params = {
'url':url,
'title':title,
Map<String, String> params = {
'url': url,
'title': title,
};
Console.log('监听图片预览---------------给页面传参---------------------------------$params');
context.pushNamed(Routes.scaleImage,queryParameters: params);
Console.log(
'监听图片预览---------------给页面传参---------------------------------$params');
context.pushNamed(Routes.scaleImage,
queryParameters: params);
});
// 前端 token过期回调
controller.addJavaScriptHandler(handlerName: 'refreshTokenCallback', callback: (args) async {
controller.addJavaScriptHandler(
handlerName: 'refreshTokenCallback',
callback: (args) async {
Console.log('刷新token------------------------');
final result = await readController.refreshToken();
Map<String, dynamic> param = {
'token': result,
};
String jsonStr = jsonEncode(param);
Console.log('刷新token------------------------$jsonStr');
controller.evaluateJavascript(source: 'refreshTokenSuccess($jsonStr)');
Console.log(
'刷新token------------------------$jsonStr');
controller.evaluateJavascript(
source: 'refreshTokenSuccess($jsonStr)');
});
controller.addJavaScriptHandler(handlerName: 'showLoadingCallback', callback: (args) async {
controller.addJavaScriptHandler(
handlerName: 'showLoadingCallback',
callback: (args) async {
CustomToast.loading();
});
controller.addJavaScriptHandler(handlerName: 'dismissLoadingCallback', callback: (args) async {
controller.addJavaScriptHandler(
handlerName: 'dismissLoadingCallback',
callback: (args) async {
CustomToast.dismiss();
});
/// 离线需要参数
// 添加高亮划线笔记
controller.addJavaScriptHandler(handlerName: 'offlineAddNoteCallBack', callback: (args){
Console.log('添加笔记回调------------------------------------------${args[0]}');
Map<String,dynamic> data = args[0];
controller.addJavaScriptHandler(
handlerName: 'offlineAddNoteCallBack',
callback: (args) {
Console.log(
'添加笔记回调------------------------------------------${args[0]}');
Map<String, dynamic> data = args[0];
readController.addLocalNote(data);
});
// 删除高亮划线笔记
controller.addJavaScriptHandler(handlerName: 'offlineDelNoteCallBack', callback: (args){
Console.log('删除笔记回调------------------------------------------${args[0]}');
Map<String,dynamic> data = args[0];
controller.addJavaScriptHandler(
handlerName: 'offlineDelNoteCallBack',
callback: (args) {
Console.log(
'删除笔记回调------------------------------------------${args[0]}');
Map<String, dynamic> data = args[0];
int notesId = 0;
if (data['notes_id'].runtimeType == int) {
notesId = data['notes_id'];
}
else if (data['notes_id'].runtimeType == String){
} else if (data['notes_id'].runtimeType == String) {
notesId = int.parse(data['notes_id']);
}
int id = 0;
if(data.keys.contains('id')){
if (data.keys.contains('id')) {
// id = data['id'];
if (data['id'].runtimeType == int) {
id = data['id'];
}
else if (data['id'].runtimeType == String){
} else if (data['id'].runtimeType == String) {
id = int.parse(data['id']);
}
}
readController.delLocalNote(noteId: notesId,id: id);
readController.delLocalNote(noteId: notesId, id: id);
});
// 更新高亮划线笔记
controller.addJavaScriptHandler(handlerName: 'offlineUpdateNoteCallBack', callback: (args){
Console.log('更新笔记回调------------------------------------------${args[0]}');
controller.addJavaScriptHandler(
handlerName: 'offlineUpdateNoteCallBack',
callback: (args) {
Console.log(
'更新笔记回调------------------------------------------${args[0]}');
// int id = 0;
// Map<String, dynamic> data = {};
Map<String,dynamic> data = args[0];
Map<String, dynamic> data = args[0];
int notesId = 0;
if (data['notes_id'].runtimeType == int) {
notesId = data['notes_id'];
}
else if (data['notes_id'].runtimeType == String){
} else if (data['notes_id'].runtimeType == String) {
notesId = int.parse(data['notes_id']);
}
int id = 0;
if(data.keys.contains('id')){
if (data.keys.contains('id')) {
if (data['id'].runtimeType == int) {
id = data['id'];
}
else if (data['id'].runtimeType == String){
} else if (data['id'].runtimeType == String) {
id = int.parse(data['id']);
}
}
readController.updateLocalNote(notesId: notesId,id:id, data: data);
readController.updateLocalNote(
notesId: notesId, id: id, data: data);
});
},
),
......@@ -343,12 +413,13 @@ class _ReadPageState extends State<ReadPage> {
right: 0,
top: 0,
bottom: 69,
child: _showContent(readController,readController.toolModel)
),
child:
_showContent(readController, readController.toolModel)),
/// 底部工具栏布局
Visibility(
visible: readController.show,
child:Positioned(
child: Positioned(
left: 0,
right: 0,
bottom: 0,
......@@ -358,11 +429,10 @@ class _ReadPageState extends State<ReadPage> {
height: 69,
color: Colors.limeAccent,
alignment: Alignment.center,
child: _createToolBar(readController)
),
),
)
child: _createToolBar(readController)),
),
)),
/// 悬浮按钮点击发起话题布局
Visibility(
visible: readController.showChat,
......@@ -370,12 +440,11 @@ class _ReadPageState extends State<ReadPage> {
left: 0,
right: 0,
top: 0,
bottom:0,
bottom: 0,
child: GestureDetector(
onTap: (){
onTap: () {
readController.setShowChat(false);
readController.clearAllDiscussInput();
},
child: Container(
color: const Color(0xFF000000).withOpacity(0.5),
......@@ -383,18 +452,20 @@ class _ReadPageState extends State<ReadPage> {
reverse: true,
child: Container(
color: Colors.white,
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context)
.viewInsets
.bottom),
// alignment:Alignment.bottomCenter,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: (){},
child: ReadInputDiscuss(controller: readController,)
)
onTap: () {},
child: ReadInputDiscuss(
controller: readController,
))),
),
),
),
)
),
)),
),
],
),
......@@ -402,17 +473,16 @@ class _ReadPageState extends State<ReadPage> {
),
),
);
}
// 目录 笔记 讨论 工具栏
Widget _createToolBar(ReadController controller){
Widget _createToolBar(ReadController controller) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: controller.tools.map((model){
children: controller.tools.map((model) {
return Expanded(
child: GestureDetector(
onTap: (){
onTap: () {
controller.chooseTool(model);
},
child: Container(
......@@ -425,36 +495,54 @@ class _ReadPageState extends State<ReadPage> {
SizedBox(
width: 25,
height: 25,
child: Image.asset(model.selected?model.activeIcon:model.icon,fit: BoxFit.cover,)
child: Image.asset(
model.selected ? model.activeIcon : model.icon,
fit: BoxFit.cover,
)),
SizedBox(
height: 2.5.w,
),
SizedBox(height: 2.5.w,),
model.selected?Text(model.name,style: TextStyle(fontSize: 12.w,height: 1.4,fontWeight: Fonts.boldSemi,color: AppTheme.primary),)
:Text(model.name,style: TextStyle(fontSize: 12.w,height: 1.4,fontWeight: Fonts.medium,color: Colours.c6))
model.selected
? Text(
model.name,
style: TextStyle(
fontSize: 12.w,
height: 1.4,
fontWeight: Fonts.boldSemi,
color: AppTheme.primary),
)
: Text(model.name,
style: TextStyle(
fontSize: 12.w,
height: 1.4,
fontWeight: Fonts.medium,
color: Colours.c6))
],
),
),
),
);
}).toList()
);
}).toList());
}
Widget detail(ReadController controller,ToolModel model){
if(model.tag == 0){
return ReadCategoryPage(controller: controller,
Widget detail(ReadController controller, ToolModel model) {
if (model.tag == 0) {
return ReadCategoryPage(
controller: controller,
// 点 x 事件
onTap: (){
onTap: () {
controller.chooseTool(model);
},
// 点 搜索全部 列表 某一项 事件
onTapSearchItem: (SearchAllModel sModel){
onTapSearchItem: (SearchAllModel sModel) {
controller.chooseTool(model);
// 加载阅读界面 参数:chapter_id text
controller.sModel = sModel;
controller.selectChapter(ChapterModel(id: sModel.chapterId,name: sModel.chapterName));
controller.selectChapter(
ChapterModel(id: sModel.chapterId, name: sModel.chapterName));
controller.webViewController.reload();
},
onTapChapter: (ChapterModel chapterModel){
onTapChapter: (ChapterModel chapterModel) {
// 配置选择的章节
controller.selectChapter(chapterModel);
// 取消选中 tool
......@@ -464,76 +552,78 @@ class _ReadPageState extends State<ReadPage> {
// controller.setCurrentReadChapterId();
},
);
}
else if(model.tag == 1){
} else if (model.tag == 1) {
return ReadNotePage(
// 点 x 事件
onTap: (){
onTap: () {
controller.chooseTool(model);
},
// 点 搜索全部 列表 某一项 事件
onTapSearchItem: (SearchAllModel sModel){
onTapSearchItem: (SearchAllModel sModel) {
controller.chooseTool(model);
// 重新加载阅读界面 参数:chapter_id text
controller.sModel = sModel;
controller.selectChapter(ChapterModel(id: sModel.chapterId,name: sModel.chapterName));
controller.selectChapter(
ChapterModel(id: sModel.chapterId, name: sModel.chapterName));
controller.webViewController.reload();
},
// 删除笔记后 重新刷新web页
delTapCallBack: (){
delTapCallBack: () {
controller.webViewController.reload();
},
bookDetailModel: controller.bookDetailModel,
chapterId: controller.chapterId,
);
}
else if(model.tag == 2){
} else if (model.tag == 2) {
return ReadDiscussPage(
// 点 x 事件
onTap: (){
onTap: () {
controller.chooseTool(model);
},
// 点 搜索全部 列表 某一项 事件
onTapSearchItem: (SearchAllModel sModel){
onTapSearchItem: (SearchAllModel sModel) {
controller.chooseTool(model);
controller.sModel = sModel;
// 重新加载阅读界面 参数:chapter_id text
controller.selectChapter(ChapterModel(id: sModel.chapterId,name: sModel.chapterName));
controller.selectChapter(
ChapterModel(id: sModel.chapterId, name: sModel.chapterName));
controller.webViewController.reload();
},
bookDetailModel: controller.bookDetailModel,
chapterId:controller.chapterId,
chapterId: controller.chapterId,
);
}
return const SizedBox();
}
// 目录、评论、笔记 背景
Widget _showContent(ReadController controller,ToolModel model) {
if (controller.show){
if(model.selected){
Widget _showContent(ReadController controller, ToolModel model) {
if (controller.show) {
if (model.selected) {
return GestureDetector(
onTap: (){
onTap: () {
controller.chooseTool(model);
},
child: Container(
color: const Color(0xFF000000).withOpacity(0.5),
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.2),
padding:
EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.2),
child: ClipRRect(
borderRadius: BorderRadius.only(topRight: Radius.circular(8.w),topLeft: Radius.circular(8.w)),
borderRadius: BorderRadius.only(
topRight: Radius.circular(8.w),
topLeft: Radius.circular(8.w)),
child: Container(
color: Colors.white,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: (){},
onTap: () {},
child: detail(controller, model),
)
),
)),
),
// child: ReadCategoryPage(),
),
);
}
else{
} else {
return const SizedBox();
}
}
......@@ -541,18 +631,15 @@ class _ReadPageState extends State<ReadPage> {
}
}
// 定制 悬浮按钮位置
class MyFloatingActionButtonLocation extends FloatingActionButtonLocation {
@override
Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
// You can customize the position of the FloatingActionButton here
return Offset(scaffoldGeometry.scaffoldSize.width - 15.0.w - 45.w, scaffoldGeometry.scaffoldSize.height - 118.w-49.w);
return Offset(scaffoldGeometry.scaffoldSize.width - 15.0.w - 45.w,
scaffoldGeometry.scaffoldSize.height - 118.w - 49.w);
}
@override
String toString() => 'MyFloatingActionButtonLocation';
}
......@@ -192,8 +192,11 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
onTap: () async{
final assets = await AssetsPicker.image(
context: context,
purpose: '用于从相册中选择图片添加到讨论中,仅用于上传和显示图片内容。',
);
widget.controller.addDiscussInputImages(assets!.path);
if (assets != null) {
widget.controller.addDiscussInputImages(assets.path);
}
},
child: SizedBox(
// color: Colors.red,
......
library splash_page;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart';
import 'package:go_router/go_router.dart';
import '../../routes/index.dart';
import '../../services/index.dart';
import '../../utils/index.dart';
part 'view.dart';
......@@ -8,31 +8,135 @@ class SplashPage extends StatefulWidget {
}
class _SplashPageState extends State<SplashPage> {
bool _navigated = false;
@override
void initState() {
super.initState();
Future.wait([
Future.delayed(const Duration(seconds: 2))
]).whenComplete(() async {
WidgetsBinding.instance.addPostFrameCallback((_) {
_startLaunchFlow();
});
}
Future<void> _startLaunchFlow() async {
await _showPrivacyNoticeIfNeeded();
if (!mounted || _navigated) return;
await Future.delayed(const Duration(seconds: 2));
if (!mounted || _navigated) return;
final netStatus = await Tools.checkCurrentNetStatus();
if (netStatus){
if (!mounted || _navigated) return;
if (netStatus) {
final ads = await CommonAPI.list(type: '1');
if (!mounted || _navigated) return;
if (ads.isNotEmpty) {
context.pushReplacementNamed(Routes.ad,extra: ads);
}
else {
context.pushReplacementNamed(Routes.main);
_navigated = true;
context.pushReplacementNamed(Routes.ad, extra: ads);
return;
}
}
else {
_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
......@@ -43,7 +147,10 @@ class _SplashPageState extends State<SplashPage> {
body: SizedBox(
height: double.infinity,
width: double.infinity,
child: Image.asset('assets/images/splash.png',fit: BoxFit.cover,),
child: Image.asset(
'assets/images/splash.png',
fit: BoxFit.cover,
),
)
// const Column(
// crossAxisAlignment: CrossAxisAlignment.stretch,
......@@ -57,7 +164,7 @@ class _SplashPageState extends State<SplashPage> {
// url: 'assets/images/logo.png',
// fit: BoxFit.contain,
// ),
// title: Text('紫荆数智学堂'),
// title: Text('清控紫荆数智学堂'),
// ),
// )
// ),
......
......@@ -44,9 +44,16 @@ class _AboutPageState extends State<AboutPage> {
child: const CustomImage.asset(url: 'assets/images/icon.png'),
),
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,
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(
......@@ -56,29 +63,50 @@ class _AboutPageState extends State<AboutPage> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
child: Text('《用户协议》',textAlign: TextAlign.right,style: TextStyle(fontSize: 10.w,color: const Color(0xFF2A82D9)),),
onTap: (){
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserAgreement','title':'用户协议'});
child: Text(
'《用户协议》',
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 10.w, color: const Color(0xFF2A82D9)),
),
onTap: () {
context.pushNamed(Routes.terms, queryParameters: {
'url': kUserAgreement,
'title': '用户协议'
});
},
),
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,
GestureDetector(
child: Text('《隐私协议》',textAlign: TextAlign.left,style: TextStyle(fontSize: 10.w,color: const Color(0xFF2A82D9))),
onTap:(){
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserPriAgreement','title':'隐私政策'});
} ,
child: Text('《隐私协议》',
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 10.w, color: const Color(0xFF2A82D9))),
onTap: () {
context.pushNamed(Routes.terms, queryParameters: {
'url': kUserPriAgreement,
'title': '隐私政策'
});
},
)
],
),
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,
],
),
)
],
),
);
......
part of recharge;
class CoinRechargePage extends StatefulWidget {
const CoinRechargePage({Key? key}) : super(key: key);
......@@ -9,12 +7,13 @@ class CoinRechargePage extends StatefulWidget {
State<CoinRechargePage> createState() => _CoinRechargePageState();
}
class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepAliveClientMixin {
class _CoinRechargePageState extends State<CoinRechargePage>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
return GetBuilder<CoinRechargeController>(
init: CoinRechargeController(context: context),
builder:(controller) => Column(
builder: (controller) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
......@@ -27,14 +26,14 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('紫荆币充值',textAlign:TextAlign.center),
Text('紫荆币充值', textAlign: TextAlign.center),
],
),
Positioned(
right: 0.w,
top: 0.w,
child: GestureDetector(
onTap: (){
onTap: () {
context.pop();
},
child: SizedBox(
......@@ -49,15 +48,14 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
),
Container(
margin: EdgeInsets.symmetric(horizontal: 20.w),
child: _buildAudioGridView(controller)
),
child: _buildAudioGridView(controller)),
Gaps.vGaps15,
_buildListView(controller),
Gaps.vGaps15,
Container(
margin: EdgeInsets.symmetric(horizontal:15.w),
margin: EdgeInsets.symmetric(horizontal: 15.w),
child: CustomGradientButton(
text: '立即充值${controller.rechargeModel.priceName??''}',
text: '立即充值${controller.rechargeModel.priceName ?? ''}',
isEnabled: true,
onPressed: () {
controller.createRechargeOrder();
......@@ -66,15 +64,25 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
),
Gaps.vGaps15,
GestureDetector(
onTap: (){
context.pushNamed(Routes.terms,queryParameters: {'url':'$kServerUrl$kUserRechargeAgreement','title':'用户充值协议'});
onTap: () {
context.pushNamed(Routes.terms, queryParameters: {
'url': kUserRechargeAgreement,
'title': '用户充值协议'
});
},
child: RichText(text: TextSpan(
children: [
TextSpan(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))),
]
)),
child: RichText(
text: TextSpan(children: [
TextSpan(
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
],
......@@ -82,7 +90,7 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
);
}
Widget _buildAudioGridView(CoinRechargeController controller){
Widget _buildAudioGridView(CoinRechargeController controller) {
return GridView.builder(
// padding: const EdgeInsets.only(left: 13,top: 10),
physics: const NeverScrollableScrollPhysics(),
......@@ -91,26 +99,41 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
crossAxisCount: 3,
crossAxisSpacing: 10.w,
mainAxisSpacing: 10.w,
childAspectRatio: 2
),
childAspectRatio: 2),
itemBuilder: (BuildContext context, int index) {
CoinModel model = controller.data[index];
return GestureDetector(
onTap: (){
onTap: () {
controller.choose(model);
},
child: Container(
decoration: BoxDecoration(
color: model.selected?AppTheme.primary.withOpacity(0.1):Colors.white,
color: model.selected
? AppTheme.primary.withOpacity(0.1)
: Colors.white,
borderRadius: BorderRadius.circular(8.w),
border: Border.all(width: 0.5.w,color: model.selected?AppTheme.primary:const Color(0xFFDADADA))
),
border: Border.all(
width: 0.5.w,
color: model.selected
? AppTheme.primary
: const Color(0xFFDADADA))),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(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),),
Text(
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
itemCount: controller.data.length,
);
}
Widget _buildListView(CoinRechargeController controller){
Widget _buildListView(CoinRechargeController controller) {
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 20.w),
shrinkWrap:true,
itemBuilder: (BuildContext context, int index){
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
PayModel model = controller.pays[index];
return GestureDetector(
onTap: (){
onTap: () {
controller.setPayModel(model);
},
child: BuildPayWay(model:model,));
child: BuildPayWay(
model: model,
));
},
itemCount: controller.pays.length,
);
......
......@@ -167,8 +167,11 @@ class _UserEditNotePageState extends State<UserEditNotePage> {
onTap: () async{
final assets = await AssetsPicker.image(
context: context,
purpose: '用于从相册中选择图片添加到笔记中,仅用于上传和显示图片内容。',
);
controller.addImage(assets!.path);
if (assets != null) {
controller.addImage(assets.path);
}
},
child: SizedBox(
// color: Colors.red,
......
......@@ -168,9 +168,12 @@ class _UserInfoPageState extends State<UserInfoPage> {
Console.log("点击拍照");
final assets = await AssetsPicker.camera(
context: context,
purpose: '用于使用相机拍照设置用户头像,仅用于上传和显示您的头像图片。',
);
controller.upload(path: assets!.path);
if (assets != null) {
controller.upload(path: assets.path);
controller.show();
}
},
child: Container(
height: 45.w,
......@@ -202,9 +205,12 @@ class _UserInfoPageState extends State<UserInfoPage> {
Console.log("点击选择现有图片");
final assets = await AssetsPicker.image(
context: context,
purpose: '用于从相册中选择图片设置用户头像,仅用于上传和显示您的头像图片。',
);
controller.upload(path: assets!.path);
if (assets != null) {
controller.upload(path: assets.path);
controller.show();
}
},
child: Container(
height: 45.w,
......
......@@ -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_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 '../models/index.dart';
......@@ -67,7 +68,6 @@ import '../pages/user_terms/index.dart';
import '../pages/version/index.dart';
import '../store/index.dart';
part 'observers.dart';
part 'routes.dart';
part 'transitions.dart';
......@@ -3,13 +3,14 @@ part of routes;
abstract class Routes {
static final RouteObserver<Route> observer = RouteObservers();
static List<String> history = [];
static final ValueNotifier<bool> _routeRefreshFallback =
ValueNotifier<bool>(false);
static const splash = 'splash';
static const main = 'main';
static const ad = 'ad';
static const adDetail = 'ad_detail';
static const web = 'read_web';
static const answer = 'answer';
static const readInfo = 'read_info';
......@@ -22,7 +23,6 @@ abstract class Routes {
// 搜索
static const search = 'search';
/// 登录模块
// 登录
static const login = 'login';
......@@ -31,7 +31,6 @@ abstract class Routes {
// 重置密码
static const resetPwd = 'reset_pwd';
/// 课程模块
// 详情
static const bookDetail = 'book_detail';
......@@ -40,7 +39,6 @@ abstract class Routes {
// 学习报告
static const studyReport = 'study_report';
/// 图书馆模块
// 百科
static const baiKe = 'bai_ke';
......@@ -54,8 +52,6 @@ abstract class Routes {
static const shop = 'shop';
static const payCoupon = 'pay_coupon';
/// 我的模块
// 个人信息
static const userInfo = 'user_info';
......@@ -108,55 +104,58 @@ abstract class Routes {
// 意见反馈
static const feedback = 'feedback';
// 订单评价
static const orderEvaluate= 'order_evaluate';
static const orderEvaluate = 'order_evaluate';
// 帮助中心
static const helpCenter= 'help_center';
static const helpCenter = 'help_center';
// 帮助中心内容
static const helpCenterContent = 'help_center_content';
// 订单列表
static const order= 'order';
static const order = 'order';
// 已完成订单
static const orderCompleted= 'order_completed';
static const orderCompleted = 'order_completed';
// 图书待付款订单
static const orderAwaiting= 'order_awaiting';
static const orderAwaiting = 'order_awaiting';
// 图书取消订单
static const orderCancel= 'order_cancel';
static const orderCancel = 'order_cancel';
// 紫荆币待付款订单
static const orderCoinAwaiting= 'order_coin_awaiting';
static const orderCoinAwaiting = 'order_coin_awaiting';
// 紫荆币已取消订单
static const orderCoinCancel= 'order_coin_cancel';
static const orderCoinCancel = 'order_coin_cancel';
// 紫荆币已完成订单
static const orderCoinCompleted= 'order_coin_completed';
static const orderCoinCompleted = 'order_coin_completed';
// 已退款订单
static const orderRefunded= 'order_refunded';
static const orderRefunded = 'order_refunded';
// 搜索订单
static const orderSearch= 'order_search';
static const orderSearch = 'order_search';
// 图片预览
static const imageView = 'image_view';
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
static UserStore? get _userStoreOrNull =>
Get.isRegistered<UserStore>() ? Get.find<UserStore>() : null;
static final GoRouter config = GoRouter(
navigatorKey: navigatorKey,
initialLocation: '/$splash',
// initialLocation: '/',
observers: [observer],
refreshListenable:UserStore.to.needLogin,
refreshListenable: _userStoreOrNull?.needLogin ?? _routeRefreshFallback,
redirect: (context, state) {
final userStore = _userStoreOrNull;
final currentRoute = state.matchedLocation;
final loginRoute = state.namedLocation(login);
final mainRoute = state.namedLocation(main);
if (currentRoute.contains(mainRoute)) return null;
if(currentRoute.contains(loginRoute)) return null;
if(!UserStore.to.isLogin) return loginRoute;
if (currentRoute.contains(loginRoute)) return null;
if (!(userStore?.isLogin ?? false)) return loginRoute;
return null;
},
routes: [
GoRoute(
path: '/$splash',
name: splash,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const SplashPage(),
......@@ -165,589 +164,533 @@ abstract class Routes {
GoRoute(
path: '/',
name: main,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const MainPage()
)
),
child: const MainPage())),
GoRoute(
path: '/$ad',
name: ad,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: AdPage(ads: state.extra as List<AdModel>,)
)
),
child: AdPage(
ads: state.extra as List<AdModel>,
))),
/// 登录模块
GoRoute(
path: '/$login',
name: login,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const LoginPage()
)
),
child: const LoginPage())),
GoRoute(
path: '/$forgetPwd',
name: forgetPwd,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const ForgetPwdPage()
)
),
child: const ForgetPwdPage())),
GoRoute(
path: '/$resetPwd',
name: resetPwd,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: ResetPwdPage(
phone: state.uri.queryParameters['phone'].toString(),
code: state.uri.queryParameters['code'].toString(),
)
)
),
))),
GoRoute(
path: '/$web',
name: web,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: ReadPage(
bookId: state.uri.queryParameters['book_id'].toString(),
chapterId: state.uri.queryParameters['chapter_id'].toString(),
chapterName: state.uri.queryParameters['chapter_name'].toString(),
chapterName:
state.uri.queryParameters['chapter_name'].toString(),
bookDetailModel: state.extra as BookDetailModel,
noteId: state.uri.queryParameters['note_id'].toString(),
)
)
),
))),
GoRoute(
path: '/$about',
name: about,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const AboutPage()
)
),
child: const AboutPage())),
GoRoute(
path: '/$bookPay',
name: bookPay,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: BookPayPage(buy: state.extra as List<CourseModel>,)
)
),
child: BookPayPage(
buy: state.extra as List<CourseModel>,
))),
GoRoute(
path: '/$terms',
name: terms,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: TermsPage(
url: state.uri.queryParameters['url'].toString(),
title: state.uri.queryParameters['title'].toString(),
)
)
),
))),
GoRoute(
path: '/$msgs',
name: msgs,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const MsgPage()
)
),
child: const MsgPage())),
GoRoute(
path: '/$userInfo',
name: userInfo,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserInfoPage(userInfo: state.extra as UserInfoModel,)
)
),
child: UserInfoPage(
userInfo: state.extra as UserInfoModel,
))),
GoRoute(
path: '/$nike',
name: nike,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserNickPage(userInfo: state.extra as UserInfoModel,)
)
),
child: UserNickPage(
userInfo: state.extra as UserInfoModel,
))),
GoRoute(
path: '/$coin',
name: coin,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserCoinPage()
)
),
child: const UserCoinPage())),
GoRoute(
path: '/$bookDetail',
name: bookDetail,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: BookDetailPage(
bookId: state.uri.queryParameters['book_id'].toString(),
)
),
)),
redirect: (context, state) => _RouteRedirect.auth(),
),
GoRoute(
path: '/$bookScore',
name: bookScore,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: BookScorePage(
bookId: state.uri.queryParameters['book_id'].toString(),
)
)
),
))),
GoRoute(
path: '/$studyReport',
name: studyReport,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: StudyReportPage(
bookId: state.uri.queryParameters['book_id'].toString(),
)
)
),
))),
GoRoute(
path: '/$creditPoints',
name: creditPoints,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const CreditPointsPage()
)
),
child: const CreditPointsPage())),
GoRoute(
path: '/$payCoupon',
name: payCoupon,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: PayCouponPage(
payController: state.extra as BookPayController,
)
)
),
))),
GoRoute(
path: '/$search',
name: search,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const SearchPage()
)
),
child: const SearchPage())),
GoRoute(
path: '/$studyHistory',
name: studyHistory,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const StudyHistoryPage()
)
),
child: const StudyHistoryPage())),
GoRoute(
path: '/$love',
name: love,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserLovePage()
)
),
child: const UserLovePage())),
GoRoute(
path: '/$shop',
name: shop,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const BookShopPage()
)
),
child: const BookShopPage())),
GoRoute(
path: '/$note',
name: note,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserNotePage()
)
),
child: const UserNotePage())),
GoRoute(
path: '/$wrong',
name: wrong,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserWrongPage()
)
),
child: const UserWrongPage())),
GoRoute(
path: '/$wrongDes',
name: wrongDes,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserWrongDesPage(
model: state.extra as CourseModel
)
)
),
child: UserWrongDesPage(model: state.extra as CourseModel))),
GoRoute(
path: '/$point',
name: point,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserPointPage()
)
),
child: const UserPointPage())),
GoRoute(
path: '/$discuss',
name: discuss,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserDiscussPage()
)
),
child: const UserDiscussPage())),
GoRoute(
path: '/$coupon',
name: coupon,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserCouponPage()
)
),
child: const UserCouponPage())),
GoRoute(
path: '/$set',
name: set,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserSetPage()
)
),
child: const UserSetPage())),
GoRoute(
path: '/$version',
name: version,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const VersionPage()
)
),
child: const VersionPage())),
GoRoute(
path: '/$versionDes',
name: versionDes,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: VersionDesPage(model: state.extra as VersionModel,)
)
),
child: VersionDesPage(
model: state.extra as VersionModel,
))),
GoRoute(
path: '/$security',
name: security,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserSecurityPage(model: state.extra as UserInfoModel,)
)
),
child: UserSecurityPage(
model: state.extra as UserInfoModel,
))),
GoRoute(
path: '/$changePhone',
name: changePhone,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const ChangePhonePage()
)
),
child: const ChangePhonePage())),
GoRoute(
path: '/$changePwd',
name: changePwd,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: ChangePwdPage(
userInfo: state.extra as UserInfoModel,
type: state.uri.queryParameters['type'].toString(),
)
)
),
))),
GoRoute(
path: '/$feedback',
name: feedback,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserFeedbackPage()
)
),
child: const UserFeedbackPage())),
GoRoute(
path: '/$noteDes',
name: noteDes,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserNotesDesPage(model: state.extra as CourseModel,)
)
),
child: UserNotesDesPage(
model: state.extra as CourseModel,
))),
GoRoute(
path: '/$gender',
name: gender,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserGenderPage(userInfo: state.extra as UserInfoModel,)
)
),
child: UserGenderPage(
userInfo: state.extra as UserInfoModel,
))),
GoRoute(
path: '/$discussDes',
name: discussDes,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserDiscussDesPage(model: state.extra as CourseModel,)
)
),
child: UserDiscussDesPage(
model: state.extra as CourseModel,
))),
GoRoute(
path: '/$orderEvaluate',
name: orderEvaluate,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderEvaluatePage(orderNum:state.uri.queryParameters['orderNum'].toString(),orderInfoModel: state.extra as OrderInfoModel,)
)
),
GoRoute( // 帮助中心
child: UserOrderEvaluatePage(
orderNum: state.uri.queryParameters['orderNum'].toString(),
orderInfoModel: state.extra as OrderInfoModel,
))),
GoRoute(
// 帮助中心
path: '/$helpCenter',
name: helpCenter,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const HelpCenterPage()
)
),
GoRoute( // 帮助中心内容
child: const HelpCenterPage())),
GoRoute(
// 帮助中心内容
path: '/$helpCenterContent',
name: helpCenterContent,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: HelpCenterContentPage(
id: state.uri.queryParameters['id'].toString(),
title: state.uri.queryParameters['title'].toString(),
)
)
),
GoRoute( // 已完成订单
))),
GoRoute(
// 已完成订单
path: '/$order',
name: order,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserOrderPage()
)
),
GoRoute( // 已完成订单
child: const UserOrderPage())),
GoRoute(
// 已完成订单
path: '/$orderCompleted',
name: orderCompleted,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderCompletedPage(orderNum: state.uri.queryParameters['orderNum'].toString())
)
),
GoRoute( // 图书待付款订单
child: UserOrderCompletedPage(
orderNum:
state.uri.queryParameters['orderNum'].toString()))),
GoRoute(
// 图书待付款订单
path: '/$orderAwaiting',
name: orderAwaiting,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderAwaitingPage(orderNum: state.uri.queryParameters['orderNum'].toString())
)
),
GoRoute( // 图书取消订单
child: UserOrderAwaitingPage(
orderNum:
state.uri.queryParameters['orderNum'].toString()))),
GoRoute(
// 图书取消订单
path: '/$orderCancel',
name: orderCancel,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderCancelDetailPage(orderNum: state.uri.queryParameters['orderNum'].toString())
)
),
GoRoute( // 紫荆币待付款订单
child: UserOrderCancelDetailPage(
orderNum:
state.uri.queryParameters['orderNum'].toString()))),
GoRoute(
// 紫荆币待付款订单
path: '/$orderCoinAwaiting',
name: orderCoinAwaiting,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderCoinAwaitingPage(orderNum: state.uri.queryParameters['orderNum'].toString())
)
),
GoRoute( // 紫荆币已取消订单
child: UserOrderCoinAwaitingPage(
orderNum:
state.uri.queryParameters['orderNum'].toString()))),
GoRoute(
// 紫荆币已取消订单
path: '/$orderCoinCancel',
name: orderCoinCancel,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderCoinCancelPage(orderNum: state.uri.queryParameters['orderNum'].toString())
)
),
GoRoute( // 紫荆币已完成订单
child: UserOrderCoinCancelPage(
orderNum:
state.uri.queryParameters['orderNum'].toString()))),
GoRoute(
// 紫荆币已完成订单
path: '/$orderCoinCompleted',
name: orderCoinCompleted,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderCoinCompletedPage(orderNum: state.uri.queryParameters['orderNum'].toString())
)
),
GoRoute( // 已退款订单
child: UserOrderCoinCompletedPage(
orderNum:
state.uri.queryParameters['orderNum'].toString()))),
GoRoute(
// 已退款订单
path: '/$orderRefunded',
name: orderRefunded,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserOrderRefundedPage(orderNum: state.uri.queryParameters['orderNum'].toString())
)
),
GoRoute( // 百科
child: UserOrderRefundedPage(
orderNum:
state.uri.queryParameters['orderNum'].toString()))),
GoRoute(
// 百科
path: '/$baiKe',
name: baiKe,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: BaiKePage(
keyword: state.uri.queryParameters['keyword'].toString(),
)
)
),
))),
GoRoute(
path: '/$editNote',
name: editNote,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserEditNotePage(model: state.extra as NoteModel,bookId: state.uri.queryParameters['book_id'].toString(),)
)
),
GoRoute( // 订单搜索
child: UserEditNotePage(
model: state.extra as NoteModel,
bookId: state.uri.queryParameters['book_id'].toString(),
))),
GoRoute(
// 订单搜索
path: '/$orderSearch',
name: orderSearch,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const OrderSearchPage(),
)
),
GoRoute( // 订单搜索
)),
GoRoute(
// 订单搜索
path: '/$adDetail',
name: adDetail,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: AdDetailPage(adModel: state.extra as AdModel,),
)
child: AdDetailPage(
adModel: state.extra as AdModel,
),
GoRoute( // 字典
)),
GoRoute(
// 字典
path: '/$baiDict',
name: baiDict,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: BaiDictPage(
keyword: state.uri.queryParameters['keyword'].toString(),
)
)
),
GoRoute( // 外部链接
))),
GoRoute(
// 外部链接
path: '/$link',
name: link,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: LinkPage(
url: state.uri.queryParameters['url'].toString(),
)
)
),
GoRoute( // 知识测评
))),
GoRoute(
// 知识测评
path: '/$answer',
name: answer,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: AnswerPage(
params: state.uri.queryParameters,
)
)
),
GoRoute( // 画廊 扩展阅读
))),
GoRoute(
// 画廊 扩展阅读
path: '/$readInfo',
name: readInfo,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: ReadInfoPage(
params: state.uri.queryParameters,
)
)
),
GoRoute( // 画廊 扩展阅读
))),
GoRoute(
// 画廊 扩展阅读
path: '/$scaleImage',
name: scaleImage,
pageBuilder: (context, state) =>CupertinoPage(
pageBuilder: (context, state) => CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: ScaleImagePage(
params: state.uri.queryParameters,
)
)
),
GoRoute( // 图片预览
))),
GoRoute(
// 图片预览
path: '/$imageView',
name: imageView,
pageBuilder: (context, state) =>BottomToTopTransitionPage(
pageBuilder: (context, state) => BottomToTopTransitionPage(
name: state.uri.toString(),
key: state.pageKey,
child: ImageViewPage(
images: state.extra as List<String>,
currentIndex: int.parse(state.uri.queryParameters['index'].toString()),
)
)
),
]
);
currentIndex:
int.parse(state.uri.queryParameters['index'].toString()),
))),
]);
}
abstract class _RouteRedirect {
static String? auth() {
if (UserStore.to.isLogin) return null;
final userStore =
Get.isRegistered<UserStore>() ? Get.find<UserStore>() : null;
if (userStore?.isLogin ?? false) return null;
return '/${Routes.login}';
}
}
part of services;
class HttpService extends GetxService {
static HttpService get to => Get.find();
late final Dio _dio;
......@@ -11,11 +10,10 @@ class HttpService extends GetxService {
final options = BaseOptions(
baseUrl: kServerUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds:10),
receiveTimeout: const Duration(seconds: 10),
headers: {},
contentType: Headers.jsonContentType,
responseType: ResponseType.json
);
responseType: ResponseType.json);
_dio = Dio(options);
_dio.interceptors.add(_RequestInterceptor());
......@@ -23,28 +21,30 @@ class HttpService extends GetxService {
}
/// 组织 headers 参数
Map<String,dynamic>? _getHeaders({bool excludeToken = false,Map<String,dynamic>? params,String? url}) {
Map<String, dynamic>? _getHeaders(
{bool excludeToken = false, Map<String, dynamic>? params, String? url}) {
final headers = <String, dynamic>{};
final userStore =
Get.isRegistered<UserStore>() ? Get.find<UserStore>() : null;
headers['appId'] = AppConfig.appID;
headers['appSecret'] = AppConfig.appSecret;
headers['timestamp'] = (DateTime.now().millisecondsSinceEpoch~/1000).toString();
headers['timestamp'] =
(DateTime.now().millisecondsSinceEpoch ~/ 1000).toString();
headers['url'] = kServerUrl + url.toString();
headers['token'] = UserStore.to.token;
headers['token'] = userStore?.token ?? '';
if (Get.isRegistered<UserStore>() &&
UserStore.to.hasToken && !excludeToken) {
if (userStore != null && userStore.hasToken && !excludeToken) {
// headers['Token'] = UserStore.to.token;
headers['Authorization'] = UserStore.to.token;
} else{
headers['Authorization'] = userStore.token;
} else {
headers['Authorization'] = '';
}
Map<String,dynamic>? tempParams = {};
Map<String, dynamic>? tempParams = {};
if (params != null) {
tempParams.addAll(params);
tempParams.addAll(headers);
headers['Sign'] = SignTool.createSign(tempParams);
}
else {
} else {
headers['Sign'] = SignTool.createSign(headers);
}
// Console.log(headers);
......@@ -53,7 +53,7 @@ class HttpService extends GetxService {
/// get
Future<ResponseModel> get(
String url,{
String url, {
Map<String, dynamic>? params,
Options? options,
CancelToken? cancelToken,
......@@ -64,10 +64,11 @@ class HttpService extends GetxService {
if (showLoading) CustomToast.loading();
try {
final requestOptions = options ?? Options();
requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
requestOptions.headers =
_getHeaders(excludeToken: excludeToken, params: params, url: url);
// 如果启用缓存,将cacheEnabled参数添加到请求选项中
if(cacheEnabled){
if (cacheEnabled) {
requestOptions.extra ??= {};
requestOptions.extra!['cacheEnabled'] = true;
}
......@@ -79,7 +80,7 @@ class HttpService extends GetxService {
);
if (showLoading) CustomToast.dismiss();
return ResponseModel.fromJson(response.data);
} catch (error){
} catch (error) {
if (error is! DioException) CustomToast.dismiss();
rethrow;
}
......@@ -87,21 +88,23 @@ class HttpService extends GetxService {
/// post
Future<ResponseModel> post(
String url,{
Map<String,dynamic>? params,
String url, {
Map<String, dynamic>? params,
Options? options,
CancelToken? cancelToken,
bool excludeToken = false,
bool showLoading = false,
bool cacheEnabled = true,
}) async{
}) async {
if (showLoading) CustomToast.loading();
try {
final requestOptions = options ?? Options();
requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
Console.log('----headers------${requestOptions.headers}\n----params------$params-----');
requestOptions.headers =
_getHeaders(excludeToken: excludeToken, params: params, url: url);
Console.log(
'----headers------${requestOptions.headers}\n----params------$params-----');
// 如果启用缓存,将cacheEnabled参数添加到请求选项中
if(cacheEnabled){
if (cacheEnabled) {
requestOptions.extra ??= {};
requestOptions.extra!['cacheEnabled'] = true;
}
......@@ -114,7 +117,7 @@ class HttpService extends GetxService {
if (showLoading) CustomToast.dismiss();
Console.log(response.data);
return ResponseModel.fromJson(response.data);
} catch (error){
} catch (error) {
if (error is! DioException) CustomToast.dismiss();
rethrow;
}
......@@ -129,58 +132,50 @@ class HttpService extends GetxService {
CancelToken? cancelToken,
bool excludeToken = false,
ProgressCallback? onSendProgress,
})async {
}) async {
final requestOptions = options ?? Options();
requestOptions.headers = _getHeaders(excludeToken: excludeToken,url:url);
final name = path.substring(path.lastIndexOf('/') + 1,path.length);
requestOptions.headers = _getHeaders(excludeToken: excludeToken, url: url);
final name = path.substring(path.lastIndexOf('/') + 1, path.length);
final image = await MultipartFile.fromFile(path, filename: name);
final formData = FormData.fromMap({
'files':image,
'file_types':fileTypes
});
final formData =
FormData.fromMap({'files': image, 'file_types': fileTypes});
final response = await _dio.post(
url,
data:formData,
options:requestOptions,
data: formData,
options: requestOptions,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
);
return ResponseModel.fromJson(response.data);
}
/// download
Future<void> download(
String url,{
String url, {
required String savePath,
Options? options,
CancelToken? cancelToken,
bool excludeToken = false,
ProgressCallback? onReceiveProgress,
}) async {
try{
try {
final requestOptions = options ?? Options();
requestOptions.headers = _getHeaders(excludeToken: excludeToken,url: url);
requestOptions.headers =
_getHeaders(excludeToken: excludeToken, url: url);
requestOptions.responseType = ResponseType.bytes;
await _dio.download(
url,
savePath,
await _dio.download(url, savePath,
options: requestOptions,
cancelToken: cancelToken,
onReceiveProgress: onReceiveProgress
);
onReceiveProgress: onReceiveProgress);
Console.log('Download completed:$savePath');
}catch(e){
} catch (e) {
Console.log('Error during download: $e');
rethrow;
}
}
}
class _RequestInterceptor extends Interceptor {
@override
void onResponse(Response response, ResponseInterceptorHandler handler) async {
......@@ -211,7 +206,7 @@ class _RequestInterceptor extends Interceptor {
),
true,
);
}else {
} else {
super.onResponse(response, handler);
}
}
......@@ -242,7 +237,8 @@ class _RequestInterceptor extends Interceptor {
var msg = '服务器错误';
switch (statusCode) {
case 403:
Console.log('----------403---------access_token-------------------------${UserStore.to.accessToken}--------------------');
Console.log(
'----------403---------access_token-------------------------${UserStore.to.accessToken}--------------------');
msg = '$statusCode - Unauthorized';
final newToken = await refreshToken();
Console.log('newToken----------$newToken');
......@@ -264,16 +260,15 @@ class _RequestInterceptor extends Interceptor {
try {
final newResponse = await Dio().fetch(requestOptions);
handler.resolve(newResponse);
}catch (e) {
} catch (e) {
handler.reject(err);
}
} else {
UserStore.to.logout();
await Tools.clearData();
CustomToast.fail('该账号已在其他设备登录,请重新登录');
Console.log('-------------------access_token-------------------------${UserStore.to.accessToken}--------------------');
Console.log(
'-------------------access_token-------------------------${UserStore.to.accessToken}--------------------');
}
break;
case 404:
......@@ -290,11 +285,10 @@ class _RequestInterceptor extends Interceptor {
break;
default:
msg = response?.data?['message']?.toString() ?? msg;
if(code == 3001){
if (code == 3001) {
// Toast.show(msg);
CustomToast.info(msg);
}
else{
} else {
CustomToast.fail(msg);
}
break;
......@@ -314,18 +308,15 @@ class _RequestInterceptor extends Interceptor {
}
Future<String?> refreshToken() async {
Console.log('--------refreshToken----------------');
final result = await HttpService.to.post(
'/v1/members/login/getToken',
final result = await HttpService.to.post('/v1/members/login/getToken',
params: {
'access_token':StorageService.to.getString(kLocalAccessToken)
}
);
'access_token': StorageService.to.getString(kLocalAccessToken)
});
if (result.data is Map) {
final String token = result.data['token'];
final String accessToken = result.data['access_token'];
if(token.isEmpty && accessToken.isEmpty){
if (token.isEmpty && accessToken.isEmpty) {
UserStore.to.logout();
// Routes.config.goNamed(Routes.login);
return null;
......@@ -340,15 +331,13 @@ class _RequestInterceptor extends Interceptor {
// 如果刷新成功,返回新的token;如果刷新失败,返回null
return null;
}
}
// 缓存拦截器
class _CacheInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
final cacheEnabled = options.extra['cacheEnabled'] ?? false;
final status = await Tools.checkCurrentNetStatus();
if (cacheEnabled && !status) {
......@@ -389,12 +378,9 @@ class _CacheInterceptor extends Interceptor {
List<int> bytes = utf8.encode(jsonEncode(response.data));
Uint8List uint8List = Uint8List.fromList(bytes);
// 创建一个缓存文件并将数据写入其中
DefaultCacheManager().putFile(
cacheKey,
uint8List,
DefaultCacheManager().putFile(cacheKey, uint8List,
fileExtension: '.json', // 可以根据需求修改文件扩展名
maxAge: const Duration(hours: 12)
);
maxAge: const Duration(hours: 12));
} catch (e) {
Console.log('Error caching response: $e');
}
......@@ -402,5 +388,3 @@ class _CacheInterceptor extends Interceptor {
super.onResponse(response, handler);
}
}
part of utils;
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) {
final result = await [Permission.photos].request();
return result[Permission.photos] == PermissionStatus.granted ||
......@@ -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();
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();
......@@ -36,5 +191,4 @@ abstract class Access {
final result = await [Permission.microphone].request();
return result[Permission.microphone] == PermissionStatus.granted;
}
}
......@@ -4,22 +4,47 @@ abstract class AssetsPicker {
static final ImagePicker _imagePicker = ImagePicker();
/// 获取图库
/// [purpose] 权限使用目的说明,用于在申请权限前告知用户
static Future<XFile?> image({
required BuildContext context,
ImageSource source = ImageSource.gallery,
double maxWidth = 1024,
double maxHeight = 1024,
String purpose = '用于从相册中选择图片,用于设置头像、上传图片等功能。',
}) async {
if (!(await Access.photos())) {
// 先检查权限状态
bool hasPermission = false;
if (Platform.isIOS) {
final status = await Permission.photos.status;
hasPermission = status == PermissionStatus.granted || status == PermissionStatus.limited;
} else if (Platform.isAndroid) {
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) {
// CustomDialog.showAccess(
// context: context,
// content: const Text('获取相册权限'),
// );
_showSettingDialog(context,'获取相册权限');
final status = Platform.isIOS
? await Permission.photos.status
: await Permission.storage.status;
if (status.isPermanentlyDenied) {
_showSettingDialog(context, '获取相册权限');
}
}
return null;
}
}
return _imagePicker.pickImage(
source: source,
// maxWidth: maxWidth,
......@@ -27,23 +52,41 @@ abstract class AssetsPicker {
// imageQuality: 100
);
}
/// 拍照
/// [purpose] 权限使用目的说明,用于在申请权限前告知用户
static Future<XFile?> camera({
required BuildContext context,
ImageSource source = ImageSource.camera,
double maxWidth = 512,
double maxHeight = 512,
String purpose = '用于使用相机拍照,用于设置头像、上传图片等功能。',
}) async {
if (!(await Access.photos())) {
// 先检查权限状态
final cameraStatus = await Permission.camera.status;
bool hasPermission = cameraStatus == PermissionStatus.granted;
// 如果没有相机权限,先显示说明对话框,然后申请权限
if (!hasPermission) {
final granted = await Access.camera(
showExplanation: true,
purpose: purpose,
context: context,
);
if (!granted) {
// 用户拒绝或取消,检查是否需要跳转设置
if (context.mounted) {
// CustomDialog.showAccess(
// context: context,
// content: const Text('获取拍照权限'),
// );
_showSettingDialog(context,'获取拍照权限');
final status = await Permission.camera.status;
if (status.isPermanentlyDenied) {
_showSettingDialog(context, '获取相机权限');
}
}
return null;
}
}
return _imagePicker.pickImage(
source: source,
// maxWidth: maxWidth,
......
part of utils;
// 服务器地址
// const String kServerUrl = 'http://192.168.11.88:81';
// const String kServerUrl = 'http://8.141.148.247:7421';
const String kServerUrl = 'https://ebook-app.ezijing.com';
const String kHtmlBaseServer = 'http://150.158.138.40:9200';
const String kServerUrl = 'https://project-api.ezijing.com/api/book';
// const String kServerUrl = 'http://172.16.51.175:7421';
const String kHtmlBaseServer = 'https://webapp-pub.ezijing.com/book-app';
const String kLocalToken = 'local_token';
const String kLocalAccessToken = 'local_access_token';
const String kLocalAccount = 'local_account';
const String kLocalPassword = 'local_password';
const String kLocalUserInfo = 'local_user_info';
const String kLocalPrivacyNoticeShown = 'local_privacy_notice_shown';
const String kSearchHistory = 'search_history';
const String kFailOrder = 'failOrder';
const String kNoteTable = 'members_book_notes';
const String kReadTable = 'read_history';
const String kUserAgreement = '/html/agreement/ser_agreement.html';
const String kUserPriAgreement = '/html/agreement/pri_agreement.html';
const String kUserRechargeAgreement = '/html/agreement/rec_agreement.html';
const String kUserAgreement = '$kHtmlBaseServer/agreement/ser_agreement.html';
const String kUserPriAgreement =
'$kHtmlBaseServer/agreement/pri_agreement.html';
const String kUserRechargeAgreement =
'$kHtmlBaseServer/agreement/rec_agreement.html';
// 错题详情页 html
const String kUserWrongDes = '$kHtmlBaseServer/evaluating_wrong.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 kAnswer = '$kHtmlBaseServer/evaluating.html';
......@@ -33,18 +36,17 @@ const String kReadInfo = '$kHtmlBaseServer/read_info.html';
// 阅读页 图片预览
const String kScaleImage = '$kHtmlBaseServer/read_img.html';
abstract class C {
static const String localAccount = 'storage_account';
static const String localThemeMode = 'storage_theme_mode';
static const String localLanguage = 'storage_lang';
/// 自动主题
static const String themeSystem = '0';
/// 亮色主题
static const String themeLight = '1';
/// 暗色主题
static const String themeDark = '2';
}
......@@ -49,14 +49,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
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:
dependency: "direct main"
description:
......@@ -262,14 +254,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
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:
dependency: "direct main"
description:
......@@ -549,14 +533,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
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:
dependency: transitive
description:
......@@ -837,14 +813,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
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:
dependency: transitive
description:
......@@ -869,22 +837,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
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:
dependency: "direct main"
description:
......
name: flutter_book
description: 紫荆数智学堂
description: 清控紫荆数智学堂
# The following line prevents the package from being accidentally published to
# 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
......@@ -39,7 +39,6 @@ dependencies:
flutter_localizations:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
......@@ -53,11 +52,8 @@ dependencies:
shared_preferences: 2.1.1
# 屏幕适配
flutter_screenutil: 5.8.2
pull_to_refresh_flutter3: 2.0.1
# 加载图片
extended_image: 8.0.2
# 图标库
ionicons: 0.2.2
# 网络
dio: 5.3.3
crypto: 3.0.3
......@@ -97,8 +93,6 @@ dependencies:
package_info_plus: ^4.2.0
# 版本更新
flutter_app_update: ^3.0.4
# 能指定滑动位置的listView
azlistview: ^2.0.0
# 支付宝
tobias: ^3.3.0
# 内购
......@@ -120,12 +114,8 @@ dependencies:
# 安卓
android_id: ^0.3.6
# 防止截屏
# secure_application: ^3.8.0
screen_protector: ^1.4.2
# sentry_flutter: ^7.19.0
flutter_bugly: ^0.4.4
# secure_application: ^3.8.0
# sentry_flutter: ^7.19.0
dev_dependencies:
flutter_test:
......@@ -143,7 +133,6 @@ dev_dependencies:
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论