提交 77bf0eee authored 作者: maodou's avatar maodou

Merge remote-tracking branch 'origin/test' into test

# Conflicts: # lib/pages/user_info/view.dart # lib/pages/user_order/view.dart # lib/routes/routes.dart
......@@ -27,6 +27,8 @@ PODS:
- WechatOpenSDK-XCFramework (~> 2.0.2)
- image_picker_ios (0.0.1):
- Flutter
- just_audio (0.0.1):
- Flutter
- OrderedSet (5.0.0)
- package_info_plus (0.4.5):
- Flutter
......@@ -55,6 +57,7 @@ DEPENDENCIES:
- flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
- fluwx (from `.symlinks/plugins/fluwx/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- just_audio (from `.symlinks/plugins/just_audio/ios`)
- 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`)
......@@ -86,6 +89,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/fluwx/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
just_audio:
:path: ".symlinks/plugins/just_audio/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
......@@ -108,6 +113,7 @@ SPEC CHECKSUMS:
flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d
fluwx: 3c7b6df42f83d444d4538f3eaeae079f12d30c37
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
......
......@@ -165,6 +165,14 @@ abstract class CommonAPI {
return result.data['filesUrl'];
}
/// 11、获取OSS临时token
static Future <OssModel> oss() async {
final result = await HttpService.to.post(
'/v1/common/Alioss/getUploadToken',
);
if (result.data is! Map ) return OssModel();
return OssModel.fromJson(result.data);
}
}
\ No newline at end of file
......@@ -276,7 +276,7 @@ abstract class LibraryAPI {
String types = '3',
required String content,
required String isOpen,
String color= '',
String color= '#FF0000',
required String positioning,
required String noteContent,
}) async {
......@@ -288,7 +288,7 @@ abstract class LibraryAPI {
'types':types,
'content':content,
'is_open':isOpen,
'color':content,
'color':color,
'positioning':positioning,
'note_content':noteContent
......
......@@ -215,7 +215,7 @@ abstract class MineAPI {
});
}
/// 13、笔记详情列表
/// 13、讨论详情列表
///
static Future<List<DiscussModel>> discussList(
{int page = 1,
......@@ -228,7 +228,7 @@ abstract class MineAPI {
'page': page,
'page_size': limit,
'book_id': bookId,
'types': types
'type': types
},
);
if (result.data is! Map && result.data['list'] is! List) return [];
......@@ -413,5 +413,52 @@ abstract class MineAPI {
return false;
}
/// 25、删除笔记、高亮、划线的内容
static Future<bool> delNotes({
required String notesId,
required String bookId,
}) async {
final result = await HttpService.to.post(
'/v1/book/Information/delNotes',
params: {
'notes_id': notesId,
'book_id':bookId
},
);
if (result.data is Map && result.data['is_success'] == 1) {
return true;
}
return false;
}
/// 25、修改笔记、高亮、划线的内容
static Future<bool> editNotes({
required String content,
required String notesId,
required String bookId,
required String noteContent,
String isOpen = '0',
String positioning = '',
String color= '#FF0000',
}) async {
final result = await HttpService.to.post(
'/v1/book/Information/editNotes',
params: {
'notes_id': notesId,
'book_id':bookId,
'note_content':noteContent,
'positioning':positioning,
'is_open':isOpen,
'color': color,
'content':content
},
showLoading: true
);
if (result.data is Map && result.data['is_success'] == 1) {
return true;
}
return false;
}
}
......@@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_book/routes/index.dart';
import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
......@@ -21,6 +22,10 @@ void main() {
Future.wait([
UserStore.to.profile(),
]).whenComplete(() {
// String name = EncryptUtil.aesEncrypt('我是谁');
// print('2222222---------$name');
// final result = EncryptUtil.aesDecrypt(name);
// Console.log('解密--------------------------$result');
runApp(const MyApp());
//FlutterNativeSplash.remove();
});
......
......@@ -2,7 +2,7 @@ library models;
import 'package:just_audio/just_audio.dart' as just_audio;
part 'response.dart';
part 'course.dart';
......
......@@ -494,4 +494,41 @@ class BookDownloadModel {
}
class OssModel {
OssModel({
this.securityToken,
this.accessKeyId,
this.accessKeySecret,
this.expiration,});
OssModel.fromJson(dynamic json) {
securityToken = json['SecurityToken'];
accessKeyId = json['AccessKeyId'];
accessKeySecret = json['AccessKeySecret'];
expiration = json['Expiration'];
}
String? securityToken;
String? accessKeyId;
String? accessKeySecret;
String? expiration;
OssModel copyWith({ String? securityToken,
String? accessKeyId,
String? accessKeySecret,
String? expiration,
}) => OssModel( securityToken: securityToken ?? this.securityToken,
accessKeyId: accessKeyId ?? this.accessKeyId,
accessKeySecret: accessKeySecret ?? this.accessKeySecret,
expiration: expiration ?? this.expiration,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['SecurityToken'] = securityToken;
map['AccessKeyId'] = accessKeyId;
map['AccessKeySecret'] = accessKeySecret;
map['Expiration'] = expiration;
return map;
}
}
......@@ -338,6 +338,7 @@ class UserInfoModel {
/// 笔记详情模型
class NoteModel {
NoteModel({
this.notesId,
this.types,
this.chapterId,
this.content,
......@@ -348,33 +349,38 @@ class NoteModel {
});
NoteModel.fromJson(dynamic json) {
notesId = json['notes_id'];
types = json['types'];
chapterId = json['chapter_id'];
content = json['content'];
positioning = json['positioning'];
noteContent = json['note_content'];
// noteContent = json['note_content'];
noteContent = json['note_content'] != null ? NoteContentModel.fromJson(json['note_content']) : null;
color = json['color'];
chapterName = json['chapter_name'];
}
num? notesId;
num? types;
num? chapterId;
String? content;
String? positioning;
String? noteContent;
// String? noteContent;
NoteContentModel? noteContent;
String? color;
String? chapterName;
NoteModel copyWith({
num? notesId,
num? types,
num? chapterId,
String? content,
String? positioning,
String? noteContent,
NoteContentModel? noteContent,
String? color,
String? chapterName,
}) =>
NoteModel(
notesId: notesId ?? this.notesId,
types: types ?? this.types,
chapterId: chapterId ?? this.chapterId,
content: content ?? this.content,
......@@ -386,17 +392,114 @@ class NoteModel {
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['notes_id'] = notesId;
map['types'] = types;
map['chapter_id'] = chapterId;
map['content'] = content;
map['positioning'] = positioning;
map['note_content'] = noteContent;
if (noteContent != null) {
map['note_content'] = noteContent?.toJson();
}
map['color'] = color;
map['chapter_name'] = chapterName;
return map;
}
}
class MediaModel {
MediaModel({
this.privacyStatus,
this.content,
this.id,
this.duration = '',
this.path = '',
this.currentDuration = '0:00:00'
});
MediaModel.fromJson(dynamic json) {
privacyStatus = json['privacy_status'];
content = json['content'];
id = json['id'];
duration = '';
path = '';
currentDuration = '0:00:00';
}
num? privacyStatus;
String? content;
late String path;
num? id;
late String duration;
late String currentDuration;
MediaModel copyWith({
num? privacyStatus,
String? content,
num? id,
String path = '',
}) => MediaModel(
privacyStatus: privacyStatus ?? this.privacyStatus,
content: content ?? this.content,
id: id ?? this.id,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['privacy_status'] = privacyStatus;
map['content'] = content;
map['id'] = id;
return map;
}
}
class NoteContentModel {
NoteContentModel({
this.text,
this.audio,
this.image,});
NoteContentModel.fromJson(dynamic json) {
text = json['text'] != null ? MediaModel.fromJson(json['text']) : null;
if (json['audio'] != null) {
audio = [];
json['audio'].forEach((v) {
audio?.add(MediaModel.fromJson(v));
});
}
if (json['image'] != null) {
image = [];
json['image'].forEach((v) {
image?.add(MediaModel.fromJson(v));
});
}
}
MediaModel? text;
List<MediaModel>? audio;
List<MediaModel>? image;
NoteContentModel copyWith({ MediaModel? text,
List<MediaModel>? audio,
List<MediaModel>? image,
}) => NoteContentModel( text: text ?? this.text,
audio: audio ?? this.audio,
image: image ?? this.image,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
if (text != null) {
map['text'] = text?.toJson();
}
if (audio != null) {
map['audio'] = audio?.map((v) => v.toJson()).toList();
}
if (image != null) {
map['image'] = image?.map((v) => v.toJson()).toList();
}
return map;
}
}
/// 讨论模型
class DiscussModel {
DiscussModel({
......@@ -662,3 +765,4 @@ class CoinModel {
}
}
......@@ -4,8 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:go_router/go_router.dart';
import '../../models/index.dart';
import '../../routes/index.dart';
part 'view.dart';
part 'widgets/item.dart';
\ No newline at end of file
......@@ -3,9 +3,13 @@ part of book_category;
class BookCategoryPage extends StatefulWidget {
final List <ChapterModel> chapters;
final String bookId;
final BookDetailModel bookDetails;
const BookCategoryPage({
Key? key,
required this.chapters
required this.chapters,
required this.bookId,
required this.bookDetails
}) : super(key: key);
@override
......@@ -20,7 +24,7 @@ class _BookCategoryPageState extends State<BookCategoryPage> {
Expanded(
child: ListView.builder(
itemBuilder:(BuildContext context, int index){
return BuildItem(model: widget.chapters[index],);
return BuildItem(model: widget.chapters[index],bookId: widget.bookId,bookDetails: widget.bookDetails,);
},
itemCount: widget.chapters.length,
),
......
......@@ -2,9 +2,13 @@ part of book_category;
class BuildItem extends StatefulWidget {
final ChapterModel model;
final String bookId;
final BookDetailModel bookDetails;
const BuildItem({
Key? key,
required this.model
required this.model,
required this.bookId,
required this.bookDetails
}) : super(key: key);
@override
......@@ -19,9 +23,13 @@ class _BuildItemState extends State<BuildItem> {
/// 章节名称容器
GestureDetector(
onTap: (){
setState(() {
widget.model.selected = !widget.model.selected;
});
// 如果章下面没有节 点击才会跳转
if (widget.model.children!.isEmpty){
context.pushNamed(Routes.web,queryParameters: {'book_id': widget.bookDetails.bookId.toString(),'chapter_id': widget.model.id.toString(),'chapter_name':widget.model.name.toString()},extra: widget.bookDetails);
}
// setState(() {
// widget.model.selected = !widget.model.selected;
// });
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w),
......@@ -49,7 +57,19 @@ class _BuildItemState extends State<BuildItem> {
),
Transform.rotate(
angle: widget.model.selected?0:-90 * (3.141592653589793 / 180),
child: GestureDetector(
onTap: (){
setState(() {
widget.model.selected = !widget.model.selected;
});
},
child: Container(
width: 20,
height: 20,
// color: Colors.red,
child: Image.asset('assets/images/down.png')
),
)
)
],
......@@ -63,7 +83,13 @@ class _BuildItemState extends State<BuildItem> {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index){
return _buildSection(widget.model.children![index]);
ChapterModel model = widget.model.children![index];
return GestureDetector(
onTap: (){
context.pushNamed(Routes.web,queryParameters: {'book_id': widget.bookDetails.bookId.toString(),'chapter_id':model.id.toString(),'chapter_name':model.name.toString()},extra: widget.bookDetails);
},
child: _buildSection(model)
);
},
itemCount: widget.model.children!.length,
)
......
......@@ -20,7 +20,12 @@ class _BookDetailPageState extends State<BookDetailPage> with SingleTickerProvid
Widget build(BuildContext context) {
return GetBuilder<BookDetailController>(
init:BookDetailController(widget.bookId),
builder: (controller)=> Scaffold(
builder: (controller)=> WillPopScope(
onWillPop: () async {
context.pop(true);
return false;
},
child: Scaffold(
appBar: CustomAppBar(
backgroundColor: const Color(0xFFAB1941).withOpacity(0.02),
title: const Text('详情'),
......@@ -75,7 +80,7 @@ class _BookDetailPageState extends State<BookDetailPage> with SingleTickerProvid
child: TabBarView(
controller: controller.tabController,
children: [
BookCategoryPage(chapters: controller.chapters,),
BookCategoryPage(chapters: controller.chapters,bookId: controller.bookId,bookDetails: controller.bookDetails,),
Container(
padding: EdgeInsets.only(left: 15.w,right: 15.w,top:12.w),
color: Colors.white,
......@@ -128,7 +133,7 @@ class _BookDetailPageState extends State<BookDetailPage> with SingleTickerProvid
// 1免费 0 不免费
if(controller.bookDetails.isFree == 1){
context.pushNamed(Routes.web,queryParameters: {'book_id': controller.bookDetails.bookId.toString(),'chapter_id': controller.bookDetails.chapterId.toString(),'chapter_name':controller.bookDetails.chapterName.toString});
context.pushNamed(Routes.web,queryParameters: {'book_id': controller.bookDetails.bookId.toString(),'chapter_id': controller.bookDetails.chapterId.toString(),'chapter_name':controller.bookDetails.chapterName.toString()},extra: controller.bookDetails);
}
else {
// 没有购买
......@@ -154,11 +159,11 @@ class _BookDetailPageState extends State<BookDetailPage> with SingleTickerProvid
}
}
else{
context.pushNamed(Routes.web,queryParameters: {'book_id': controller.bookDetails.bookId.toString(),'chapter_id': controller.bookDetails.chapterId.toString(),'chapter_name':controller.bookDetails.chapterName.toString()});
context.pushNamed(Routes.web,queryParameters: {'book_id': controller.bookDetails.bookId.toString(),'chapter_id': controller.bookDetails.chapterId.toString(),'chapter_name':controller.bookDetails.chapterName.toString()},extra: controller.bookDetails);
}
}
else{
context.pushNamed(Routes.web,queryParameters: {'book_id': controller.bookDetails.bookId.toString(),'chapter_id': controller.bookDetails.chapterId.toString(),'chapter_name':controller.bookDetails.chapterName.toString()});
context.pushNamed(Routes.web,queryParameters: {'book_id': controller.bookDetails.bookId.toString(),'chapter_id': controller.bookDetails.chapterId.toString(),'chapter_name':controller.bookDetails.chapterName.toString()},extra: controller.bookDetails);
}
}
},
......@@ -179,6 +184,7 @@ class _BookDetailPageState extends State<BookDetailPage> with SingleTickerProvid
),
),
),
),
);
}
......
......@@ -45,6 +45,7 @@ class BookInfoPage extends StatelessWidget {
Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
......@@ -71,17 +72,24 @@ class BookInfoPage extends StatelessWidget {
height: 90,
// color: Colors.green,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: List.generate(model.ratingList!.length, (index){
return _buildProgrss(5- index.toDouble(), model.ratingList![index].toDouble()/model.ratingCount!);
}).toList()
),
Text('${model.ratingCount}个评分',style: TextStyle(fontSize: 9.w,height: 1.4,color: Colours.c9),)
],
),
),
),
)
],
),
)
),
],
),
Container(height: 1,width: double.infinity,color: Colours.cF2,),
......@@ -90,19 +98,26 @@ class BookInfoPage extends StatelessWidget {
Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: ['书名','作者','分类','出品方','出版社','上架时间'].map((item){
children: [
{'name':'书名','value':model.bookName},
{'name':'作者','value':model.authors},
{'name':'分类','value':model.categoryName},
{'name':'出品方','value':model.producersName},
{'name':'出版社','value':model.pressName},
{'name':'上架时间','value':Tools.dateFromMS(model.onsaleTime!.toInt(),pattern:'yyyy年MM月dd日' )},
].map((item){
return Row(
children: [
Container(
// color: Colors.cyan,
alignment: Alignment.centerRight,
width: 50,
child: Text(item,style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c3),),
child: Text(item['name'].toString(),style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c3),),
),
Gaps.hGaps20,
Container(
alignment: Alignment.centerLeft,
child: Text(item,style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c9),textAlign: TextAlign.end,),
child: Text(item['value'].toString(),style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c9),textAlign: TextAlign.end,),
),
],
);
......
......@@ -60,7 +60,10 @@ class BookPayController extends GetxController {
/// 支付方式 默认第一个
late PayModel _payModel = pays.first;
PayModel get payModel => _payModel;
PayModel get payModel {
_payModel.selected = true;
return _payModel;
}
/// 选择积分状态
void show(){
......
......@@ -113,7 +113,7 @@ class _ChangePwdPageState extends State<ChangePwdPage> {
),
Container(
margin: EdgeInsets.only(left: 5.w,top: 5.w),
child: Text('密码必须是6-20个英文字母、数字或符号',style: TextStyle(fontSize: 10.w,color: Colours.c9),),
child: Text('密码必须是8-20个英文字母、数字或符号(除空格)',style: TextStyle(fontSize: 10.w,color: Colours.c9),),
),
Gaps.vGaps40,
CustomGradientButton(
......
......@@ -86,15 +86,15 @@ class CourseController extends GetxController {
}
}
void logout(BuildContext context) async {
final result = await AccountAPI.logout();
if (result){
CustomToast.success('退出成功');
await UserStore.to.logout();
if(context.mounted){
context.goNamed(Routes.main);
}
}
}
// void logout(BuildContext context) async {
// final result = await AccountAPI.logout();
// if (result){
// CustomToast.success('退出成功');
// await UserStore.to.logout();
// if(context.mounted){
// context.goNamed(Routes.main);
// }
// }
// }
}
\ No newline at end of file
......@@ -42,7 +42,6 @@ class _CoursePageState extends State<CoursePage> {
if (result == true) {
controller.getNums();
}
// controller.logout(context);
},
child: badges.Badge(
position: badges.BadgePosition.topEnd(top: -5.w, end: 0),
......
......@@ -38,10 +38,10 @@ class BuildBanner extends StatelessWidget {
// ),
),
//CustomImage.network(item.pic??'',)
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: Image.network(item.pic??'',)
child: CustomImage.network(url: item.pic??'')
),
// child: CustomImage.asset(
// url: 'assets/images/banner.png',
......
......@@ -3,9 +3,12 @@ part of web;
class ReadController extends FullLifeCycleController with GetSingleTickerProviderStateMixin{
final String bookId;
final String chapterId;
final String chapterName;
ReadController({required this.bookId, required this.chapterId,required this.chapterName});
String chapterId;
String chapterName;
final BookDetailModel bookDetailModel;
ReadController({required this.bookId, required this.chapterId,required this.chapterName,required this.bookDetailModel});
late InAppWebViewController webViewController;
// 目录
List <ChapterModel> chapters = [];
......@@ -37,27 +40,49 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 讨论添加的图片path数组
List <String> discussInputImages= [];
// 讨论添加的语音path数组
List <String> discussInputAudios= [];
List <AudioModel> discussInputAudios= [];
// 笔记标题
String noteTitle = '';
bool _show = true;
bool get show => _show;
// 录音
final FlutterSoundRecorder _mRecorder = FlutterSoundRecorder();
// 录音开始
bool startRecording = false;
///------------------------------------------ 页面 生命周期--------------------------------------------------------
@override
void onInit() {
discussTitleFocusNode.addListener(_onCommentFocusChanged);
openTheRecorder();
super.onInit();
}
@override
void onReady() {
void onReady() async {
// 上报开始阅读时间
_addReadTime(type: 'open');
_getChapters();
final netStatus = await _checkCurrentNetStatus();
// 判断是否有离线文件 如果有使用离线阅读
final exist = await _isExistFile(bookId);
// 没有网并且有离线文件 离线阅读
if (netStatus && exist){
// webViewController.loadUrl(urlRequest: Uri.parse(urlRequest))
}
String path = await _getDirectory();
String finalPath = '$path/175/333.html';
final content = await readHtmlFileContent(finalPath);
// EncryptUtil.aesDecrypt(content);
Console.log('原始内容-----------------$content');
Console.log('解密-----------------${EncryptUtil.aesDecrypt(content!)}');
super.onReady();
}
......@@ -78,6 +103,97 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
update();
}
// 选择了某个章节
void selectChapter(ChapterModel model) {
chapterName = model.name??'';
chapterId = model.id.toString();
update();
}
// 初始化录音组件
Future<void> openTheRecorder() async {
// 获取权限
if(await Access.microphone()){
await _mRecorder.openRecorder();
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration(
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
avAudioSessionCategoryOptions:
AVAudioSessionCategoryOptions.allowBluetooth |
AVAudioSessionCategoryOptions.defaultToSpeaker,
avAudioSessionMode: AVAudioSessionMode.spokenAudio,
avAudioSessionRouteSharingPolicy:
AVAudioSessionRouteSharingPolicy.defaultPolicy,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
androidAudioAttributes: const AndroidAudioAttributes(
contentType: AndroidAudioContentType.speech,
flags: AndroidAudioFlags.none,
usage: AndroidAudioUsage.voiceCommunication,
),
androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
androidWillPauseWhenDucked: true,
));
}
// 没有权限
else {
}
}
// 开启录音
void record() async {
startRecording = true;
String filePath = await Tools.getDirectory();
String fileName = Tools.generateVoiceFileName();
_mRecorder.startRecorder(
toFile: '$filePath/$fileName',
audioSource: AudioSource.microphone,
codec: Codec.aacMP4,
);
// _mRecorder?.setSubscriptionDuration(Duration(milliseconds: 100));
// _mRecorder?.onProgress?.listen((e)
// {
// var date = DateTime.fromMillisecondsSinceEpoch(e.duration.inMilliseconds,isUtc: true);
// var text = DateFormat('mm:ss:').format(date);
// print('--------------$text');
//
// });
update();
}
// 停止录音
void stopRecorder() async{
just_audio.AudioPlayer audioPlayer = just_audio.AudioPlayer();
startRecording = false;
final path = await _mRecorder.stopRecorder();
var duration = await audioPlayer.setFilePath(path!);
print('-----duration---------------------$duration------');
AudioModel audioModel = AudioModel(path: path,duration: Tools.formatDuration(duration!));
discussInputAudios.add(audioModel);
update();
}
//
String formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return '${duration.inHours}:$twoDigitMinutes:${twoDigitSeconds}';
}
// 语音文件名称
String generateVoiceFileName(){
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyyMMddHHmmss').format(now);
return 'voice_$formattedDate.mp4';
}
// 添加语音路径
void addVoiceFilePath(){
}
// 添加讨论图片
void addDiscussInputImages(String path){
discussInputImages.add(path);
......@@ -117,8 +233,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
Future<String> upload({
required String path
}) async {
String result = await CommonAPI.upload(path:path,fileTypes: 'comment');
return result;
// String result = await CommonAPI.upload(path:path,fileTypes: 'comment');
// return result;
OssTool tool = OssTool('zxts-comment-file');
final response = await tool.putObjectFile(path);
print('------response--------------------------${response.realUri}');
return response.realUri.toString();
}
// 提交
......@@ -135,8 +255,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
// 循环上传音频获取地址
for(String path in discussInputAudios){
final url = await upload(path: path);
for(AudioModel model in discussInputAudios){
final url = await upload(path: model.path);
audios.add(url);
}
......@@ -148,41 +268,19 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 话题
if (chatType == 0){
final result = await addDiscuss();
final result = await addDiscuss(contentMap);
return result;
}
// 笔记
else if (chatType == 1){
final result = addNote();
final result = addNote(contentMap);
return result;
}
return false;
}
// 添加笔记
Future<bool> addNote() async {
// 音频链接数组
List<String> audios = [];
// 图片链接数组
List<String> images = [];
// 循环上传图片获取地址
for(String path in discussInputImages){
final url = await upload(path: path);
images.add(url);
}
// 循环上传音频获取地址
for(String path in discussInputAudios){
final url = await upload(path: path);
audios.add(url);
}
Map<String,dynamic> contentMap = {
'text':contentInput.text,
'audio':audios,
'image':images
};
Future<bool> addNote(Map<String,dynamic> contentMap) async {
final result = await LibraryAPI.addNote(
bookId: bookId,
......@@ -192,35 +290,17 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
positioning: notePosition,
noteContent: jsonEncode(contentMap)
);
titleInput.text = '';
contentInput.text = '';
setShowChat(false);
return result;
}
// 发表评论
// {String commentId ='0',String quoteContent ='',String title = ''}
Future<bool> addDiscuss() async{
// 音频链接数组
List<String> audios = [];
// 图片链接数组
List<String> images = [];
// 循环上传图片获取地址
for(String path in discussInputImages){
final url = await upload(path: path);
images.add(url);
}
// 循环上传音频获取地址
for(String path in discussInputAudios){
final url = await upload(path: path);
audios.add(url);
}
Map<String,dynamic> contentMap = {
'text':contentInput.text,
'audio':audios,
'image':images
};
Future<bool> addDiscuss(Map<String,dynamic> contentMap) async{
final result = await LibraryAPI.addDiscuss(
bookId: bookId,
......@@ -306,23 +386,116 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
/// 获取离线文件路径
void getBookDown() async{
final exit = await _isExistFile(bookId);
// 存在离线文件
if (exit){
}
else{
final result = await LibraryAPI.getbookDownloadParam(bookId: bookId);
Console.log('----------_getBookDown------------------${result.download}');
final String savePath = await _getDocumentsDirectory();
LibraryAPI.downBookByUrl(url: result.download!,savePath: '$savePath$bookId.zip',onReceiveProgress: (int received,int total){
if (total !=-1){
double progress = (received / total) * 100;
Console.log('Download progress: $progress%');
// final String savePath = await _getDirectory();
// LibraryAPI.downBookByUrl(url: result.download!,savePath: '$savePath$bookId.zip',onReceiveProgress: (int received,int total){
// if (total !=-1){
// double progress = (received / total) * 100;
// Console.log('Download progress: $progress%');
// }
// });
extractZipFileFromCache(result.download!);
}
}
// 下载文件
Future<void> extractZipFileFromCache(String url) async {
// 从缓存中获取 ZIP 文件
var file = await DefaultCacheManager().getSingleFile(url);
if (file != null) {
// 读取 ZIP 文件内容
Uint8List bytes = await file.readAsBytes();
// 解压缩 ZIP 文件
Archive archive = ZipDecoder().decodeBytes(bytes);
// 获取设备上的临时目录
Directory? tempDir = await getExternalStorageDirectory();
// 将解压缩后的文件保存到临时目录中
for (var file in archive) {
if (file.isFile) {
String fileName = file.name;
String filePath = '${tempDir!.path}/$bookId/$fileName';
File(filePath)
..createSync(recursive: true)
..writeAsBytesSync(file.content as List<int>);
print('解压缩文件:$fileName,保存路径:$filePath');
}
}
} else {
print('未找到缓存中的文件或文件不存在');
}
}
// 判断当前网络状态
Future<bool> _checkCurrentNetStatus() async {
final connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none){
return false;
}
return true;
// if (connectivityResult == ConnectivityResult.mobile) {
// // I am connected to a mobile network.
// } else if (connectivityResult == ConnectivityResult.wifi) {
// // I am connected to a wifi network.
// } else if (connectivityResult == ConnectivityResult.ethernet) {
// // I am connected to a ethernet network.
// } else if (connectivityResult == ConnectivityResult.vpn) {
// // I am connected to a vpn network.
// // Note for iOS and macOS:
// // There is no separate network interface type for [vpn].
// // It returns [other] on any device (also simulator)
// } else if (connectivityResult == ConnectivityResult.bluetooth) {
// // I am connected to a bluetooth.
// } else if (connectivityResult == ConnectivityResult.other) {
// // I am connected to a network which is not in the above mentioned networks.
// } else if (connectivityResult == ConnectivityResult.none) {
// // I am not connected to any network.
// }
}
// 判断是否存在离线文件
Future<bool> _isExistFile(String bookId) async {
String directoryPath = await _getDirectory();
Directory directory = Directory(directoryPath);
bool directoryExists = await directory.exists();
if (directoryExists) {
print('存在名为 "$bookId" 的文件夹');
return await Directory('${directory.path}/$bookId').exists();
}
print('不存在名为 "$bookId" 的文件夹');
return false;
}
// 读取html内容
Future<String?> readHtmlFileContent(String filePath) async {
try {
File htmlFile = File(filePath);
String fileContent = await htmlFile.readAsString();
return fileContent;
}
catch (e){
print('Error reading file: $e');
return '';
}
});
}
//获取存储目录
Future<String> _getDocumentsDirectory() async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
Future<String> _getDirectory() async {
// getTemporaryDirectory
final directory = await getExternalStorageDirectory();
return directory!.path;
}
///------------------------------------------ app 生命周期--------------------------------------------------------
......@@ -367,3 +540,9 @@ class ToolModel {
String icon;
String activeIcon;
}
class AudioModel {
AudioModel({required this.path, required this.duration});
String path;
String duration;
}
part of web;
class DiscussController extends GetxController {
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
DiscussController({required this.bookDetailModel,required this.chapterId});
List<DiscussModel> discuss = [];
final EasyRefreshController refreshController = EasyRefreshController(
controlFinishLoad: true,
......@@ -32,8 +36,8 @@ class DiscussController extends GetxController {
final result = await LibraryAPI.discussList(
page: _page,
limit: _limit,
bookId: '110',
chapterId: '1'
bookId: bookDetailModel.bookId.toString(),
chapterId: chapterId
);
// 如果是刷新 清理数据
if (isRefresh) discuss.clear();
......
......@@ -2,17 +2,29 @@ library web;
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:archive/archive.dart';
import 'package:audio_session/audio_session.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_sound/public/flutter_sound_recorder.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_platform_interface.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart';
import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:just_audio/just_audio.dart' as just_audio;
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import '../../apis/index.dart';
import '../../models/index.dart';
......@@ -32,3 +44,4 @@ part 'widgets/book.dart';
part 'note_controller.dart';
part 'discuss_controller.dart';
part 'widgets/input_discuss.dart';
part 'widgets/item.dart';
\ No newline at end of file
part of web;
class NoteController extends GetxController {
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
NoteController({required this.bookDetailModel,required this.chapterId});
List<NoteModel> notes = [];
final EasyRefreshController refreshController = EasyRefreshController(
......@@ -34,8 +37,8 @@ class NoteController extends GetxController {
final result = await LibraryAPI.noteList(
page: _page,
limit: _limit,
bookId: '110',
chapterId: '1'
bookId: bookDetailModel.bookId.toString(),
chapterId: chapterId
);
// 如果是刷新 清理数据
if (isRefresh) notes.clear();
......
......@@ -4,11 +4,13 @@ class ReadPage extends StatefulWidget {
final String bookId;
final String chapterId;
final String chapterName;
final BookDetailModel bookDetailModel;
const ReadPage({
Key? key,
required this.bookId,
required this.chapterId,
required this.chapterName,
required this.bookDetailModel
}) : super(key: key);
@override
......@@ -24,10 +26,10 @@ class _ReadPageState extends State<ReadPage> {
@override
Widget build(BuildContext context) {
return GetBuilder<ReadController>(
init: ReadController(bookId: widget.bookId, chapterId: widget.chapterId,chapterName: widget.chapterName),
init: ReadController(bookId: widget.bookId, chapterId: widget.chapterId,chapterName: widget.chapterName,bookDetailModel: widget.bookDetailModel),
builder: (readController) => Scaffold(
appBar: CustomAppBar(
title: Text(widget.chapterName),
title: Text(readController.chapterName),
centerTitle: false,
actions: [
GestureDetector(
......@@ -42,7 +44,7 @@ class _ReadPageState extends State<ReadPage> {
],
),
resizeToAvoidBottomInset: false,
floatingActionButton: readController.show?GestureDetector(
floatingActionButton: readController.show&& !readController.toolModel.selected?GestureDetector(
onTap: (){
readController.setShowChat(true);
readController.noteTitle = '你好你问你你等您第五年对哦in我ID呢哦win地哦为内地那打卡你打困哪';
......@@ -56,25 +58,33 @@ class _ReadPageState extends State<ReadPage> {
color: Colors.white,
child: Stack(
children: [
Container(
height: 40,
width: double.infinity,
color: Colors.lightBlue,
),
// Container(
// height: 40,
// width: double.infinity,
// color: Colors.lightBlue,
// ),
InAppWebView(
initialUrlRequest: URLRequest(
url: Uri.parse('http://192.168.11.46:9200/read.html'),
url: Uri.parse('http://150.158.138.40:9200/read.html'),
),
contextMenu: ContextMenu(
options: ContextMenuOptions(hideDefaultSystemContextMenuItems: true),
),
onWebViewCreated: (InAppWebViewController controller){
readController.webViewController = controller;
},
onConsoleMessage: (controller, consoleMessage) {
// 接收从 WebView 发送的消息
print("Received message from WebView: ${consoleMessage.message}");
},
onLoadStop: (controller, url) {
// flutter 主动给 js 传参数
Map<String, dynamic> param = {
'book_id': 110,
'chapter_id': 1,
'book_id': readController.bookId,
'chapter_id': readController.chapterId,
'token':UserStore.to.token
};
Console.log('param--------------------------------$param');
controller.evaluateJavascript(source: 'callbackInFlutterComponent("$param");');
// 添加单击事件
......@@ -232,57 +242,56 @@ class _ReadPageState extends State<ReadPage> {
);
}
/// 目录、评论、笔记
Widget _showContent(ReadController controller,ToolModel model) {
Console.log('++++++++++++++++++++++++${model.tag}');
if (controller.show){
if (model.tag == 0){
return model.selected? Container(
color: const Color(0xFF000000).withOpacity(0.5),
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)),
child: Container(
color: Colors.white,
child: ReadCategoryPage(controller: controller,onTap: (){
Widget detail(ReadController controller,ToolModel model){
if(model.tag == 0){
return ReadCategoryPage(controller: controller,
onTap: (){
controller.chooseTool(model);
},),
),
),
// child: ReadCategoryPage(),
):const SizedBox();
},
onTapChapter: (ChapterModel chapterModel){
print('-----------选择的章节-------------${chapterModel.name}--------');
// 配置选择的章节
controller.selectChapter(chapterModel);
// 取消选中 tool
controller.chooseTool(model);
// 选择了新的章节 刷新 webview
controller.webViewController.reload();
},
);
}
else if (model.tag == 1){
return model.selected? Container(
color: const Color(0xFF000000).withOpacity(0.5),
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)),
child: Container(
color: Colors.white,
child: ReadNotePage(onTap: (){
else if(model.tag == 1){
return ReadNotePage(onTap: (){
controller.chooseTool(model);
},),
),
),
// child: ReadCategoryPage(),
):const SizedBox();
},bookDetailModel: controller.bookDetailModel,chapterId: controller.chapterId,);
}
else if (model.tag == 2){
return model.selected? Container(
else if(model.tag == 2){
return ReadDiscussPage(onTap: (){
controller.chooseTool(model);
},bookDetailModel: controller.bookDetailModel,chapterId:controller.chapterId,);
}
return const SizedBox();
}
/// 目录、评论、笔记 背景
Widget _showContent(ReadController controller,ToolModel model) {
Console.log('++++++++++++++++++++++++${model.tag}');
if (controller.show){
if(model.selected){
return Container(
color: const Color(0xFF000000).withOpacity(0.5),
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)),
child: Container(
color: Colors.white,
child: ReadDiscussPage(onTap: (){
controller.chooseTool(model);
},),
child: detail(controller, model)
),
),
// child: ReadCategoryPage(),
):const SizedBox();
);
}
else{
return const SizedBox();
}
}
return const SizedBox();
......
part of web;
class BuildBook extends StatelessWidget {
const BuildBook({Key? key}) : super(key: key);
final BookDetailModel bookDetailModel;
const BuildBook({
Key? key,
required this.bookDetailModel
}) : super(key: key);
@override
Widget build(BuildContext context) {
......@@ -18,7 +22,7 @@ class BuildBook extends StatelessWidget {
CustomCard(
width: 72.w,
height: 86.w,
url: '',
url: bookDetailModel.img??'',
),
Container(
height: 87.w,
......@@ -31,8 +35,8 @@ class BuildBook extends StatelessWidget {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('书名',style: TextStyle(fontSize: 14.w,height: 1.5,fontWeight: Fonts.medium,color: Colours.c3),),
Text('作者',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c6),),
Text(bookDetailModel.bookName??'',style: TextStyle(fontSize: 14.w,height: 1.5,fontWeight: Fonts.medium,color: Colours.c3),),
Text(bookDetailModel.authors??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c6),),
],
),
],
......
......@@ -3,10 +3,12 @@ part of web;
class ReadCategoryPage extends StatefulWidget {
final ReadController controller;
final void Function()? onTap;
final Function(ChapterModel chapterModel) onTapChapter;
const ReadCategoryPage({
Key? key,
required this.controller,
required this.onTap,
required this.onTapChapter
}) : super(key: key);
@override
......@@ -60,15 +62,18 @@ class _ReadCategoryPageState extends State<ReadCategoryPage> {
),
),
),
BuildBook(),
// Expanded(
// child: ListView.builder(
// itemBuilder:(BuildContext context, int index){
// return BuildItem(model: widget.controller.chapters[index],);
// },
// itemCount: widget.controller.chapters.length,
// ),
// ),
BuildBook(bookDetailModel: widget.controller.bookDetailModel,),
Expanded(
child: ListView.builder(
itemBuilder:(BuildContext context, int index){
return BuildItem(model: widget.controller.chapters[index],onTapChapter:(ChapterModel chapterModel){
widget.onTapChapter(chapterModel);
widget.onTap;
},);
},
itemCount: widget.controller.chapters.length,
),
),
],
),
);
......
......@@ -2,9 +2,14 @@ part of web;
class ReadDiscussPage extends StatefulWidget {
final void Function()? onTap;
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
const ReadDiscussPage({
Key? key,
required this.onTap,
required this.bookDetailModel,
required this.chapterId
}) : super(key: key);
@override
......@@ -16,7 +21,7 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> {
@override
Widget build(BuildContext context) {
return GetBuilder<DiscussController>(
init: DiscussController(),
init: DiscussController(bookDetailModel: widget.bookDetailModel,chapterId: widget.chapterId),
builder:(controller) => Scaffold(
resizeToAvoidBottomInset: false,
body: Column(
......@@ -58,7 +63,7 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> {
),
),
),
BuildBook(),
BuildBook(bookDetailModel: widget.bookDetailModel,),
Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context,int index){
......
......@@ -106,7 +106,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
onTap: (){
widget.controller.delDiscussInputImages(widget.controller.discussInputImages[index]);
},
child: Image.asset('assets/images/del_close.png',width: 12.w,height: 12.w,)
child: Image.asset('assets/images/media_del.png',width: 12.w,height: 12.w,)
)
)
],
......@@ -145,7 +145,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
),
);
},
itemCount: 0,
itemCount: widget.controller.discussInputAudios.length,
),
),
)
......@@ -174,9 +174,15 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
Gaps.hGaps10,
GestureDetector(
onTap: () async {
if(widget.controller.startRecording){
widget.controller.stopRecorder();
}
else {
widget.controller.record();
}
},
child: Image.asset('assets/images/read_add_audio.png')
child: Image.asset(widget.controller.startRecording?'assets/images/stop.png':'assets/images/read_add_audio.png')
),
widget.controller.chatType ==0?const SizedBox():GestureDetector(
onTap: (){
......
part of web;
class BuildItem extends StatefulWidget {
final ChapterModel model;
final Function(ChapterModel chapterModel) onTapChapter;
const BuildItem({
Key? key,
required this.model,
required this.onTapChapter
}) : super(key: key);
@override
State<BuildItem> createState() => _BuildItemState();
}
class _BuildItemState extends State<BuildItem> {
@override
Widget build(BuildContext context) {
return Column(
children: [
/// 章节名称容器
GestureDetector(
onTap: (){
if(widget.model.children!.isEmpty){
widget.onTapChapter(widget.model);
}
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w),
height: 30.w,
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(widget.model.name??'',style: TextStyle(fontSize: 14.w,color: widget.model.seen ==0? Colours.c3:Colours.c9,fontWeight: Fonts.medium,height: 2),),
Gaps.hGaps5,
widget.model.isReading == 1? Container(
height: 17,
width: 17,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.5.w),
border: Border.all(width:1,color: AppTheme.primary)
),
child: Text('试',style: TextStyle(fontSize: 12.w,color: AppTheme.primary),),
):const SizedBox(),
],
),
Transform.rotate(
angle: widget.model.selected?0:-90 * (3.141592653589793 / 180),
child: GestureDetector(
onTap: (){
setState(() {
widget.model.selected = !widget.model.selected;
});
},
child: SizedBox(
width: 20,
height: 20,
child: Image.asset('assets/images/down.png')
),
)
)
],
)
),
),
/// 节的名称容器
Visibility(
visible: widget.model.selected,
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index){
ChapterModel model = widget.model.children![index];
return GestureDetector(
onTap: (){
widget.onTapChapter(model);
},
child: _buildSection(model)
);
},
itemCount: widget.model.children!.length,
)
)
],
);
}
Widget _buildSection(ChapterModel model){
return Container(
color: Colors.white,
padding: const EdgeInsets.only(left: 60),
child: Text(model.name??'',style:TextStyle(fontSize: 12,color: model.seen ==0? Colours.c3:Colours.c9,height: 2),),
);
}
}
part of web;
class ReadNotePage extends StatefulWidget {
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
final void Function()? onTap;
const ReadNotePage({
Key? key,
required this.onTap,
required this.bookDetailModel,
required this.chapterId
}) : super(key: key);
@override
......@@ -16,7 +21,7 @@ class _ReadNotePageState extends State<ReadNotePage> {
@override
Widget build(BuildContext context) {
return GetBuilder<NoteController>(
init: NoteController(),
init: NoteController(bookDetailModel: widget.bookDetailModel,chapterId: widget.chapterId),
builder:(controller) =>Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
......@@ -56,7 +61,7 @@ class _ReadNotePageState extends State<ReadNotePage> {
),
),
),
BuildBook(),
BuildBook(bookDetailModel: widget.bookDetailModel,),
Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context,int index){
......
......@@ -33,28 +33,29 @@ class _SplashPageState extends State<SplashPage> {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(),
body: const Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 5,
child: Center(
child: CustomEmpty(
icon: CustomImage.asset(
url: 'assets/images/logo.png',
fit: BoxFit.contain,
),
title: Text('紫荆数智学堂'),
),
)
),
body: Image.asset('assets/images/splash.png',fit: BoxFit.cover,)
// const Column(
// crossAxisAlignment: CrossAxisAlignment.stretch,
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Expanded(
// flex: 2,
// child: Center(child: CupertinoActivityIndicator(),),
// flex: 5,
// child: Center(
// child: CustomEmpty(
// icon: CustomImage.asset(
// url: 'assets/images/logo.png',
// fit: BoxFit.contain,
// ),
// title: Text('紫荆数智学堂'),
// ),
// )
],
),
// ),
// // Expanded(
// // flex: 2,
// // child: Center(child: CupertinoActivityIndicator(),),
// // )
// ],
// ),
);
}
}
......@@ -40,8 +40,8 @@ class _AboutPageState extends State<AboutPage> {
margin: EdgeInsets.only(top: 55.w),
height: 60.w,
width: 60.w,
color: Colors.cyan,
child: const CustomImage.asset(url: 'assets/images/banner.png'),
// color: Colors.cyan,
child: const CustomImage.asset(url: 'assets/images/icon.png'),
),
Gaps.vGaps15,
Text('紫荆数智学堂',style: TextStyle(fontSize: 17.w,fontWeight: Fonts.medium,color: Colours.c3),),
......
......@@ -16,6 +16,7 @@ import 'package:get/get_state_manager/src/simple/get_controllers.dart';
import 'package:go_router/go_router.dart';
import 'package:tobias/tobias.dart';
import '../../routes/index.dart';
import '../../services/index.dart';
import '../../utils/index.dart';
import '../../widgets/index.dart';
......
......@@ -68,12 +68,17 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
),
),
Gaps.vGaps15,
RichText(text: TextSpan(
GestureDetector(
onTap: (){
context.pushNamed(Routes.terms);
},
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: Color(0xFF2A82D9))),
]
)),
),
Gaps.vGaps15
],
),
......
......@@ -10,7 +10,7 @@ class UserCouponController extends GetxController {
// 优惠券
List <CouponModel> coupons = [];
late int type = 1;
late int type = 2;
final int _limit = 10;
int _page = 1;
......@@ -27,7 +27,7 @@ class UserCouponController extends GetxController {
void getOverCoupons() {
_noMore = true;
_page = 1;
type = 2;
type = 1;
_getCoupon();
}
......
......@@ -54,6 +54,7 @@ class UserDiscussDesController extends GetxController {
refreshController.finishRefresh(IndicatorResult.success);
refreshController.resetFooter();
} catch (error) {
Console.log('------------onRefresh-----------------$error');
refreshController.finishRefresh(IndicatorResult.fail);
}
}
......
......@@ -22,6 +22,7 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl
init: UserDiscussDesController(widget.tag,widget.model),
builder: (controller) =>CustomPullScrollView(
controller: controller.refreshController,
// onRefresh: controller.onRefresh,
onLoading: controller.onLoading,
child: ListView.builder(
itemBuilder: (BuildContext context,int index){
......
part of user_edit_note;
class UserEditNoteController extends GetxController {
final NoteModel model;
final String bookId;
late TextEditingController contentInput;
UserEditNoteController(this.model,this.bookId){
_setDuration();
contentInput = TextEditingController(text: model.noteContent?.text?.content);
}
// 录音
final FlutterSoundRecorder _mRecorder = FlutterSoundRecorder();
just_audio.AudioPlayer audioPlayer = just_audio.AudioPlayer();
// 录音开始
bool startRecording = false;
// 笔记是否公开
bool isPublic = false;
bool initRecorder = false;
String currentDuration = '';
late MediaModel currentPlayMediaModel = MediaModel();
@override
void onInit() {
super.onInit();
}
@override
void onClose() {
contentInput.dispose();
audioPlayer.dispose();
super.onClose();
}
// 设置笔记是否公开
void setIsPublic(){
isPublic = !isPublic;
update();
}
// 配置音频时长
void _setDuration() async {
for(MediaModel mediaModel in model.noteContent!.audio!){
Duration? duration = await just_audio.AudioPlayer().setUrl(mediaModel.content??'');
mediaModel.duration = Tools.formatDuration(duration!);
}
update();
}
// 删除图片
void delImage(MediaModel mediaModel){
model.noteContent!.image!.remove(mediaModel);
update();
}
// 删除音频
void delAudio(MediaModel mediaModel){
model.noteContent!.audio!.remove(mediaModel);
// 新加的录音 如果删除要删除源文件
if(mediaModel.id == 0){
}
update();
}
// 添加音频
void addAudio(String path,String duration){
MediaModel mediaModel = MediaModel(path: path,id: 0,duration: duration);
model.noteContent!.audio!.add(mediaModel);
update();
}
// 添加图片
void addImage(String path){
MediaModel mediaModel = MediaModel(path: path,id: 0);
model.noteContent!.image!.add(mediaModel);
update();
}
// 初始化录音组件
Future<void> openTheRecorder() async {
if(!initRecorder){
// 获取权限
if(await Access.microphone()){
await _mRecorder.openRecorder();
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration(
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
avAudioSessionCategoryOptions:
AVAudioSessionCategoryOptions.allowBluetooth |
AVAudioSessionCategoryOptions.defaultToSpeaker,
avAudioSessionMode: AVAudioSessionMode.spokenAudio,
avAudioSessionRouteSharingPolicy:
AVAudioSessionRouteSharingPolicy.defaultPolicy,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
androidAudioAttributes: const AndroidAudioAttributes(
contentType: AndroidAudioContentType.speech,
flags: AndroidAudioFlags.none,
usage: AndroidAudioUsage.voiceCommunication,
),
androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
androidWillPauseWhenDucked: true,
));
}
// 没有权限
else {
}
}
initRecorder = true;
}
// 开启录音
void record() async {
openTheRecorder();
update();
startRecording = true;
String filePath = await Tools.getDirectory();
String fileName = Tools.generateVoiceFileName();
_mRecorder.startRecorder(
toFile: '$filePath/$fileName',
audioSource: sound_recorder_platform.AudioSource.microphone,
codec: Codec.aacMP4,
);
}
// 停止录音
void stopRecorder() async{
startRecording = false;
final path = await _mRecorder.stopRecorder();
var duration = await audioPlayer.setFilePath(path!);
print('-----duration---------------------$duration------');
// 添加到数组
addAudio(path, Tools.formatDuration(duration!));
}
// 播放音频
void playAudio(MediaModel mediaModel){
if(audioPlayer.playerState.playing){
audioPlayer.stop();
mediaModel.currentDuration = '0:00:00';
// if(currentPlayMediaModel.id == mediaModel.id){
// return;
// }
}
// 本地音频
if (mediaModel.id == 0){
audioPlayer.setFilePath(mediaModel.path);
}
// 远程音频
else {
audioPlayer.setUrl(mediaModel.content??'');
}
audioPlayer.play();
audioPlayer.positionStream.listen((position) {
String temp = Tools.formatDuration(position);
Console.log('播放时间---------------------$temp');
mediaModel.currentDuration = temp;
update();
});
// currentPlayMediaModel = mediaModel;
}
// 上传文件
Future<String> upload({
required String path
}) async {
// String result = await CommonAPI.upload(path:path,fileTypes: 'comment');
// return result;
OssTool tool = OssTool('zxts-comment-file');
final response = await tool.putObjectFile(path);
print('------response--------------------------${response.realUri}');
return response.realUri.toString();
}
// 提交
Future<bool> submit() async {
// 循环上传图片获取地址
for(MediaModel mediaModel in model.noteContent!.image!){
if (mediaModel.id == 0){
final url = await upload(path: mediaModel.path);
mediaModel.content = url;
}
}
// 循环上传音频获取地址
for(MediaModel mediaModel in model.noteContent!.audio!){
if (mediaModel.id == 0){
final url = await upload(path: mediaModel.path);
mediaModel.content = url;
}
}
// 组织图片
List<Map> images = [];
for(MediaModel mediaModel in model.noteContent!.image!){
images.add( mediaModel.toJson());
}
// 组织音频
List<Map> audios = [];
for(MediaModel mediaModel in model.noteContent!.audio!){
audios.add( mediaModel.toJson());
}
model.noteContent!.text!.content = contentInput.text;
Map<String,dynamic> contentMap = {
'text':model.noteContent!.text!.toJson(),
'audio':audios,
'image':images
};
final result = MineAPI.editNotes(
content: model.content??'',
notesId: model.notesId.toString(),
bookId: bookId,
noteContent: jsonEncode(contentMap)
);
return result;
}
}
\ No newline at end of file
library user_edit_note;
import 'dart:convert';
import 'package:audio_session/audio_session.dart';
import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_sound/public/flutter_sound_recorder.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_platform_interface.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart' as sound_recorder_platform;
import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import 'package:just_audio/just_audio.dart' as just_audio;
import 'package:just_audio/just_audio.dart';
import 'dart:async';
import '../../models/index.dart';
import '../../theme.dart';
import '../../utils/index.dart';
import '../../widgets/index.dart';
part 'view.dart';
part 'controller.dart';
\ No newline at end of file
part of user_edit_note;
class UserEditNotePage extends StatefulWidget {
final NoteModel model;
final String bookId;
const UserEditNotePage({
Key? key,
required this.model,
required this.bookId,
}) : super(key: key);
@override
State<UserEditNotePage> createState() => _UserEditNotePageState();
}
class _UserEditNotePageState extends State<UserEditNotePage> {
@override
Widget build(BuildContext context) {
return GetBuilder<UserEditNoteController>(
init: UserEditNoteController(widget.model,widget.bookId),
builder:(controller)=> Scaffold(
appBar: AppBar(
title: const Text('笔记编辑'),
centerTitle: true,
),
body: Container(
color: Colors.white,
margin: EdgeInsets.symmetric(horizontal: 10.w),
child: Stack(
children: [
Container(
// color: Colours.cF8,
constraints: BoxConstraints(
minHeight: 100.w
),
child: Column(
children: [
TextField(
// focusNode: widget.controller.discussContentFocusNode,
maxLines: null,
autofocus: true,
controller: controller.contentInput,
decoration: InputDecoration(
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
hintText: '请输入内容',
hintStyle:TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9,),
filled: true,
// fillColor: Colours.cF8,
),
),
Column(
children: [
GridView.builder(
// padding: const EdgeInsets.only(left: 13,top: 10),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 2.w,
mainAxisSpacing: 2.w,
childAspectRatio: 1
),
itemBuilder: (BuildContext context, int index) {
MediaModel mediaModel = controller.model.noteContent!.image![index];
return Stack(
children: [
Positioned(
left:0,
right: 0,
top:0,
bottom: 0,
child: mediaModel.id == 0?CustomImage.file(url:mediaModel.path): CustomImage.network(url: mediaModel.content??'')
),
Positioned(
right: 0.w,
top: 0.w,
child: GestureDetector(
onTap: (){
controller.delImage(mediaModel);
},
child: Image.asset('assets/images/media_del.png',width: 17.w,height: 17.w,)
)
)
],
);
},
itemCount: controller.model.noteContent?.image?.length,
),
Container(
// color: Colors.red,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index){
MediaModel mediaModel = controller.model.noteContent!.audio![index];
return Container(
// height: 20.w,
margin: EdgeInsets.only(right: 130.w,),
child: Stack(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.w),
color: Colours.cF9,
),
padding: EdgeInsets.only(right:20.w,left: 10.w),
margin: EdgeInsets.only(top: 10.w,right: 10.w),
height: 20,
child: Row(
// mainAxisSize: MainAxisSize.min,
mainAxisAlignment:MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap:(){
controller.playAudio(mediaModel);
},
child: Image.asset('assets/images/audio.png')
),
Text('${mediaModel.currentDuration}/${mediaModel.duration}',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),)
],
),
),
Positioned(
right: 6,
top: 6,
child: GestureDetector(
onTap: (){
controller.delAudio(mediaModel);
},
child: Image.asset('assets/images/media_del.png',width: 12.w,height: 12.w,)
)
)
],
),
);
},
itemCount: controller.model.noteContent?.audio?.length,
),
),
)
],
)
],
),
),
Visibility(
visible: true,
child: Positioned(
left: 10,
right: 10,
bottom: 0,
child: Container(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
GestureDetector(
onTap: () async{
final assets = await AssetsPicker.image(
context: context,
);
controller.addImage(assets!.path);
},
child: Image.asset('assets/images/read_add_img.png')),
Gaps.hGaps10,
GestureDetector(
onTap: () async {
if(controller.startRecording){
controller.stopRecorder();
}
else {
controller.record();
}
},
child: Image.asset(controller.startRecording?'assets/images/stop.png':'assets/images/read_add_audio.png')
),
GestureDetector(
onTap: (){
controller.setIsPublic();
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Gaps.hGaps10,
Image.asset(controller.isPublic?'assets/images/pay_check.png':'assets/images/pay_uncheck.png'),
SizedBox(width: 3.w,),
Text('公开',style: TextStyle(fontSize: 13.w,height: 1.3,color: Colours.c9),)
],
),
)
],
),
GestureDetector(
onTap: () async{
final result = await controller.submit();
if (result){
Tools.unfocus();
Toast.show('修改成功');
context.pop();
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.w),
color: AppTheme.primary,
gradient: LinearGradient(
colors: [const Color(0xFFD53676).withOpacity(0.9),AppTheme.primary] , // 不可点击时的颜色,透明度为0.7
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
padding: EdgeInsets.symmetric(horizontal:13.5.w,vertical: 4.w),
child: Text('发表',style: TextStyle(fontSize: 14.w,fontWeight: Fonts.medium,color: Colors.white),),
),
)
],
),
),
),
)
],
)
),
),
);
}
}
......@@ -9,8 +9,12 @@ class UserInfoController extends GetxController {
void upload({
required String path
}) async {
String result = await CommonAPI.upload(path:path,fileTypes: 'user');
_changeInfo(result);
// String result = await CommonAPI.upload(path:path,fileTypes: 'user');
OssTool tool = OssTool('zxts-user-file');
print('------path--------------------------$path');
final response = await tool.putObjectFile(path);
print('------response--------------------------${response.realUri}');
_changeInfo(response.realUri.toString());
}
void show(){
......
......@@ -217,6 +217,124 @@ class _UserInfoPageState extends State<UserInfoPage> {
),
],
);
return WillPopScope(
onWillPop: () async {
context.pop(true);
return false;
},
child: GetBuilder<UserInfoController>(
init: UserInfoController(widget.userInfo),
builder: (controller) =>
Scaffold(
appBar: AppBar(
title: const Text('个人信息'),
centerTitle: true,
),
body: Container(
margin: EdgeInsets.symmetric(
horizontal: AppTheme.margin, vertical: AppTheme.margin),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.w),
color: Colors.white,
boxShadow: [
BoxShadow(
color: const Color(0xFFC7C7C7).withOpacity(0.5),
offset: Offset(3.w, 0),
blurRadius: 10.w,
spreadRadius: 0.w,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// 头像
Container(
height: 52.w,
padding: EdgeInsets.only(left: 18.w, right: 15.w),
child: GestureDetector(
onTap: () async {
final assets = await AssetsPicker.image(
context: context,
);
controller.upload(path: assets!.path);
},
child: Container(
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'头像',
style: TextStyle(
color: Colours.c3,
fontSize: 14.w,
height: 1.6),
),
Row(
children: [
Container(
width: 33.w,
height: 33.w,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.cyan,
),
child: CustomImage.network(
url: controller.userInfo.headImg ?? '',
radius: 16.5,
),
),
Gaps.hGaps10,
SizedBox(
width: 5.w,
height: 8.w,
child: Image.asset(
'assets/images/right_arrow.png'),
)
],
)
],
),
),
),
),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
GestureDetector(
onTap: () {
context.pushNamed(Routes.nike, extra: controller.userInfo);
},
child: _buildItem(
'昵称',
widget.userInfo.name ?? '',
)),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
GestureDetector(
onTap: () {
context.pushNamed(
Routes.gender, extra: controller.userInfo);
},
child: _buildItem(
'性别',
getGender(widget.userInfo.sex)
// widget.userInfo.sex == 1 ? '男' : '女',
)),
],
),
),
)),),
);
}
getGender(num? sex) {
......
......@@ -35,12 +35,24 @@ class MsgPage extends StatelessWidget {
if(model.type == 1){
// 1订单支付快要超时(跳转订单详情)
final result = await context.pushNamed(Routes.order);
if (result == true){
controller.onRefresh();
}
///TODO:
}else if (model.type == 2){
// 2 购买完成三天未评价(跳转订单列表--已完成)
final result = await context.pushNamed(Routes.order);
if (result == true){
controller.onRefresh();
}
///TODO:
}else if (model.type == 3){
// 3 讨论有人回复的时候 (跳转对应书籍的讨论页面)
final result = await context.pushNamed(Routes.bookDetail,queryParameters: {'book_id':model.urlId?.bookId.toString()});
if (result == true){
controller.onRefresh();
}
}
else if (model.type == 4){
// 4 订单完成后有新的积分增加(跳转用户积分记录页)
......
......@@ -11,6 +11,8 @@ class UserNotesDesController extends GetxController {
controlFinishRefresh: true,
);
just_audio.AudioPlayer audioPlayer = just_audio.AudioPlayer();
final int _limit = 10;
int _page = 1;
bool _noMore = false;
......@@ -25,9 +27,44 @@ class UserNotesDesController extends GetxController {
@override
void onClose() {
refreshController.dispose();
audioPlayer.dispose();
super.onClose();
}
// 播放音频
void playAudio(MediaModel mediaModel){
if(audioPlayer.playerState.playing){
audioPlayer.stop();
mediaModel.currentDuration = '0:00:00';
// if(currentPlayMediaModel.id == mediaModel.id){
// return;
// }
}
// 本地音频
if (mediaModel.id == 0){
audioPlayer.setFilePath(mediaModel.path);
}
// 远程音频
else {
audioPlayer.setUrl(mediaModel.content??'');
}
audioPlayer.play();
audioPlayer.positionStream.listen((position) {
String temp = Tools.formatDuration(position);
Console.log('播放时间---------------------$temp');
mediaModel.currentDuration = temp;
update();
});
// currentPlayMediaModel = mediaModel;
}
void delNotes({required String notesId,required String bookId}) async {
final result = await MineAPI.delNotes(notesId: notesId, bookId: bookId);
if (result){
Toast.show('删除笔记成功');
onRefresh();
}
}
/// 获取笔记列表
Future<void> _getNotes([bool isRefresh = false]) async {
......@@ -39,6 +76,15 @@ class UserNotesDesController extends GetxController {
bookId: model.bookId.toString(),
types: tag
);
for(NoteModel noteModel in result){
if(noteModel.noteContent!.audio!.isNotEmpty){
for(MediaModel mediaModel in noteModel.noteContent!.audio!){
Duration? duration = await just_audio.AudioPlayer().setUrl(mediaModel.content??'');
mediaModel.duration = Tools.formatDuration(duration!);
}
}
}
// 如果是刷新 清理数据
if (isRefresh) notes.clear();
notes.addAll(result);
......@@ -54,6 +100,7 @@ class UserNotesDesController extends GetxController {
refreshController.finishRefresh(IndicatorResult.success);
refreshController.resetFooter();
} catch (error) {
Console.log('----error--------------------------------------$error--------------');
refreshController.finishRefresh(IndicatorResult.fail);
}
}
......
......@@ -4,12 +4,16 @@ import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import '../../apis/index.dart';
import '../../models/index.dart';
import '../../routes/index.dart';
import '../../theme.dart';
import '../../utils/index.dart';
import 'package:just_audio/just_audio.dart' as just_audio;
part 'view.dart';
......
......@@ -2,16 +2,17 @@ part of user_notes_des;
class BuildHigh extends StatelessWidget {
final NoteModel model;
final void Function()? onTapDel;
const BuildHigh({
Key? key,
required this.model
required this.model,
this.onTapDel
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w),
color: Colors.white,
......@@ -24,6 +25,24 @@ class BuildHigh extends StatelessWidget {
),
],
),
child: Slidable(
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
onTapDel;
},
backgroundColor: const Color(0xFFAE1414),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '删除',
),
],
),
child: Container(
padding: EdgeInsets.all(10.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
......@@ -42,6 +61,8 @@ class BuildHigh extends StatelessWidget {
)
],
),
),
),
);
}
}
......@@ -2,16 +2,17 @@ part of user_notes_des;
class BuildLine extends StatelessWidget {
final NoteModel model;
final void Function()? onTapDel;
const BuildLine({
Key? key,
required this.model
required this.model,
this.onTapDel
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w),
color: Colors.white,
......@@ -24,6 +25,24 @@ class BuildLine extends StatelessWidget {
),
],
),
child: Slidable(
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
onTapDel;
},
backgroundColor: const Color(0xFFAE1414),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '删除',
),
],
),
child: Container(
padding: EdgeInsets.all(10.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
......@@ -45,6 +64,8 @@ class BuildLine extends StatelessWidget {
)
],
),
),
),
);
}
}
......@@ -28,16 +28,35 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl
NoteModel model = controller.notes[index];
// 划线
if(model.types == 1){
return BuildLine(model: model,);
return BuildLine(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
);
}
// 高亮
else if(model.types == 2){
return BuildHigh(model: model,);
return BuildHigh(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
);
}
// 笔记
else if(model.types == 3){
return BuildNote(model: model,);
return BuildNote(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
onTapEdit: (){
context.pushNamed(Routes.editNote,extra: model,queryParameters: {'book_id':widget.model.bookId.toString()});
},
onTapAudio: (mediaModel){
controller.playAudio(mediaModel);
},
);
}
return null;
},
itemCount: controller.notes.length,
),
......
......@@ -2,16 +2,22 @@ part of user_notes_des;
class BuildNote extends StatelessWidget {
final NoteModel model;
final void Function()? onTapDel;
final void Function()? onTapEdit;
final void Function(MediaModel mediaModel)? onTapAudio;
const BuildNote({
Key? key,
required this.model
required this.model,
this.onTapDel,
this.onTapEdit,
this.onTapAudio
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w),
// padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w),
color: Colors.white,
......@@ -24,10 +30,39 @@ class BuildNote extends StatelessWidget {
),
],
),
child: Slidable(
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
if (onTapEdit !=null) onTapEdit!();
},
backgroundColor: const Color(0xFFEB914A),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '编辑',
),
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
if (onTapDel !=null) onTapDel!();
},
backgroundColor: const Color(0xFFAE1414),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '删除',
),
],
),
child: Container(
padding: EdgeInsets.all(10.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('看来谁都不记得自己为何出现在此',style: TextStyle(
Text(model.noteContent?.text?.content??'',style: TextStyle(
fontSize: 14.w,
height: 1.5,
// color: Colors.red,
......@@ -52,6 +87,8 @@ class BuildNote extends StatelessWidget {
)
],
),
),
),
);
}
......@@ -59,7 +96,7 @@ class BuildNote extends StatelessWidget {
Widget _buildImageGridView(){
return GridView.builder(
// padding: const EdgeInsets.only(left: 13,top: 10),
physics: NeverScrollableScrollPhysics(),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6,
......@@ -68,12 +105,9 @@ class BuildNote extends StatelessWidget {
childAspectRatio: 1
),
itemBuilder: (BuildContext context, int index) {
return Container(
color: Colors.red,
child: Center(child: Text('图片')),
);
return CustomImage.network(url: model.noteContent!.image![index].content??'',fit: BoxFit.cover,);
},
itemCount: 3,
itemCount: model.noteContent?.image?.length,
);
}
......@@ -82,6 +116,7 @@ class BuildNote extends StatelessWidget {
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
MediaModel mediaModel = model.noteContent!.audio![index];
return Container(
height: 20.w,
margin: EdgeInsets.only(right: 130.w),
......@@ -96,34 +131,19 @@ class BuildNote extends StatelessWidget {
// mainAxisSize: MainAxisSize.min,
mainAxisAlignment:MainAxisAlignment.spaceBetween,
children: [
Image.asset('assets/images/audio.png'),
Text('0:00/1:52',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),)
GestureDetector(
onTap: (){
if (onTapAudio !=null) onTapAudio!(mediaModel);
},
child: Image.asset('assets/images/audio.png')
),
Text('0:00/${mediaModel.duration}',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),)
],
),
),
);
},
itemCount: 3,
itemCount: model.noteContent?.audio?.length,
);
}
// Widget _buildAudioGridView(){
// return GridView.builder(
// physics: const NeverScrollableScrollPhysics(),
// shrinkWrap: true,
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 6,
// crossAxisSpacing: 2.w,
// mainAxisSpacing: 2.w,
// childAspectRatio: 1
// ),
// itemBuilder: (BuildContext context, int index) {
// return Container(
// color: Colors.red,
// child: Center(child: Text('音频')),
// );
// },
// itemCount: 3,
// );
// }
}
......@@ -45,6 +45,7 @@ import 'package:go_router/go_router.dart';
import '../models/index.dart';
import '../pages/bai_ke/index.dart';
import '../pages/read_web/index.dart';
import '../pages/user_edit_note/index.dart';
import '../pages/user_order/index.dart';
import '../pages/user_order_evaluate/index.dart';
import '../pages/pay_coupon/index.dart';
......
......@@ -79,6 +79,8 @@ abstract class Routes {
static const note = 'note';
// 笔记详情
static const noteDes = 'note_des';
// 编辑笔记
static const editNote = 'edit_note';
// 错题
static const wrong = 'wrong';
// 讨论
......@@ -189,6 +191,7 @@ abstract class Routes {
bookId: state.uri.queryParameters['book_id'].toString(),
chapterId: state.uri.queryParameters['chapter_id'].toString(),
chapterName: state.uri.queryParameters['chapter_name'].toString(),
bookDetailModel: state.extra as BookDetailModel,
)
)
),
......@@ -553,6 +556,15 @@ abstract class Routes {
)
)
),
GoRoute(
path: '/$editNote',
name: editNote,
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( // 订单搜索
path: '/$orderSearch',
name: orderSearch,
......
......@@ -19,13 +19,14 @@ class HttpService extends GetxService {
_dio = Dio(options);
_dio.interceptors.add(_RequestInterceptor());
_dio.interceptors.add(_CacheInterceptor());
}
/// 组织 headers 参数
Map<String,dynamic>? _getHeaders({bool excludeToken = false,Map<String,dynamic>? params,String? url}) {
final headers = <String, dynamic>{};
headers['appId'] = AppConfig.AppID;
headers['appSecret'] = AppConfig.AppSecret;
headers['appId'] = AppConfig.appID;
headers['appSecret'] = AppConfig.appSecret;
headers['timestamp'] = (DateTime.now().millisecondsSinceEpoch~/1000).toString();
headers['url'] = kServerUrl + url.toString();
......@@ -56,11 +57,18 @@ class HttpService extends GetxService {
CancelToken? cancelToken,
bool excludeToken = false,
bool showLoading = false,
bool cacheEnabled = false,
}) async {
if (showLoading) CustomToast.loading();
try {
final requestOptions = options ?? Options();
requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
// 如果启用缓存,将cacheEnabled参数添加到请求选项中
if(cacheEnabled){
requestOptions.extra ??= {};
requestOptions.extra!['cacheEnabled'] = true;
}
final response = await _dio.get(
url,
queryParameters: params,
......@@ -83,6 +91,7 @@ class HttpService extends GetxService {
CancelToken? cancelToken,
bool excludeToken = false,
bool showLoading = false,
bool cacheEnabled = true,
}) async{
if (showLoading) CustomToast.loading();
try {
......@@ -90,6 +99,12 @@ class HttpService extends GetxService {
requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
Console.log('----headers------${requestOptions.headers}');
Console.log('----params------$params');
// 如果启用缓存,将cacheEnabled参数添加到请求选项中
if(cacheEnabled){
requestOptions.extra ??= {};
requestOptions.extra!['cacheEnabled'] = false;
}
final response = await _dio.post(
url,
data: params,
......@@ -169,25 +184,10 @@ class HttpService extends GetxService {
class _RequestInterceptor extends Interceptor {
@override
void onResponse(Response response, ResponseInterceptorHandler handler) async {
final responseData = response.data;
if (responseData is Map && responseData.containsKey('code')) {
final code = responseData['code'];
if (code != 200) {
handler.reject(
DioException(
requestOptions: response.requestOptions,
response: response,
type: DioExceptionType.badResponse,
),
true,
);
return;
}
} else if (response.requestOptions.responseType == ResponseType.bytes) {
}
super.onResponse(response, handler);
// if (response.data['code'] != 200) {
// final responseData = response.data;
// if (responseData is Map && responseData.containsKey('code')) {
// final code = responseData['code'];
// if (code != 200) {
// handler.reject(
// DioException(
// requestOptions: response.requestOptions,
......@@ -196,9 +196,24 @@ class _RequestInterceptor extends Interceptor {
// ),
// true,
// );
// }else {
// super.onResponse(response, handler);
// return;
// }
// } else if (response.requestOptions.responseType == ResponseType.bytes) {
//
// }
// super.onResponse(response, handler);
if (response.data['code'] != 200) {
handler.reject(
DioException(
requestOptions: response.requestOptions,
response: response,
type: DioExceptionType.badResponse,
),
true,
);
}else {
super.onResponse(response, handler);
}
}
@override
......@@ -226,7 +241,7 @@ class _RequestInterceptor extends Interceptor {
var msg = '服务器错误';
switch (statusCode) {
case 403:
print('-------------------access_token-------------------------${UserStore.to.accessToken}--------------------');
print('----------403---------access_token-------------------------${UserStore.to.accessToken}--------------------');
msg = '$statusCode - Unauthorized';
final newToken = await refreshToken();
if (newToken != null) {
......@@ -260,15 +275,15 @@ class _RequestInterceptor extends Interceptor {
break;
case 404:
msg = '$statusCode - Server not found';
CustomToast.fail(msg);
// CustomToast.fail(msg);
break;
case 500:
msg = '$statusCode - Server error';
CustomToast.fail(msg);
// CustomToast.fail(msg);
break;
case 502:
msg = '$statusCode - Bad gateway';
CustomToast.fail(msg);
// CustomToast.fail(msg);
break;
default:
// if (code == 901) UserStore.to.logout();
......@@ -296,7 +311,7 @@ class _RequestInterceptor extends Interceptor {
final result = await HttpService.to.post(
'/v1/members/login/getToken',
params: {
'access_token':UserStore.to.accessToken
'access_token':StorageService.to.getString(kLocalAccessToken)
}
);
if (result.data is Map) {
......@@ -313,4 +328,61 @@ class _RequestInterceptor extends Interceptor {
}
// 缓存拦截器
class _CacheInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
final cacheEnabled = options.extra?['cacheEnabled'] ?? false;
if (cacheEnabled) {
// 在发起请求之前,检查缓存是否存在有效数据
final cachedFile = await DefaultCacheManager().getFileFromCache(options.uri.toString());
if (cachedFile != null && cachedFile.validTill.isAfter(DateTime.now())) {
// 如果缓存有效,直接返回缓存数据
final cachedData = await cachedFile.file.readAsBytes();
final decodedData = utf8.decode(cachedData); // 将字节列表解码为字符串
final jsonData = jsonDecode(decodedData);
handler.resolve(Response(
requestOptions: options,
statusCode: 200,
data: jsonData,
));
return;
}
}
// 如果缓存不存在或已过期,或未启用缓存,继续发起网络请求
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) async {
// 只缓存状态码为200的响应
if (response.statusCode == 200) {
try {
// 获取请求的 URL
final url = response.requestOptions.uri.toString();
// 获取请求体参数(如果是 POST 请求)
final requestBody = response.requestOptions.data.toString();
// 将 GET 请求的参数和请求体参数拼接成缓存的键
final cacheKey = requestBody.isEmpty ? url : '$url?$requestBody';
// Console.log('----------cacheKey-----------------------$cacheKey');
// 将响应数据转换为字符串,并将其编码为字节列表
List<int> bytes = utf8.encode(jsonEncode(response.data));
Uint8List uint8List = Uint8List.fromList(bytes);
// 创建一个缓存文件并将数据写入其中
DefaultCacheManager().putFile(
cacheKey,
uint8List,
fileExtension: '.json', // 可以根据需求修改文件扩展名
maxAge: const Duration(hours: 1)
);
} catch (e) {
Console.log('Error caching response: $e');
}
}
super.onResponse(response, handler);
}
}
......@@ -2,11 +2,13 @@ library services;
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:get/get.dart' hide Response, FormData, MultipartFile;
import 'package:shared_preferences/shared_preferences.dart';
......
......@@ -31,4 +31,10 @@ abstract class Access {
return result[Permission.storage] == PermissionStatus.granted;
}
/// 获取麦克风权限
static Future<bool> microphone() async {
final result = await [Permission.microphone].request();
return result[Permission.microphone] == PermissionStatus.granted;
}
}
......@@ -2,10 +2,14 @@ part of utils;
class AppConfig{
//app在服务端注册的id,由后端直接提供
static const String AppID = 'dkFv6OwWyQhSeaZG';
static const String appID = 'dkFv6OwWyQhSeaZG';
//app在服务端的秘钥串,由后端直接提供
static const String AppSecret = 'd34cea92316d1da113e95158bed9ca97';
static const String appSecret = 'd34cea92316d1da113e95158bed9ca97';
//签名时使用的加密串,由后端直接提供
static const String AppEncodeKey = '&4F6g4Y6b5L4R9';
static const String appEncodeKey = '&4F6g4Y6b5L4R9';
// 下载需要的key
static const String aesKey = 'e8a83e801cf85d90b69556b0cb1e0c8a';
static const String aesIV = '13e801c8901b6955';
}
......@@ -14,6 +14,7 @@ const String kFailOrder = 'failOrder';
abstract class C {
static const String localAccount = 'storage_account';
static const String localThemeMode = 'storage_theme_mode';
......
......@@ -7,4 +7,25 @@ class EncryptUtil {
var digest = md5.convert(content);
return hex.encode(digest.bytes);
}
/// AES 加密
static aesEncrypt(String content,[String aesKey = AppConfig.aesKey,]) {
final key = encrypt.Key.fromUtf8(aesKey);
final iv = encrypt.IV.fromBase64(base64Encode(utf8.encode(AppConfig.aesIV)));
final encryptor = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc),);
final encrypted = encryptor.encrypt(content, iv: iv);
return base64Encode(utf8.encode(encrypted.base64));
}
/// AES 解密
static aesDecrypt(String content,[String aesKey = AppConfig.aesKey,]) {
final key = encrypt.Key.fromUtf8(aesKey);
final iv = encrypt.IV.fromBase64(base64Encode(utf8.encode(AppConfig.aesIV)));
final encryptor = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypt.Encrypted.fromBase64(utf8.decode(base64.decode(content)));
final decrypted = encryptor.decrypt(encrypted, iv: iv);
return decrypted.trimRight();
}
}
\ No newline at end of file
library utils;
import 'dart:convert';
import 'dart:convert';
import 'dart:convert';
import 'dart:io';
import 'dart:ui';
// import 'package:encrypt/encrypt.dart';
import 'package:dio/dio.dart';
import 'package:encrypt/encrypt.dart' as encrypt; // 使用别名 'encrypt'
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
......@@ -17,6 +23,8 @@ import 'package:oktoast/oktoast.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart';
import '../models/index.dart';
import '../widgets/index.dart';
......@@ -33,3 +41,4 @@ part 'toast_utils.dart';
part 'clear_cache_util.dart';
part 'access.dart';
part 'assets_picker.dart';
part 'oss.dart';
\ No newline at end of file
part of utils;
//'bucket_comment' => 'zxts-comment-file', 'bucket_user' => 'zxts-user-file', 'endpoint' => 'zijingebook.com',
class OssTool {
late OssModel ossModel;
final String bucketName;
OssTool(this.bucketName){
initOss(bucketName);
}
void initOss(String bucketName) async {
Client.init(ossEndpoint: 'oss-cn-beijing.aliyuncs.com', bucketName:bucketName,authGetter: _authGetter);
}
// 获取临时凭证
Future<Auth> _authGetter() async {
final result = await CommonAPI.oss();
return Auth(
accessKey: result.accessKeyId!,
accessSecret: result.accessKeySecret!,
expire: result.expiration!,
secureToken: result.securityToken!,
);
}
// 本地文件上传
Future<Response<dynamic>> putObjectFile(String filePath) async {
print('------------------222222222222222222222--------------$filePath');
print('------------------222222222222222222222--------------');
List<String> parts = filePath.split('/');
String fileName = parts.last;
final String path = '${DateTime.now().year}/${DateTime.now().month.toString().padLeft(2,'0')}/${DateTime.now().day.toString().padLeft(2,'0')}';
final Response<dynamic> resp = await Client().putObjectFile(
filePath,
fileKey: '$path/$fileName'
);
print('99999999999-------------${resp.realUri}---${resp.extra}--${resp.data}');
return resp;
}
// 批量本地文件上传
Future<List<Response>> putObjectFiles(List<String> filePaths){
return Client().putObjectFiles(
filePaths.map((e){
return AssetFileEntity(
filepath: e
);
}).toList()
);
}
}
\ No newline at end of file
......@@ -8,7 +8,7 @@ class SignTool {
tempArr.add("$key=${tempParams[key]}");
}
String tempStr = tempArr.join("&");
String finalString = tempStr + AppConfig.AppEncodeKey;
String finalString = tempStr + AppConfig.appEncodeKey;
String finalEncodeStr = EncryptUtil.encodeMd5(EncryptUtil.encodeMd5(finalString));
return finalEncodeStr;
}
......
......@@ -5,4 +5,51 @@ abstract class Tools {
static void unfocus() {
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
}
static String dateFromMS(
int timestamp, {
String pattern = 'yyyy-MM-dd',
bool humanize = false,
}) {
final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp*1000);
if (humanize) {
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inMinutes < 60) {
if (difference.inMinutes < 1) return '刚刚';
return '${difference.inMinutes}分钟前';
} else if (difference.inHours < 24) {
return '${difference.inHours}小时前';
} else if (difference.inDays < 30) {
return '${difference.inDays}天前';
} else if (now.year == dateTime.year) {
return DateFormat('MM-dd').format(dateTime);
}
}
return DateFormat(pattern).format(dateTime);
}
static String formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${duration.inHours}:$twoDigitMinutes:$twoDigitSeconds";
}
static Future<String> getDirectory() async {
// getTemporaryDirectory
final directory = await getExternalStorageDirectory();
return directory!.path;
}
// 语音文件名称
static String generateVoiceFileName(){
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyyMMddHHmmss').format(now);
return 'voice_$formattedDate.mp4';
}
}
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: "direct main"
description:
name: archive
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.4.10"
args:
dependency: transitive
description:
name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.2"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.5.2"
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.11.0"
audio_session:
......@@ -14,7 +38,7 @@ packages:
description:
name: audio_session
sha256: "6fdf255ed3af86535c96452c33ecff1245990bb25a605bfb1958661ccc3d467f"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.18"
azlistview:
......@@ -22,7 +46,7 @@ packages:
description:
name: azlistview
sha256: "93e865f11777a271b439f0d6b00799c0797e9daeec2e082a2e01373809c4b90d"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
badges:
......@@ -30,7 +54,7 @@ packages:
description:
name: badges
sha256: a7b6bbd60dce418df0db3058b53f9d083c22cdb5132a052145dc267494df0b84
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.2"
boolean_selector:
......@@ -38,7 +62,7 @@ packages:
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
carousel_slider:
......@@ -46,7 +70,7 @@ packages:
description:
name: carousel_slider
sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.1"
characters:
......@@ -54,7 +78,7 @@ packages:
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
clock:
......@@ -62,7 +86,7 @@ packages:
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
collection:
......@@ -70,15 +94,31 @@ packages:
description:
name: collection
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.17.1"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.2"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.4"
convert:
dependency: "direct main"
description:
name: convert
sha256: f08428ad63615f96a27e34221c65e1a451439b5f26030f78d790f461c686d65d
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
cross_file:
......@@ -86,7 +126,7 @@ packages:
description:
name: cross_file
sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.3+7"
crypto:
......@@ -94,7 +134,7 @@ packages:
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.3"
cupertino_icons:
......@@ -102,15 +142,23 @@ packages:
description:
name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.6"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.10"
decimal:
dependency: "direct main"
description:
name: decimal
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.3"
dio:
......@@ -118,7 +166,7 @@ packages:
description:
name: dio
sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.3.3"
easy_refresh:
......@@ -126,15 +174,23 @@ packages:
description:
name: easy_refresh
sha256: "77b025ea49f27b5ebc5eef40a6678be52564c293bd97ce91a4088d6646478329"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.3.2+1"
encrypt:
dependency: "direct main"
description:
name: encrypt
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.3"
extended_image:
dependency: "direct main"
description:
name: extended_image
sha256: e77d18f956649ba6e5ecebd0cb68542120886336a75ee673788145bd4c3f0767
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.0.2"
extended_image_library:
......@@ -142,7 +198,7 @@ packages:
description:
name: extended_image_library
sha256: bb8d08c504ebc73d476ec1c99451a61f12e95538869e734fc4f55a3a2d5c98ec
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.5.3"
fake_async:
......@@ -150,7 +206,7 @@ packages:
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
ffi:
......@@ -158,7 +214,7 @@ packages:
description:
name: ffi
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
file:
......@@ -166,7 +222,7 @@ packages:
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.0.0"
flutter:
......@@ -179,15 +235,23 @@ packages:
description:
name: flutter_app_update
sha256: "2202e6e6d49eacc571304e5a6a5404f893382b492029d01d33b3bfb9af1c2f25"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.4"
flutter_cache_manager:
dependency: "direct main"
description:
name: flutter_cache_manager
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.3.1"
flutter_easyloading:
dependency: "direct main"
description:
name: flutter_easyloading
sha256: ba21a3c883544e582f9cc455a4a0907556714e1e9cf0eababfcb600da191d17c
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.5"
flutter_inapp_purchase:
......@@ -195,7 +259,7 @@ packages:
description:
name: flutter_inapp_purchase
sha256: d67544e703439feefe91cab9cb4920cffc0f1576f7ce51a78e3a0a3c5f7d3f1e
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.6.1"
flutter_inappwebview:
......@@ -203,7 +267,7 @@ packages:
description:
name: flutter_inappwebview
sha256: d198297060d116b94048301ee6749cd2e7d03c1f2689783f52d210a6b7aba350
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.8.0"
flutter_lints:
......@@ -211,7 +275,7 @@ packages:
description:
name: flutter_lints
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
flutter_localizations:
......@@ -219,12 +283,20 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_oss_aliyun:
dependency: "direct main"
description:
name: flutter_oss_aliyun
sha256: d4e7423a29a3ff41aff6ab8278e0371716a3c3c1d086f937a9695a70ea8f8d39
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.4.1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.17"
flutter_screenutil:
......@@ -232,7 +304,7 @@ packages:
description:
name: flutter_screenutil
sha256: b3e155ee4f2cf5b21a2e15182d1c49c848147ed47f62083fc9a9beccb85f59f9
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.8.2"
flutter_slidable:
......@@ -240,7 +312,7 @@ packages:
description:
name: flutter_slidable
sha256: "19ed4813003a6ff4e9c6bcce37e792a2a358919d7603b2b31ff200229191e44c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
flutter_sound:
......@@ -248,7 +320,7 @@ packages:
description:
name: flutter_sound
sha256: "090a4694b11ecc744c2010621c4ffc5fe7c3079d304ea014961a72c7b72cfe6c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "9.2.13"
flutter_sound_platform_interface:
......@@ -256,7 +328,7 @@ packages:
description:
name: flutter_sound_platform_interface
sha256: "4537eaeb58a32748c42b621ad6116f7f4c6ee0a8d6ffaa501b165fe1c9df4753"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "9.2.13"
flutter_sound_web:
......@@ -264,7 +336,7 @@ packages:
description:
name: flutter_sound_web
sha256: ad4ca92671a1879e1f613e900bbbdb8170b20d57d1e4e6363018fe56b055594f
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "9.2.13"
flutter_spinkit:
......@@ -272,7 +344,7 @@ packages:
description:
name: flutter_spinkit
sha256: b39c753e909d4796906c5696a14daf33639a76e017136c8d82bf3e620ce5bb8e
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.2.0"
flutter_star:
......@@ -280,7 +352,7 @@ packages:
description:
name: flutter_star
sha256: "7dc10b8b3667ace2aa575a37ea0c00558a7514019cfe7e76322573d85b72a472"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
flutter_test:
......@@ -293,7 +365,7 @@ packages:
description:
name: flutter_tts
sha256: cbb3fd43b946e62398560235469e6113e4fe26c40eab1b7cb5e7c417503fb3a8
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.8.5"
flutter_web_plugins:
......@@ -306,7 +378,7 @@ packages:
description:
name: fluwx
sha256: "7a1596e8fad1b2191cf62f4cc80f3c63c01306bd6d8fa4f5c815f5e309da1dba"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.4.9"
get:
......@@ -314,7 +386,7 @@ packages:
description:
name: get
sha256: "2ba20a47c8f1f233bed775ba2dd0d3ac97b4cf32fc17731b3dfc672b06b0e92a"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.6.5"
go_router:
......@@ -322,7 +394,7 @@ packages:
description:
name: go_router
sha256: "2aa884667eeda3a1c461f31e72af1f77984ab0f29450d8fb12ec1f7bc53eea14"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "10.1.0"
http:
......@@ -330,7 +402,7 @@ packages:
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
http_client_helper:
......@@ -338,7 +410,7 @@ packages:
description:
name: http_client_helper
sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0"
http_parser:
......@@ -346,7 +418,7 @@ packages:
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.2"
image_picker:
......@@ -354,7 +426,7 @@ packages:
description:
name: image_picker
sha256: "9978d3510af4e6a902e545ce19229b926e6de6a1828d6134d3aab2e129a4d270"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.7+5"
image_picker_android:
......@@ -362,7 +434,7 @@ packages:
description:
name: image_picker_android
sha256: "39f2bfe497e495450c81abcd44b62f56c2a36a37a175da7d137b4454977b51b1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.9+3"
image_picker_for_web:
......@@ -370,7 +442,7 @@ packages:
description:
name: image_picker_for_web
sha256: "869fe8a64771b7afbc99fc433a5f7be2fea4d1cb3d7c11a48b6b579eb9c797f0"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
image_picker_ios:
......@@ -378,7 +450,7 @@ packages:
description:
name: image_picker_ios
sha256: fadafce49e8569257a0cad56d24438a6fa1f0cbd7ee0af9b631f7492818a4ca3
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.9+1"
image_picker_platform_interface:
......@@ -386,7 +458,7 @@ packages:
description:
name: image_picker_platform_interface
sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.9.3"
intl:
......@@ -394,7 +466,7 @@ packages:
description:
name: intl
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.18.0"
ionicons:
......@@ -402,7 +474,7 @@ packages:
description:
name: ionicons
sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.2"
js:
......@@ -410,15 +482,39 @@ packages:
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.7"
just_audio:
dependency: "direct main"
description:
name: just_audio
sha256: b607cd1a43bac03d85c3aaee00448ff4a589ef2a77104e3d409889ff079bf823
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.36"
just_audio_platform_interface:
dependency: transitive
description:
name: just_audio_platform_interface
sha256: c3dee0014248c97c91fe6299edb73dc4d6c6930a2f4f713579cd692d9e47f4a1
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.2"
just_audio_web:
dependency: transitive
description:
name: just_audio_web
sha256: "134356b0fe3d898293102b33b5fd618831ffdc72bb7a1b726140abdf22772b70"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.9"
lints:
dependency: transitive
description:
name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
logger:
......@@ -426,7 +522,7 @@ packages:
description:
name: logger
sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
logging:
......@@ -434,7 +530,7 @@ packages:
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
matcher:
......@@ -442,7 +538,7 @@ packages:
description:
name: matcher
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.15"
material_color_utilities:
......@@ -450,7 +546,7 @@ packages:
description:
name: material_color_utilities
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
meta:
......@@ -458,7 +554,7 @@ packages:
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
mime:
......@@ -466,7 +562,7 @@ packages:
description:
name: mime
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
nested:
......@@ -474,15 +570,23 @@ packages:
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0"
oktoast:
dependency: "direct main"
description:
name: oktoast
sha256: f1366c5c793ddfb8f55bc6fc3e45db43c45debf173b765fb4c5ec096cbdeb84a
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.4.0"
package_info_plus:
......@@ -490,7 +594,7 @@ packages:
description:
name: package_info_plus
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.0"
package_info_plus_platform_interface:
......@@ -498,7 +602,7 @@ packages:
description:
name: package_info_plus_platform_interface
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
path:
......@@ -506,7 +610,7 @@ packages:
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.3"
path_drawing:
......@@ -514,7 +618,7 @@ packages:
description:
name: path_drawing
sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
path_parsing:
......@@ -522,7 +626,7 @@ packages:
description:
name: path_parsing
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
path_provider:
......@@ -530,7 +634,7 @@ packages:
description:
name: path_provider
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
path_provider_android:
......@@ -538,7 +642,7 @@ packages:
description:
name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.2"
path_provider_foundation:
......@@ -546,7 +650,7 @@ packages:
description:
name: path_provider_foundation
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
path_provider_linux:
......@@ -554,7 +658,7 @@ packages:
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
......@@ -562,7 +666,7 @@ packages:
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
path_provider_windows:
......@@ -570,7 +674,7 @@ packages:
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
permission_handler:
......@@ -578,7 +682,7 @@ packages:
description:
name: permission_handler
sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.0.1"
permission_handler_android:
......@@ -586,7 +690,7 @@ packages:
description:
name: permission_handler_android
sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.1.0"
permission_handler_apple:
......@@ -594,7 +698,7 @@ packages:
description:
name: permission_handler_apple
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "9.1.4"
permission_handler_platform_interface:
......@@ -602,7 +706,7 @@ packages:
description:
name: permission_handler_platform_interface
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.12.0"
permission_handler_windows:
......@@ -610,15 +714,23 @@ packages:
description:
name: permission_handler_windows
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.3"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.4.0"
platform:
dependency: transitive
description:
name: platform
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.4"
plugin_platform_interface:
......@@ -626,15 +738,23 @@ packages:
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.7.4"
provider:
dependency: transitive
description:
name: provider
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.1"
pull_to_refresh_flutter3:
......@@ -642,7 +762,7 @@ packages:
description:
name: pull_to_refresh_flutter3
sha256: "223a6241067162dc15cf8c46c05af998ce7aa85e0703d8f696101eb1b5629d76"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
rational:
......@@ -650,7 +770,7 @@ packages:
description:
name: rational
sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.2"
recase:
......@@ -658,7 +778,7 @@ packages:
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.0"
rxdart:
......@@ -666,7 +786,7 @@ packages:
description:
name: rxdart
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.27.7"
scrollable_positioned_list:
......@@ -674,7 +794,7 @@ packages:
description:
name: scrollable_positioned_list
sha256: "9566352ab9ba05794ee6c8864f154afba5d36c5637d0e3e32c615ba4ceb92772"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.3"
shared_preferences:
......@@ -682,7 +802,7 @@ packages:
description:
name: shared_preferences
sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
shared_preferences_android:
......@@ -690,7 +810,7 @@ packages:
description:
name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
shared_preferences_foundation:
......@@ -698,7 +818,7 @@ packages:
description:
name: shared_preferences_foundation
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.5"
shared_preferences_linux:
......@@ -706,7 +826,7 @@ packages:
description:
name: shared_preferences_linux
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
shared_preferences_platform_interface:
......@@ -714,7 +834,7 @@ packages:
description:
name: shared_preferences_platform_interface
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
shared_preferences_web:
......@@ -722,7 +842,7 @@ packages:
description:
name: shared_preferences_web
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
shared_preferences_windows:
......@@ -730,7 +850,7 @@ packages:
description:
name: shared_preferences_windows
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
sky_engine:
......@@ -743,15 +863,31 @@ packages:
description:
name: source_span
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.3"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.11.0"
stream_channel:
......@@ -759,7 +895,7 @@ packages:
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
string_scanner:
......@@ -767,7 +903,7 @@ packages:
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
synchronized:
......@@ -775,7 +911,7 @@ packages:
description:
name: synchronized
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0+1"
term_glyph:
......@@ -783,7 +919,7 @@ packages:
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.1"
test_api:
......@@ -791,7 +927,7 @@ packages:
description:
name: test_api
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.1"
tobias:
......@@ -799,7 +935,7 @@ packages:
description:
name: tobias
sha256: "16cad7d60e910fa45498dacb852f393b065d4d0fce7feff4db43159d745c7c6e"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.3.0"
typed_data:
......@@ -807,7 +943,7 @@ packages:
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.2"
uuid:
......@@ -815,7 +951,7 @@ packages:
description:
name: uuid
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.7"
vector_math:
......@@ -823,7 +959,7 @@ packages:
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
win32:
......@@ -831,7 +967,7 @@ packages:
description:
name: win32
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.9"
xdg_directories:
......@@ -839,9 +975,17 @@ packages:
description:
name: xdg_directories
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
xml:
dependency: transitive
description:
name: xml
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.0"
sdks:
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0"
......@@ -103,6 +103,17 @@ dependencies:
tobias: ^3.3.0
# 内购
flutter_inapp_purchase: ^5.6.1
encrypt: ^5.0.3
# 阿里oss直传
flutter_oss_aliyun: ^6.4.1
just_audio: ^0.9.36
# 缓存
flutter_cache_manager: ^3.3.1
# 解压
archive: ^3.1.2
# 判断当前网络情况
connectivity_plus: ^5.0.2
dev_dependencies:
flutter_test:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论