前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 截图上传

Flutter 截图上传

作者头像
Jimmy_is_jimmy
发布2024-04-21 08:41:25
760
发布2024-04-21 08:41:25
举报
文章被收录于专栏:call_me_Rcall_me_R

在上一篇文章中,我们谈了 Flutter 中下载并保存图片为文件 的内容,今天,我们来说说怎么将 widget 生成截图,并且怎么通过接口上传。

生成截图

我们指定生成图片的 widget 区域:

代码语言:javascript
复制
final GlobalKey boundaryKey = GlobalKey();

// ... 
child: RepaintBoundary(
  key: boundaryKey,
  child: const Text('测试内容'), // 这里可以替换成自定义的 widget 内容
)

上面,我们通过 RepaintBoundary 包裹了需要生成图片的 widget。下面,我们触发方法,生成图片:

代码语言:javascript
复制
Widget build(BuildContext context) {
  return IconButton(
    icon: const Icon(Icons.insert_photo_rounded),
    onPressed: () async {
      try {
          // widget 转换成图片
          RenderRepaintBoundary? boundary = boundaryKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
          ui.Image? image = await boundary?.toImage()
          
          final byteData = await image?.toByteData(format: ui.ImageByteFormat.png);
          
          // 弹窗展示
          showDialog(
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                title: const Text(
                  '展示截图', 
                  style: TextStyle(
                    fontSize: 16.0,
                  ),
                ),
                content: SizeBox(
                  width: 520.0,
                  height: 160.0
                  child: Image.memory(
                    byteData!.buffer.asUnit8List(),
                    fit: BoxFit.fill,
                  ),
                ),
                actions: <Widget>[
                  TextButton(
                    onPressed: () {
                      Navigator.of(context).pop();
                    },
                    child: const Text(
                      '取消',
                      style: TextStyle(
                        color: Colors.grey,
                      ),
                    ),
                  ),
                ],
              );
            }
          );
      } catch(e) {
        debugPrint(e.toString());
      }
    }
  )
}

上面代码中,我们指定了 boundaryKey 区域,通过 boundary?.toImage() 生成了图片。然后,为了方便我们查看生成的图片,我们写了个弹窗,通过 Image.memory 将保存在内存的图片数据流展示出来。

保存为临时文件

接着,我们将该图片数据流写成临时的文件。

我们需要用到包 path_provider,截止发稿,该版本为 2.1.3

该插件方便我们查找文件系统的常用位置。

代码语言:javascript
复制
onPressed: () async {
  // widget 转换成图片
  RenderRepaintBoundary? boundary = boundaryKey.currentContext?.findRenderObject() as   RenderRepaintBoundary?;
  ui.Image? image = await boundary?.toImage()
          
  final byteData = await image?.toByteData(format: ui.ImageByteFormat.png);
  
  // 创建一个临时的文件进行上传
  final output = await getTemporaryDirectory();
  final file = File("${output.path}/${(new DateTime.now()).millisecondsSinceEpoch}.png");
  await file.writeAsBytes(byteData!.buffer.asUint8List());
}

getTemporaryDirectory() 用于获取临时文件目录的路径。不同操作系统,获取的临时文件目录路径可能会有所不同,这个我们不需要关心。然后,我们生成了当前时间戳的文件名 DateTime.now()).millisecondsSinceEpoch}.png,并把生成的截图数据写入文件中。

接口上传

为了方便理解,我们扩展演示,随便增加一个字段,上传的 Form 表单内容如下:

代码语言:javascript
复制
final TextEditingController _descriptionController = TextEditingController(text: '描述内容');

// 其他代码
Form(
  child: Column(
    children: <Widget>[
      Container(
        width: 520.0,
        height: 160.0,
        child: Image.memory(
          byteData!.buffer.asUnit8List(),
          fit: BoxFit.fill,
        ),
      ),
      TextFormField(
        controller: _descriptionController,
        decoration: InputDecoration(
          labelText: '描述'
        ),
      ),
    ],
  ),
),

我们增加了 description 图片描述的字段,所以,我们将上传如下的字段到服务器:

代码语言:javascript
复制
{
  "file": *,
  "description": *,
}

通过 Bloc 管理:

代码语言:javascript
复制
onPressed: () {
  fileBloc.add(
    FileSaveEvent(
      file: file,
      description: '相关描述',
    ),
  );
}
代码语言:javascript
复制
// file_event.dart
abstract class FileEvent {}

class FileSaveEvent extends FileEvent {
  final File file;
  final String description;
  
  FileSaveEvent({
    required this.file,
    required this.description,
  });
}

PS 关于 bloc 的知识点,我们将会开一篇文章进行讲解,这里先一笔带过

代码语言:javascript
复制
// file_bloc.dart
FileBloc() : super(FileState().init()) {
  on<FileSaveEvent>(_onFileSaveEvent);
} 

Future<String> _onFileSaveEvent(FileSaveEvent event, Emitter<FileState> emit) async {
  Map<String, dynamic> snapshot = {
    'description': event.description ?? '',
    'type': 'IMAGE',
  };
  
  FormData formData = FormData.fromMap({
    'file': await MultipartFile.fromFile(event.file.path),
    'snapshot': jsonEncode(snapshot)
  });
  
  try {
    // 接口调用
    await apiService?.postFileEvent(recordId, formData);
  } catch(e) {
    debugPrint(e.toString());
    return '保存失败';
  }
  return '保存成功';
}

PS 上面代码上的 FileState 用于管理数据的状态,比如请求成功,失败,获取数据列表的管理等。

我们假设存在一个 POST 保存文件的接口服务 apiService?.postFileEvent(id, formData),其中 recordId 是记录的 idformData 是放在 body 请求的数据。我们把 description 当作 snapshot 变量中的一个属性进行扩展。

我们可以在 FileState 文件中,定义接口执行的状态,并在页面中监听该状态的更改来展示接口操作成功与否的 Toast。或者,我们也可以通过 EventBus,在 await apiService?.postFileEvent(recordId, formData); 下进行 eventBus.fire 发射状态,在页面中进行监听 eventBus.on().listen。这个根据实际情况来使用。

感谢读者捧场,谢谢🌹

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 生成截图
  • 保存为临时文件
  • 接口上传
相关产品与服务
图数据库 KonisGraph
图数据库 KonisGraph(TencentDB for KonisGraph)是一种云端图数据库服务,基于腾讯在海量图数据上的实践经验,提供一站式海量图数据存储、管理、实时查询、计算、可视化分析能力;KonisGraph 支持属性图模型和 TinkerPop Gremlin 查询语言,能够帮助用户快速完成对图数据的建模、查询和可视化分析。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档