flutter简单的本地草稿箱功能

请添加图片描述

需求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;}},);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/83946.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用免费的敏捷研发管理工具管理端到端敏捷研发流程

Leangoo领歌是Scrum中文网&#xff08;scrum.cn&#xff09;旗下的一款永久免费的敏捷研发管理工具。 Leangoo领歌覆盖了敏捷研发全流程&#xff0c;它提供端到端敏捷研发管理解决方案&#xff0c;包括小型团队敏捷开发&#xff0c;规模化敏捷SAFe&#xff0c;Scrum of Scrums…

D. Boris and His Amazing Haircut

Problem - D - Codeforces 问题描述&#xff1a;剪发&#xff0c;将数组a减为数组b&#xff0c;有m个剪刀&#xff0c;每个剪刀只可以用一次且可以在任意区间内剪发&#xff0c;将长度大于mi的减为mi。现在有m数组&#xff0c;数组元素是第i个剪刀可以剪到mi&#xff0c;问能否…

项目运行报错:error:0308010C:digital envelope routines::unsupported

node版本升到18之后&#xff0c;运行老项目报错 运行命令&#xff1a;npm run dev 解决办法&#xff1a; 第一步&#xff1a;在运行命令中补充set NODE_OPTIONS–openssl-legacy-provider & 第二步&#xff1a;如果依然报错&#xff0c;在终端中运行set NODE_OPTIONS–ope…

Android Media3 ExoPlayer 开启缓存功能

ExoPlayer 开启播放缓存功能&#xff0c;在下次加载已经播放过的网络资源的时候&#xff0c;可以直接从本地缓存加载&#xff0c;实现为用户节省流量和提升加载效率的作用。 方法一&#xff1a;采用 ExoPlayer 缓存策略 第 1 步&#xff1a;实现 Exoplayer 参考 Exoplayer 官…

【SpringBoot项目】SpringBoot+MyBatis+MySQL电脑商城

在b站听了袁老师的开发课&#xff0c;做了一点笔记。 01-项目环境搭建_哔哩哔哩_bilibili 基于springboot框架的电脑商城项目&#xff08;一&#xff09;_springboot商城项目_失重外太空.的博客-CSDN博客 项目环境搭建 1.项目分析 1.项目功能:登录、注册、热销商品、用户管…

Redis之hash类型

文章目录 Redis之hash类型1. 设置一个字段/获取一个字段2. 获取所有字段值3. 判断字段是否存在4. 设置多个字段/获取多个字段5. 只获取字段名/字段值6. 获取某个key内全部数量7. 增加数字8. 删除key内字段9. 字段不存在时赋值10. 应用场景 Redis之hash类型 redis的hash类型&…

postman接口传参案例

目录 案例1&#xff1a; 接口A 接口B 案例2&#xff1a; //断言 案例1&#xff1a; 接口A 根据返回值需要从返回值中提取userid值&#xff0c;在Tests标签栏下编写脚本 //获取返回的响应值&#xff0c;并转化为json格式 var jsonData pm.response.json(); // 获取返回…

Transformer 01(自注意机制Self-attention)

一、Self-attention [台大李宏毅] 1.1 向量序列的输入 一个序列作为输入&#xff1a; 多个向量输入举例&#xff1a; 一个句子&#xff1a; 声音信号&#xff1a; 图&#xff1a; 1.2 输出 二、Sequence labeling 输入与输出一样多&#xff1a;Sequence labeling 窗口开的…

DDR模块电路的PCB设计建议

DDR电路简介 RK3588 DDR 控制器接口支持 JEDEC SDRAM 标准接口&#xff0c;原理电路16位数据信号如图8-1所示&#xff0c;地址、控制信号如图8-2所示&#xff0c;电源信号如图8-3所示。电路控制器有如下特点&#xff1a; 1、兼容 LPDDR4/LPDDR4X/LPDDR5 标准&#xff1b; 2、…

[补题记录] Atcoder Beginner Contest 309(E)

URL&#xff1a;https://atcoder.jp/contests/abc309 目录 E Problem/题意 Thought/思路 解法一&#xff1a; 解法二&#xff1a; Code/代码 E Problem/题意 一个家庭有 N 个人&#xff0c;根节点为 1&#xff0c;给出 2 ~ N 的父节点。一共购买 M 次保险&#xff0c;每…

