需求1:发帖退出时提示是否保存草稿
需求2:每条草稿中可以保存多张图片(最多9张)或一条视频及三十来个其它参数
需求3:每条草稿都是可以被覆盖的、可以点击删除
需求4:草稿页面可以一键清空
需求5:草稿随app删除一起没掉
看到需求第一时间想到的就是存轻量级SharedPreferences中;
行动:将图片转为base64,然后将base64转json从而实现将图片存入轻量级
/** Asset类型图片转base64(其它类型替换能获取字节的对象)* */imageToUint8List(List<Asset> imageList) async {Completer<List<String>> _completer = Completer<List<String>>();List<String> dataList = [];if(imageList.length > 0){for (int i = 0; i < imageList.length; i++) {Asset asset = imageList[i];ByteData byteData = await asset.getByteData();var imageBase64 = base64.encode(Uint8List.view(byteData.buffer));dataList.add(imageBase64);}_completer.complete(dataList);}return _completer.future;}
结果:行不通。转换加储存极易造成内存泄漏且耗时太多(一张图片都有可能10mb以上而需求更是多图)
方法:
1.在app的file目录下新建一个文件夹保存草稿箱时把图片、视频文件复制进去。
2.把普通的字符串、数字等参数和图片、视频的文件路径封装转成json字符串一起存入轻量级
封装工具
import 'dart:convert';
import 'dart:io';import 'package:test/common/sp_util.dart';
import 'package:test/generated/l10n.dart';
import 'package:test/jade/bean/DraftsBean.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';class DraftsUtil{/** 保存草稿箱提示框* */draftsSaveDialog(context, {Function callback,Function cancelCallback}) async {showDialog(context: context,barrierDismissible: false,builder: (BuildContext context) {return CupertinoAlertDialog(content: SingleChildScrollView(child: ListBody(children: <Widget>[Padding(child: Text(S.current.tuichu,style: TextStyle(fontSize: 15, color: Color(0xffea2020)),),padding: EdgeInsets.only(top: 15),),SizedBox(height: 10),Padding(child: Text('是否保存编辑到草稿箱?',style: TextStyle(fontSize: 15, color: Color(0xff666666)),),padding: EdgeInsets.only(bottom: 15),)],),),actions: <Widget>[CupertinoDialogAction(child: Text(S.current.quxiao,style: TextStyle(fontSize: 14, color: Color(0xff999999)),),onPressed: () {if(cancelCallback != null){cancelCallback();}Navigator.pop(context, false);},),CupertinoDialogAction(child: Text('保存并退出',style: TextStyle(fontSize: 14, color: Color(0xff4ec6cc)),),onPressed: () {if (callback != null) {callback();}Navigator.pop(context, false);},)],);});}/** 读取草稿列表数据* */readDraftsList(var userId){List<DraftsBean> draftsList = [];List<Map<dynamic, dynamic>> getDraftsList = SpUtil.getObjectList('${userId}draftsList');if (getDraftsList != null && getDraftsList.length > 0) {getDraftsList.forEach((element) {draftsList.add(DraftsBean(type: element['type'],paramsData: element['paramsData'],time: element['time']));});}return draftsList;}/** 删除某一条草稿* */deleteDrafts(var userId,int index,{List<DraftsBean> draftsList}){if(draftsList == null){draftsList = [];List<Map<dynamic,dynamic>> _getDraftsList = SpUtil.getObjectList('${userId}draftsList');if (_getDraftsList != null && _getDraftsList.length > 0) {_getDraftsList.forEach((element) {draftsList.add(DraftsBean(type: element['type'],paramsData: element['paramsData'],time: element['time']));});}}var _paramsData = json.decode(draftsList[index].paramsData);List <dynamic> imagePathList = _paramsData['selectImageList'];String videoPath = _paramsData['selectVideoPath'];if(imagePathList != null && imagePathList.length>0){imagePathList.forEach((element) {var imageFile = File(element);if(imageFile.existsSync()){imageFile.deleteSync();}});}if(videoPath != null){var videoFile = File(videoPath);if(videoFile.existsSync()){videoFile.deleteSync();}}draftsList.removeAt(index);SpUtil.putObjectList('${userId}draftsList',draftsList);}//清空草稿箱clearDrafts(var userId) async {Directory libDir = await getExternalStorageDirectory();String draftsPath = libDir.path + '/draftsFiles';var directory = Directory(draftsPath);try{bool exists = await directory.exists();if(exists){directory.deleteSync(recursive: true);}}catch(e){print('删除文件夹出错:${e.toString()}');}SpUtil.remove('${userId}draftsList');}
}
草稿箱参数对象
class DraftsBean {int type; //草稿类型: 0普通发帖 1 A型发帖 2 B型发帖String paramsData;//保存的发帖参数 json字符串String time; //日期DraftsBean({this.type, this.paramsData,this.time});DraftsBean.fromJson(dynamic json) {type = json['type'];paramsData = json['paramsData'];time = json['time'];}Map<String, dynamic> toJson() {final map = <String, dynamic>{};map['type'] = type;map['paramsData'] = paramsData;map['time'] = time;return map;}
}
引用
//草稿箱提示框_backDrafts(context){if(logic.canSaveDrafts()){DraftsUtil().draftsSaveDialog(context,cancelCallback: (){Navigator.of(context).pop();},callback: () async {print('========点击了保存草稿');//当前时间DateTime nowTime = DateTime.now();var _dateFormatStr = DateUtil.formatDate(nowTime, format: DateFormats.y_mo_d);state.selectImagePathList.clear();state.selectVideoPath = null;List<DraftsBean> draftsList = DraftsUtil().readDraftsList(logic.userInfo.user.id);if(state.selectImageList.length>0){state.selectImagePathList = await Utils().copyFile(state.selectImageList);}else if(state.videoPath != null){List<String> videoPathList = await Utils().copyFile([File(state.videoPath)]);state.selectVideoPath = videoPathList.first;}if(widget.draftsIndex != null ){//从草稿箱进来的,传递了草稿的index,先删除再插入用来覆盖该条DraftsUtil().deleteDrafts(logic.userInfo.user.id, widget.draftsIndex,draftsList: draftsList);List<DraftsBean> latestDraftsList = DraftsUtil().readDraftsList(logic.userInfo.user.id);latestDraftsList.insert(widget.draftsIndex, DraftsBean(type: logic.state.type == 0? 1:2,paramsData: json.encode(logic.paramsData()),time: '$_dateFormatStr',));SpUtil.putObjectList('${logic.userInfo.user.id}draftsList',latestDraftsList);}else{draftsList.insert(0, DraftsBean(type: logic.state.type == 0? 1:2,paramsData: json.encode(logic.paramsData()),time: '$_dateFormatStr',));SpUtil.putObjectList('${logic.userInfo.user.id}draftsList',draftsList);}ATuiEventBusTool.shared().fire(CommonConfig.draftsPageRefresh);Navigator.of(context).pop();});}else{Navigator.of(context).pop();}}
草稿箱列表页面
import 'dart:convert';
import 'dart:io';
import 'package:test/common/sp_util.dart';
import 'package:test/jade/bean/DraftsBean.dart';
import 'package:test/jade/configs/CommonConfig.dart';
import 'package:test/jade/configs/PathConfig.dart';
import 'package:test/jade/customWidget/EmptyWidget.dart';
import 'package:test/jade/homePage/wantHave/WantPost.dart';
import 'package:test/jade/homePage/wantHave/wantHaveLogic/WantHaveLogic.dart';
import 'package:test/jade/homePage/wantHave/wantHaveLogic/WantHavePostBinding.dart';
import 'package:test/jade/mine/drafts/DraftsUtil.dart';
import 'package:test/jade/mine/draftsVideoItemView.dart';
import 'package:test/jade/utils/JadeColors.dart';
import 'package:test/jade/utils/Utils.dart';
import 'package:test/model/user/user_info_model.dart';
import 'package:test/pages/user_share/edit/push_share_edit.dart';
import 'package:test/util/eventbustool.dart';
import 'package:test/util/navigator_util.dart';
import 'package:test/widget/custom_appbar.dart';
import 'package:city_pickers/city_pickers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';class DraftsPage extends StatefulWidget{State<StatefulWidget> createState() {// TODO: implement createStatereturn _DraftsPage();}
}class _DraftsPage extends State<DraftsPage>{String _userId;List<DraftsBean> _draftsList = [];var _eventBus;void initState() {// TODO: implement initStatesuper.initState();_userId = Provider.of<UserInfoModel>(context, listen: false).user?.id;_draftsList = DraftsUtil().readDraftsList(_userId);_eventBus = ATuiEventBusTool.shared().on().listen((event) {if(event == CommonConfig.draftsPageRefresh){_draftsList = DraftsUtil().readDraftsList(_userId);setState(() {});}});}void dispose() {// TODO: implement disposeif(_eventBus != null){_eventBus.cancel();_eventBus = null;}super.dispose();}Widget build(BuildContext context) {// TODO: implement buildreturn Scaffold(backgroundColor: JadeColors.lightGrey,appBar: CustomAppBar(elevation: 0.5,leading: GestureDetector(onTap: () {Navigator.of(context).pop();},child: Icon(Icons.arrow_back_ios,color: Colors.black,),),title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [Container(),Container(margin: EdgeInsets.only(right: 34.w),child: Text('草稿箱',style: TextStyle(color: Colors.black,fontSize: 38.sp,fontWeight: FontWeight.w400),),),GestureDetector(child: Container(color: Colors.transparent,padding: EdgeInsets.only(left: 25.w,top: 6.w,bottom: 6.w),child: Text('清空',style: TextStyle(color: JadeColors.grey_2,fontSize:32.sp),),),onTap: () async {var bool = await Utils.commonDialog(context,"是否清空草稿?");if (bool) {DraftsUtil().clearDrafts(_userId);setState(() {_draftsList.clear();});}},)],)),body: _draftsList.length>0?_body(): EmptyLayout(description: '没有草稿~',),);}_body(){return Column(children: [Container(margin: EdgeInsets.only(left: 20.w,right: 20.w,top: 24.w,bottom: 40.w),alignment: Alignment.centerLeft,child: Text('草稿在APP卸载后会被删除,请及时发布。',style: TextStyle(color: JadeColors.grey_2,fontSize: 24.sp,),)),Expanded(child: Container(margin: EdgeInsets.symmetric(horizontal: 20.w,),padding: EdgeInsets.only(bottom: 20.w),child: MasonryGridView.count(crossAxisCount: 2,itemBuilder: (BuildContext context, int index){var data = _draftsList[index].paramsData;var decodeData = json.decode(data);return _itemView(index,decodeData);},mainAxisSpacing: 6.0,crossAxisSpacing: 6.0,itemCount: _draftsList.length,shrinkWrap: true,)))],);}_itemView(int index,var paramsData){return GestureDetector(child: Container(decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(10)),child: Column(children: [if(paramsData['selectImageList'] != null && paramsData['selectImageList'].isNotEmpty)ClipRRect(borderRadius: BorderRadius.only(topLeft: Radius.circular(10),topRight: Radius.circular(10)),child: Image.file(File(paramsData['selectImageList'][0]),width: double.infinity,height: index % 2 == 1?500.w : 400.w,fit: BoxFit.cover,frameBuilder: (context, child, frame, wasSynchronouslyLoaded){if(wasSynchronouslyLoaded){return child;}return AnimatedOpacity(child: child,opacity: frame == null ? 0 : 1,duration: const Duration(seconds: 4),curve: Curves.easeOut,);}),),if(paramsData['selectVideoPath'] != null)DraftsVideoItemView(mediaUrl: paramsData['selectVideoPath'],currentIndex: index,),Container(margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 20.w,bottom: 20.w),alignment: Alignment.centerLeft,child: Text('${paramsData['articleContent']??''}',style: TextStyle(fontSize: 26.sp,fontWeight: FontWeight.w600),maxLines: 2,overflow: TextOverflow.ellipsis)),Container(margin: EdgeInsets.only(left: 20.w,right: 20.w,bottom: 20.w),child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [Text('${_draftsList[index].time}',style: TextStyle(fontSize: 20.sp,color: JadeColors.grey)),GestureDetector(child: Container(width: 32.w,height: 32.w,color: Colors.transparent,padding: EdgeInsets.all(2),child: Image.asset(PathConfig.iconDeleteGrey),),onTap: () async {var bool = await Utils.commonDialog(context,"是否删除草稿?");if (bool) {DraftsUtil().deleteDrafts(_userId,index,draftsList:_draftsList);setState(() {});}},)],))],)),onTap: (){switch(_draftsList[index].type){case 0:NavigatorUtil.push(PushShareEdit(draftsJson: _draftsList[index].paramsData,draftsIndex: index,));break;case 1:Get.to(() => WantPost(),binding: WantHavePostBinding(), arguments: {'type':0,'draftsParamsJson': _draftsList[index].paramsData, //草稿箱json数据'draftsIndex': index //草稿index});break;case 2:Get.to(() => WantPost(),binding: WantHavePostBinding(), arguments: {'type':1,'draftsParamsJson': _draftsList[index].paramsData, //草稿箱json数据'draftsIndex': index //草稿index});break;}},);}
}