提交 a4d27b1e authored 作者: yueweilu's avatar yueweilu

优化阅读页代码

上级 9bfd0e61
......@@ -11,11 +11,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
final BookDetailModel bookDetailModel;
// 笔记id 用于我的笔记跳转阅读页对应位置
final String noteId;
ReadController({required this.bookId, required this.chapterId,required this.chapterName,required this.bookDetailModel,required this.noteId});
late InAppWebViewController webViewController;
// 目录
List <ChapterModel> chapters = [];
// 工具栏数组
......@@ -26,7 +23,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
];
// 默认工具栏选项
late ToolModel toolModel = tools.first;
// 输入框窗口是否展示
bool showChat = false;
// 0 讨论 1 笔记
......@@ -50,42 +46,33 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
List <AudioModel> discussInputAudios= [];
// 笔记标题
String noteTitle = '';
bool _show = false;
bool get show => _show;
// 录音
final FlutterSoundRecorder _mRecorder = FlutterSoundRecorder(logLevel:Level.error);
// 录音开始
bool startRecording = false;
// 是否存在离线文件
bool existDownFile= false;
// 网络状态
bool netStatus = false;
// 当前html名称 0-318.html
late String currentHtmlName = '';
// 朗读组件
late FlutterTts flutterTts;
// 是否展示搜索结果
bool showSearch = false;
final int _searchLimit = 10;
int _searchPage = 1;
bool _searchNoMore = false;
// 搜全文
List<SearchAllModel> searchALlResults = [];
// 刷新组件
final EasyRefreshController refreshController = EasyRefreshController(
controlFinishLoad: true,
controlFinishRefresh: true,
);
// 搜全文输入控制器
late TextEditingController searchInput = TextEditingController();
// 音频
......@@ -140,6 +127,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
///------------------------------------------ 页面 生命周期--------------------------------------------------------
// 选择了底部工具栏
void chooseTool(ToolModel selectedModel){
for (var model in tools) {
// 如果当前遍历到的工具是选中的,并且不是点击的工具,则取消选中
......@@ -169,6 +157,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
update();
}
// 是否显示搜索组件
void setShowSearch(bool show){
if(show == false){
searchInput.text = '';
......@@ -185,25 +174,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
update();
}
// 读取那个章节
void readLocalHtml(String chapterId) async {
// 判断是否有离线文件 如果有使用离线阅读
final exist = await _isExistFile(bookId);
if (netStatus && exist){
// 没有网并且有离线文件 离线阅读
String path = await _getDirectory();
String finalPath = '$path/$bookId/0-318.html';
final content = await readHtmlFileContent(finalPath);
Console.log('原始内容-----------------$content');
String htmlStr = EncryptUtil.aesDecrypt(content!);
Console.log('解密-----------------$htmlStr');
Console.log('-------------使用本地文件-------------------');
webViewController.loadData(data: htmlStr);
}
}
// 初始化朗读组件
initTts() {
flutterTts = FlutterTts();
......@@ -248,6 +218,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
Future _setAwaitOptions() async {
await flutterTts.awaitSpeakCompletion(true);
}
// 获取默认引擎
Future _getDefaultEngine() async {
var engine = await flutterTts.getDefaultEngine;
if (engine != null) {
......@@ -255,6 +227,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
}
// 获取默认声音
Future _getDefaultVoice() async {
var voice = await flutterTts.getDefaultVoice;
if (voice != null) {
......@@ -262,6 +235,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
}
// 朗读
Future speak(String text) async {
await flutterTts.setVolume(0.5);
await flutterTts.setSpeechRate(0.5);
......@@ -300,6 +274,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
androidWillPauseWhenDucked: true,
));
}
// 开启录音
void record() async {
await openTheRecorder();
......@@ -324,6 +299,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// });
update();
}
// 停止录音
void stopRecorder() async{
startRecording = false;
......@@ -341,15 +317,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
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();
......@@ -357,11 +324,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
return 'voice_$formattedDate.mp4';
}
// 添加语音路径
void addVoiceFilePath(){
}
// 重置所有信息
void reset(){
clearAllDiscussInput();
......@@ -369,14 +331,10 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 播放音频
void playAudio(AudioModel audioModel){
Console.log('-------------播放开始-------------------');
if(audioPlayer.playerState.playing){
audioPlayer.stop();
audioModel.currentDuration = '0:00:00';
// if(currentPlayMediaModel.id == mediaModel.id){
// return;
// }
}
// 本地音频
audioPlayer.setFilePath(audioModel.path);
......@@ -394,30 +352,35 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
});
// currentPlayMediaModel = mediaModel;
}
// 添加讨论图片
void addDiscussInputImages(String path){
discussInputImages.add(path);
Console.log('discussInputImages--------------------------------$path');
update();
}
// 删除讨论图片
void delDiscussInputImages(String path){
discussInputImages.remove(path);
Console.log('delDiscussInputImages--------------------------------$path');
update();
}
// 清空讨论图片
void clearDiscussInputImages(){
discussInputImages.clear();
Console.log('clearDiscussInputImages--------------------------------');
update();
}
// 情况语音
void clearDiscussAudios(){
discussInputAudios.clear();
Console.log('clearDiscussAudios--------------------------------');
update();
}
// 清空所有已经填写的数据
void clearAllDiscussInput(){
clearDiscussInputImages();
......@@ -429,13 +392,13 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
Console.log('clearAllDiscussInput--------------------------------');
update();
}
// 删除音频
void delAudio(AudioModel audioModel){
discussInputAudios.remove(audioModel);
update();
}
// 上传文件
Future<String> upload({
required String path
......@@ -469,7 +432,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
return false;
}
}
// 有网情况下 先直传oss 获取到url
if (status){
CustomToast.loading();
......@@ -478,7 +440,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
final url = await upload(path: path);
images.add(url);
}
// 循环上传音频获取地址
for(AudioModel model in discussInputAudios){
final url = await upload(path: model.path);
......@@ -498,7 +459,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
}
Map<String,dynamic> contentMap = {
'text':contentInput.text,
'audio':audios,
......@@ -547,7 +507,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
'note_content': jsonEncode(contentMap),
'notes_id': 0,
};
result = await SqlManager.insertData(data);
}
......@@ -557,13 +516,10 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
else{
Toast.show('笔记发表失败');
}
// 重置所有信息
reset();
setShowChat(false);
return result;
}
// 发表评论
......@@ -592,10 +548,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
// AnimationController get controller => _controller;
void _onCommentFocusChanged() {
if (discussTitleFocusNode.hasFocus || discussContentFocusNode.hasFocus) {
setShowChat(true);
......@@ -612,23 +564,18 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
update();
}
// 显示输入框类型
void setChatType(int type){
chatType = type;
}
// 设置笔记是否公开
void setIsPublic(){
isPublic = !isPublic;
update();
}
// void setNoteTitle(String title){
// noteTitle = title;
// // update();
// }
///------------------------------------------离线逻辑--------------------------------------------------------
// 下载文件
......@@ -682,6 +629,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
// 获取对应chapterId文件路径
Future<String> getLocalReadHtml(String chapterId) async{
String docPath = await Tools.getDirectory();
String filePath = '$docPath/$bookId';
......@@ -697,42 +645,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
}
return '';
}
// 开始阅读离线
// void read(String toReadHtmlPath) async {
// // 3、获取 离线文件的内容
// final content = await readHtmlFileContent(toReadHtmlPath);
// // 4、解密离线的内容
// String htmlStr = EncryptUtil.aesDecrypt(content!);
//
// // webViewController.evaluateJavascript(source: 'callbackInFlutterComponent($htmlStr);');
//
// // 5、将离线内容写入临时文件
// String directoryPath = path.dirname(toReadHtmlPath);
// String writeFilePath = '$directoryPath/read.html';
// // 6、创建临时文件
// File file = File(writeFilePath);
// // 7、将HTML内容写入文件
// await file.writeAsString(htmlStr);
//
// localHtml5Path = writeFilePath;
// String url = writeFilePath;
// if(Platform.isIOS){
// url = 'file://$writeFilePath';
// }
// webViewController.loadUrl(urlRequest: URLRequest(
// // url: Uri.parse(writeFilePath),
// url: WebUri.uri(Uri.parse(url))
// ));
//
// final result = await SqlManager.updateReadHistoryByBookId(int.parse(bookId), int.parse(chapterId));
// Console.log('Sql----readread---存入数据库读到的章节----------------book_id:$bookId-----chapterId:$chapterId---------result:$result--');
//
// queryLocalNote();
// }
// 获取离线数据
void getOffLineInfo() async {
Console.log('-chapterId----------------------$chapterId-----------------------------------------');
......@@ -746,7 +660,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 3、解密离线的内容
String deCodeContent = EncryptUtil.aesDecrypt(enCodeContent!);
data['content'] = deCodeContent;
// Console.log('deCodeContent-----$deCodeContent');
// 4、获取上一章节信息
Map<String, dynamic> upChapter = {};
final upId = await getChapterId(type: 0);
......@@ -778,17 +691,13 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
data['color_line'] = temp['color_line'];
// 7、 给前端参数
Console.log('data------$data');
String jsonStr = jsonEncode(data);
Console.log('callbackInFlutterComponent--------------------------------$jsonStr');
// webViewController.reload();
webViewController.evaluateJavascript(source: 'callbackInFlutterComponent($jsonStr)');
final result = await SqlManager.updateReadHistoryByBookId(int.parse(bookId), int.parse(chapterId));
Console.log('Sql----readread---存入数据库读到的章节----------------book_id:$bookId-----chapterId:$chapterId---------result:$result--');
// webViewController.reload();
}
// 获取上一章节或下一章节 id
......@@ -830,59 +739,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
return '';
}
// 本地阅读 读取上一章节 或 下一章节
// 0 上一章节
// 1 下一章节
// Future<void> readChapter({required int type}) async {
// Console.log('---------------获取所有html---------------------------------');
//
// String docPath = await _getDirectory();
// String filePath = '$docPath/$bookId';
// Directory directory = Directory(filePath);
// // 获取目录下的所有文件
// List<FileSystemEntity> files = directory.listSync(recursive: true);
//
// int findIndex = int.parse(currentHtmlName.split('-').first);
// if(type == 0){
// findIndex--;
// if(findIndex <0){
// Toast.show('前面已没有章节');
// // 已到最前
// return ;
// }
// }
// else{
// findIndex++;
// if(findIndex >files.length -1){
// // 已到最后
// return ;
// }
// }
//
// String toReadHtmlPath = '';
//
// // 打印所有 HTML 文件路径
// for (var file in files) {
// if (file is File && file.path.toLowerCase().endsWith('.html')) {
// String fileName = path.basenameWithoutExtension(file.path);
// if (int.parse(fileName.split('-').first) == findIndex){
// Console.log('HTML File--------------------------------${file.path}');
// toReadHtmlPath = file.path;
// chapterId = fileName.split('-').last;
// chapterName = getChapterName(chapterId);
//
// break;
// }
// }
// }
//
// // 开始读书
// read(toReadHtmlPath);
//
// update();
//
// }
// 读取html内容
Future<String?> readHtmlFileContent(String filePath) async {
try {
......@@ -894,7 +750,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
Console.log('Error reading file: $e');
return '';
}
}
// 通过 chapter_id 获取 chapter_name
......@@ -910,7 +765,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
}
}
}
return '';
}
......@@ -926,6 +780,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
Console.log('前端-----------queryLocalNote---------------------$result');
// webViewController.evaluateJavascript(source: 'querySuccessCallBack($result)');
}
// 本地添加划线高亮笔记
void addLocalNote(Map<String, dynamic> data) async {
data['book_id'] = int.parse(bookId);
......@@ -935,12 +790,14 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
Console.log('前端-----------addLocalNote---------------------$result');
webViewController.evaluateJavascript(source: 'addSuccessCallBack($result)');
}
// 本地删除划线高亮笔记
void delLocalNote({required int noteId,required int id}) async {
final result = await SqlManager.delLocalNote(noteId: noteId,id: id);
Console.log('前端-----------delLocalNote---------------------$result');
// webViewController.evaluateJavascript(source: 'delSuccessCallBack($result)');
}
// 修改本地划线高亮笔记
void updateLocalNote({required int notesId,required int id,required Map<String, dynamic> data}) async {
final result = await SqlManager.updateLocalNote(notesId: notesId,id: id, data: data);
......@@ -948,20 +805,13 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// webViewController.evaluateJavascript(source: 'updateSuccessCallBack($result)');
}
//获取存储目录
Future<String> _getDirectory() async {
// getTemporaryDirectory
final directory = await getExternalStorageDirectory();
return directory!.path;
}
/// 刷新token
// 刷新token
Future<String?> refreshToken() async {
final result = await CommonAPI.refreshToken();
return result;
}
/// 获取目录信息
// 获取目录信息
void _getChapters() async {
chapters = await LibraryAPI.chapters(bookId: bookId);
writeCurrentReadChapterIdToData(chapters);
......@@ -979,12 +829,10 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
writeCurrentReadChapterIdToData(cModel.children!);
}
}
ChapterModel? tModel = findChapterById(chapters, int.parse(chapterId));
if(tModel != null){
updateParentsStatus(chapters, tModel);
}
}
ChapterModel? findChapterById(List<ChapterModel> data,num id){
......@@ -1021,38 +869,26 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
return null;
}
/// 添加阅读时长
// 添加阅读时长
void _addReadTime({required type}) async{
final status = await Tools.checkCurrentNetStatus();
if(status){
LibraryAPI.addReadTime(bookId: bookId, readTypes: type);
}
}
/// 获取离线文件路径
// 获取离线文件路径
void getBookDown() async{
final exit = await _isExistFile(bookId);
// 存在离线文件
if (exit){
}
else{
if (!exit){
CustomToast.loading();
final result = await LibraryAPI.getbookDownloadParam(bookId: bookId);
Console.log('----------_getBookDown------------------${result.download}');
// 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> searchAll([bool isRefresh = false]) async {
if (isRefresh) _searchPage = 1;
// 网路请求
......@@ -1096,7 +932,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
}
///------------------------------------------ app 生命周期--------------------------------------------------------
// 当应用程序从后台切换到前台并变为活动状态时调用。这通常在用户从其他应用程序返回到你的应用程序时发生
......
......@@ -71,7 +71,6 @@ class _ReadPageState extends State<ReadPage> {
child: Stack(
children: [
InAppWebView(
// initialFile:''assets/html/read_unline.html'',
initialFile: readController.existDownFile && !readController.netStatus?'assets/html/read_unline.html':'assets/html/read.html',
// initialUrlRequest:URLRequest(
// url:WebUri.uri(Uri.parse(kReadTestUnderLineBook))
......@@ -82,17 +81,10 @@ class _ReadPageState extends State<ReadPage> {
// http的请求也不做限制
mixedContentMode:MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW
),
// initialOptions: InAppWebViewGroupOptions(
// crossPlatform: InAppWebViewOptions(
// clearCache: true
// )
// ),
contextMenu: ContextMenu(
// options: ContextMenuOptions(hideDefaultSystemContextMenuItems: true),
settings: ContextMenuSettings(
hideDefaultSystemContextMenuItems: true,
)
settings: ContextMenuSettings(
hideDefaultSystemContextMenuItems: true,
)
),
onWebViewCreated: (InAppWebViewController controller){
// CustomToast.loading();
......@@ -103,30 +95,22 @@ class _ReadPageState extends State<ReadPage> {
Console.log("Received message from WebView-----------------------------: ${consoleMessage.message}");
},
onLoadStop: (controller, url) {
// flutter 主动给 js 传参数
// String str = '$kServerUrl,${readController.bookId},${readController.chapterId},${UserStore.to.token},${readController.noteId},${readController.sModel.bookId =='0'?'':readController.sModel.combinedContent}';
// controller.evaluateJavascript(source: 'callbackInFlutterComponent("$str");');
// Console.log('传给前端的参数--------------------------------$str');
// String jsonStr = jsonEncode(param);
// controller.evaluateJavascript(source: 'callbackInFlutterComponent($jsonStr)');
if(readController.existDownFile && !readController.netStatus){
String temp = readController.existDownFile && !readController.netStatus?'assets/html/read_unline.html':'assets/html/read.html';
Console.log('-----------------加载离线数据---------------$temp');
Console.log('-----------------加载离线数据---------------');
readController.getOffLineInfo();
}
else {
// controller.loadFile(assetFilePath: 'assets/html/read.html');
Console.log('-----------------加载在线数据---------------');
String str = '$kServerUrl,${readController.bookId},${readController.chapterId},${UserStore.to.token},${readController.noteId},${readController.sModel.bookId =='0'?'':readController.sModel.combinedContent}';
controller.evaluateJavascript(source: 'callbackInFlutterComponent("$str");');
}
// 添加单击事件
controller.evaluateJavascript(source: '''
document.addEventListener('click', function() {
window.flutter_inappwebview.callHandler('onTap');
});
''');
// 监听js单击回调
controller.addJavaScriptHandler(handlerName: 'onTap', callback: (args){
readController.setShow(readController.show);
......@@ -158,12 +142,6 @@ class _ReadPageState extends State<ReadPage> {
controller.addJavaScriptHandler(handlerName: 'readCallBack', callback: (args){
Console.log('监听朗读回调------------------------------------------------$args');
readController.speak(args.first.toString());
// readController.speak('中国共产党(英文名:the Communist Party of China,简写CPC),'
// '创建于1921年7月23日,1921年中国共产党成立后,确立了新民主主义革命的正确道路,让灾难深重的中'
// '国人民看到了新的希望、有了新的依靠。我们党探索出农村包围城市、武装夺取政权的正确革命道路,“唤起工'
// '农千百万”“夺过鞭子揍敌人”,经过土地革命战争、抗日战争、解放战争,推翻了压在中国人民头上的帝国主义、'
// '封建主义、官僚资本主义“三座大山”,建立了人民当家作主的中华人民共和国,彻底结束了近代以来中国内忧外患、积贫积弱的悲惨境地,开启了中华民族发展进步的新纪元'
// );
});
// 监听讨论回调
......@@ -174,6 +152,7 @@ class _ReadPageState extends State<ReadPage> {
readController.noteTitle = args.first.toString();
});
// 答题和答题结果页回调
controller.addJavaScriptHandler(handlerName: 'answerResultCallBack', callback: (args){
Console.log('监听答题回调------------------------------------------------$args');
......@@ -245,6 +224,7 @@ class _ReadPageState extends State<ReadPage> {
Console.log('监听画廊 扩展于都---------------给页面传参---------------------------------$params');
context.pushNamed(Routes.readInfo,queryParameters: params);
});
// 图片预览
controller.addJavaScriptHandler(handlerName: 'scaleImageCallback', callback: (args){
String url = args.first[0].toString();
......@@ -274,23 +254,13 @@ class _ReadPageState extends State<ReadPage> {
});
/// 离线需要参数
// //
// Map<String, dynamic> param111 = {
// 'book_id': readController.bookId,
// 'chapter_id': readController.chapterId,
// 'token':UserStore.to.token
// };
// String jsonStr = jsonEncode(param111);
// controller.evaluateJavascript(source: 'offlineCallbackInFlutterComponent($jsonStr)');
// // controller.evaluateJavascript(source: 'callbackInFlutterComponent("$str");');
// 添加高亮划线笔记
controller.addJavaScriptHandler(handlerName: 'offlineAddNoteCallBack', callback: (args){
Console.log('添加笔记回调------------------------------------------${args[0]}');
Map<String,dynamic> data = args[0];
readController.addLocalNote(data);
});
// 删除高亮划线笔记
controller.addJavaScriptHandler(handlerName: 'offlineDelNoteCallBack', callback: (args){
Console.log('删除笔记回调------------------------------------------${args[0]}');
......@@ -316,26 +286,6 @@ class _ReadPageState extends State<ReadPage> {
}
readController.updateLocalNote(notesId: notesId,id:id, data: data);
});
// 查询高亮划线笔记
// controller.addJavaScriptHandler(handlerName: 'offlineQueryNoteCallBack', callback: (args){
//
// });
// // 上一节下一节
// controller.addJavaScriptHandler(handlerName: 'offlineReadNoteCallBack', callback: (args){
// int type = 0;
// // 上一节
// if (type == 0){
// readController.readChapter(type: 0);
// }
// // 下一节
// else{
// readController.readChapter(type: 1);
// }
//
// });
},
),
Positioned(
......@@ -405,7 +355,7 @@ class _ReadPageState extends State<ReadPage> {
}
/// 目录 笔记 讨论 工具栏
// 目录 笔记 讨论 工具栏
Widget _createToolBar(ReadController controller){
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
......@@ -439,7 +389,6 @@ class _ReadPageState extends State<ReadPage> {
);
}
Widget detail(ReadController controller,ToolModel model){
if(model.tag == 0){
return ReadCategoryPage(controller: controller,
......@@ -508,7 +457,7 @@ class _ReadPageState extends State<ReadPage> {
}
return const SizedBox();
}
/// 目录、评论、笔记 背景
// 目录、评论、笔记 背景
Widget _showContent(ReadController controller,ToolModel model) {
if (controller.show){
if(model.selected){
......@@ -540,12 +489,10 @@ class _ReadPageState extends State<ReadPage> {
}
return const SizedBox();
}
}
/// 定制 悬浮按钮位置
// 定制 悬浮按钮位置
class MyFloatingActionButtonLocation extends FloatingActionButtonLocation {
@override
Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论