数据包络分析(DEA)——CCR模型

写在前面&#xff1a; 博主本人大学期间参加数学建模竞赛十多余次&#xff0c;获奖等级均在二等奖以上。为了让更多学生在数学建模这条路上少走弯路&#xff0c;故将数学建模常用数学模型算法汇聚于此专栏&#xff0c;希望能够对要参加数学建模比赛的同学们有所帮助。 目录 1. …

Java基于微信小程序的青少年健康心理科普平台

第一章 简介 青少年心理健康科普平台为用户提供心理医生咨询服务&#xff0c;系统包括微信小程序端和后台。 微信小程序用户可以先进行注册&#xff0c;填写个人的基本信息提交到服务器&#xff0c;服务器把数据保存到数据库。管理员对青少年的信息进行验证后&#xff0c;青少…

Fedora Linux 39 Beta 预估 10 月底发布正式版

Fedora 39 Beta 镜像于今天发布&#xff0c;用户可以根据自己的使用偏好&#xff0c;下载 KDE Plasma&#xff0c;Xfce 和 Cinnamon 等不同桌面环境版本&#xff0c;正式版预估将于 10 月底发布 Fedora 39 Beta 版本主要更新了 DNF 软件包管理器&#xff0c;并优化了 Anaconda …

ASfP: 增强AOSP平台开发的利器——Android Studio for Platform

ASfP: 增强AOSP平台开发的利器——Android Studio for Platform Android Studio for Platform (ASfP) 是一个为使用 Soong 构建系统构建的 Android 开源项目&#xff08;AOSP&#xff09;平台开发者而设计的 Android Studio IDE 版本。与标准 Android Studio 不同&#xff0c;…

【Zabbix监控一】zabbix的原理与安装

利用一个优秀的监控软件&#xff0c;我们可以: ●通过一个友好的界面进行浏览整个网站所有的服务器状态 ●可以在 Web 前端方便的查看监控数据 ●可以回溯寻找事故发生时系统的问题和报警情况 总结&#xff1a;zabbix主要功能 监控&#xff0c;cpu负载&#xff0c;内存使用&a…

IT行业未来三年最靠谱的职业方向选择,一定要看完!

近些年“互联网”模式不断发展&#xff0c;以信息化带动传统产业的升级中&#xff0c;社会对IT互联网人才的需求量也在不断增加。随着AI、大数据、人工智能、云计算的兴起&#xff0c;也为对新兴事物充满兴趣热爱和探索的年轻人带来了更多的就业机会&#xff0c;在很大程度上激…

Nginx rewrite+防盗链

Nginx Nginx6、重写功能rewrite6.1 if指令6.2 return6.3 set指令6.4 break指令6.5 rewrite指令6.5.1 基本原理6.5.2 语法格式6.5.3 举例6.5.3.1 测试访问bj跳转到beijing6.5.3.2 域名重定向&#xff1a;所有域名都跳转到accp 7、防盗链7.1 什么是防盗链7.2 防盗链简介7.3 实现防…

《研发效能(DevOps)工程师国家职业技术认证》工信部教考中心认证证书:塑造研发效能的黄金标准丨IDCF

随着科技的飞速发展和市场竞争的日益激烈&#xff0c;高素质的技术管理人才在当今社会中扮演着越来越重要的角色。特别是在信息技术领域&#xff0c;企业对于拥有专业技能和丰富知识的研发效能管理与技术人才的需求愈发旺盛。工业和信息化部教育与考试中心&#xff08;以下简称…

MissionPlanner编译过程

环境 windows 10 mission planner 1.3.80 visual studio 2022 git 2.22.0 下载源码 (已配置git和ssh) 从github上克隆源码 git clone gitgithub.com:ArduPilot/MissionPlanner.git进入根目录 cd MissionPlanner在根目录下的ExtLibs文件下是链接的其它github源码&#xff0…

【深度学习】 Python 和 NumPy 系列教程(十二):NumPy详解:4、数组广播;5、排序操作

目录 一、前言 二、实验环境 三、NumPy 0、多维数组对象&#xff08;ndarray&#xff09; 多维数组的属性 1、创建数组 2、数组操作 3、数组数学 4、数组广播 5、排序操作 1. np.sort() 函数 2. np.argsort() 函数 3. ndarray.sort() 方法 4. 按列或行排序 5. n…