使用C++实现Flutter Windows插件

2023-11-01

上周实现的Flutter条形码插件已经发布到https://pub.dev/packages/flutter_barcode_sdk,平台相关部分只包含了Android的Java代码。这周新增用于Windows的C++代码。后续计划还包含iOS和Web。

在这里插入图片描述

关于Dynamsoft C++ Barcode SDK

学习资源

Flutter桌面插件开发的资源比较少。可以从以下三个入手:

如何把C++条形码SDK集成到Flutter Windows插件中

Flutter创建的Windows插件工程目录结构如下:

bin
  /DynamsoftBarcodeReaderx64.dll

include

  /flutter_barcode_sdk

    /flutter_barcode_sdk_plugin.h

  /barcode_manager.h

  /DynamsoftBarcodeReader.h

  /DynamsoftCommon.h

lib
  /DBRx64.lib

CMakeLists.txt

flutter_barcode_sdk_plugin.cpp
  • CMakeLists.txt是CMake的配置文件。
  • flutter_barcode_sdk_plugin.cpp用于实现插件的C++代码逻辑。
  • DynamsoftBarcodeReaderx64.dllDBRx64.libDynamsoftBarcodeReader.h,以及DynamsoftCommon.h可以从C++ SDK中获取。
  • 为了方便,条形码接口调用的实现都放在barcode_manager.h中。

从Dart传到C++的接口和参数,会在HandleMethodCall()中解析:

void FlutterBarcodeSdkPlugin::HandleMethodCall(
      const flutter::MethodCall<flutter::EncodableValue> &method_call,
      std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
  {
    const auto *arguments = std::get_if<EncodableMap>(method_call.arguments());

    if (method_call.method_name().compare("getPlatformVersion") == 0)
    {
      
    }
    else if (method_call.method_name().compare("setLicense") == 0)
    {
      
    }
    else if (method_call.method_name().compare("decodeFile") == 0)
    {
      
    }
    else if (method_call.method_name().compare("decodeFileBytes") == 0)
    {
      
    }
    else if (method_call.method_name().compare("decodeImageBuffer") == 0)
    {
      
    }
    else
    {
      result->NotImplemented();
    }
  }

} 

获取参数,并转换成C++类型:stringintvector<unsigned char>

std::string filename;
auto filename_it = arguments->find(EncodableValue("filename"));
if (filename_it != arguments->end())
{
  filename = std::get<std::string>(filename_it->second);
}


std::vector<unsigned char> bytes;
auto bytes_it = arguments->find(EncodableValue("bytes"));
if (bytes_it != arguments->end())
{
  bytes = std::get<vector<unsigned char>>(bytes_it->second);
}

int width = 0;
auto width_it = arguments->find(EncodableValue("width"));
if (width_it != arguments->end())
{
  width = std::get<int>(width_it->second);
}

返回的结果需要封装成Flutter定义的类型:

EncodableList results;
result->Success(results);

接下来在barcode_manager.h中实现解码和结果封装。

这里定义三个解码接口:

EncodableList DecodeFile(const char * filename) 
{
    EncodableList out;   
    int ret = reader->DecodeFile(filename, "");

    if (ret == DBRERR_FILE_NOT_FOUND)
    {
        printf("Error code %d. %s\n", ret, CBarcodeReader::GetErrorString(ret));
        return out;
    }

    return WrapResults();
}

EncodableList DecodeFileBytes(const unsigned char * bytes, int size) 
{
    reader->DecodeFileInMemory(bytes, size, "");
    return WrapResults();
}

EncodableList DecodeImageBuffer(const unsigned char * buffer, int width, int height, int stride, int format) 
{
    ImagePixelFormat pixelFormat = IPF_BGR_888;
    switch(format) {
        case 0:
            pixelFormat = IPF_GRAYSCALED;
            break;
        case 1:
            pixelFormat = IPF_ARGB_8888;
            break;
    }

    reader->DecodeBuffer(buffer, width, height, stride, pixelFormat, "");

    return WrapResults();
}

获取解码结果,并封装到Flutter的maplist中:

EncodableList WrapResults() 
{
    EncodableList out;
    TextResultArray *results = NULL;
    reader->GetAllTextResults(&results);
        
    if (results->resultsCount == 0)
    {
        printf("No barcode found.\n");
        CBarcodeReader::FreeTextResults(&results);
    }
    
    for (int index = 0; index < results->resultsCount; index++)
    {
        EncodableMap map;
        map[EncodableValue("format")] = results->results[index]->barcodeFormatString;
        map[EncodableValue("text")] = results->results[index]->barcodeText;
        map[EncodableValue("x1")] = results->results[index]->localizationResult->x1;
        map[EncodableValue("y1")] = results->results[index]->localizationResult->y1;
        map[EncodableValue("x2")] = results->results[index]->localizationResult->x2;
        map[EncodableValue("y2")] = results->results[index]->localizationResult->y2;
        map[EncodableValue("x3")] = results->results[index]->localizationResult->x3;
        map[EncodableValue("y3")] = results->results[index]->localizationResult->y3;
        map[EncodableValue("x4")] = results->results[index]->localizationResult->x4;
        map[EncodableValue("y4")] = results->results[index]->localizationResult->y4;
        out.push_back(map);
    }

    CBarcodeReader::FreeTextResults(&results);
    return out;
}

到此,用于Windows插件的C++代码已经完成。最后一步就是配置CMakeLists.txt文件。

链接C++库:

link_directories("${PROJECT_SOURCE_DIR}/lib/") 
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin "DBRx64")

打包动态链接库:

set(flutter_barcode_sdk_bundled_libraries
  "${PROJECT_SOURCE_DIR}/bin/"
  PARENT_SCOPE
)

这步很重要。如果没有打包进去,应用程序会因为缺少DLL无法运行。

用Flutter实现Windows桌面条码识别程序

有了插件就可以快速实现桌面扫码应用。

创建一个新工程,并在pubspec.yaml中添加依赖:

dependencies:
  flutter:
    sdk: flutter

  flutter_barcode_sdk:

创建SDK对象:

class _DesktopState extends State<Desktop> {
  String _platformVersion = 'Unknown';
  final _controller = TextEditingController();
  String _barcodeResults = '';
  FlutterBarcodeSdk _barcodeReader;
  bool _isValid = false;
  String _file = '';

  @override
  void initState() {
    super.initState();
    initPlatformState();
    initBarcodeSDK();
  }

  Future<void> initBarcodeSDK() async {
    _barcodeReader = FlutterBarcodeSdk();
    // Get 30-day FREEE trial license from https://www.dynamsoft.com/customer/license/trialLicense?product=dbr
    await _barcodeReader.setLicense('LICENSE-KEY');
  }
}

使用TextFieldImageMaterialButtonText构建UI:

@override
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
        appBar: AppBar(
          title: const Text('Dynamsoft Barcode Reader'),
        ),
        body: Column(children: [
          Container(
            height: 100,
            child: Row(children: <Widget>[
              Text(
                _platformVersion,
                style: TextStyle(fontSize: 14, color: Colors.black),
              )
            ]),
          ),
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              labelText: 'Input an image path',
              errorText: _isValid ? null : 'File not exists',
            ),
          ),
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  getDefaultImage(),
                  Text(
                    _barcodeResults,
                    style: TextStyle(fontSize: 14, color: Colors.black),
                  ),
                ],
              ),
            ),
          ),
          Container(
            height: 100,
            child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  MaterialButton(
                      child: Text('Decode Barcode'),
                      textColor: Colors.white,
                      color: Colors.blue,
                      onPressed: () async {
                        if (_controller.text.isEmpty) {
                          setState(() {
                            _isValid = false;
                            _barcodeResults = '';
                            _file = '';
                          });
                          return;
                        }
                        File file = File(_controller.text);
                        if (!file.existsSync()) {
                          setState(() {
                            _isValid = false;
                            _barcodeResults = '';
                            _file = '';
                          });
                          return;
                        } else {
                          setState(() {
                            _isValid = true;
                            _file = _controller.text;
                          });
                        }
                        Uint8List bytes = await file.readAsBytes();
                        List<BarcodeResult> results =
                            await _barcodeReader.decodeFileBytes(bytes);
                        setState(() {
                          _barcodeResults = getBarcodeResults(results);
                        });
                      }),
                ]),
          ),
        ])),
  );
}

图片默认显示资源包中的图。如果输入的图片有效,显示输入的图片:

Widget getDefaultImage() {
  if (_controller.text.isEmpty || !_isValid) {
    return Image.asset('images/default.png');
  } else {
    return Image.file(File(_file));
  }
}

要使用资源图片,需要在pubspec.yaml中添加:

flutter:
  assets:
    - images/default.png

最后运行程序:

flutter run -d windows

在这里插入图片描述

源码

https://github.com/yushulx/flutter_barcode_sdk

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用C++实现Flutter Windows插件 的相关文章

  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

    最近 我们遇到了一些旧版 WinForms 应用程序 我们需要更新一些新功能 在专家测试该应用程序时 发现一些旧功能被破坏 无效的跨线程操作 现在 在您认为我是新手之前 我确实有一些 Windows 窗体应用程序的经验 我不是专家 但我认为
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 我的 strlcpy 版本

    海湾合作委员会 4 4 4 c89 我的程序做了很多字符串处理 我不想使用 strncpy 因为它不会终止 我不能使用 strlcpy 因为它不可移植 只是几个问题 我怎样才能让我的函数正常运行 以确保它完全安全稳定 单元测试 这对于生产来
  • 像“1$”这样的位置参数如何与 printf() 一起使用?

    By man I find printf d width num and printf 2 1 d width num 是等价的 但在我看来 第二种风格应该与以下相同 printf d num width 然而通过测试似乎man是对的 为什
  • 检查 url 是否指向文件或页面

    我们需要以下内容 如果文件确实是文件 则从 URL 下载该文件 否则 如果它是一个页面 则什么也不做 举个简单的例子 我有以下命令来下载文件 My Computer Network DownloadFile http www wired c
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • 是否可以从图像中获取图像 GPS 位置坐标?

    我正在构建一个 Flutter 应用程序 用户可以在其中发布照片及其位置 用户可以从相机或图库中获取图片 如果用户从相机拍照 我可以使用设备的 GPS 位置来设置图片的位置 我正在尝试根据图片的元数据获取图片的 GSP 位置 但是 我还没有
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • 窗体最大化时自动缩放子控件

    有没有办法在最大化屏幕或更改分辨率时使 Windows 窗体上的所有内容自动缩放 我发现手动缩放它是正确的 但是当切换分辨率时我每次都必须更改它 this AutoScaleDimensions new System Drawing Siz
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • 如何在 C# 中播放在线资源中的 .mp3 文件?

    我的问题与此非常相似question https stackoverflow com questions 7556672 mp3 play from stream on c sharp 我有音乐网址 网址如http site com aud
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器

随机推荐

  • 二十三种设计模式第二十四篇--访问者模式(完结撒花)

    在访问者模式 Visitor Pattern 中 我们使用了一个访问者类 它改变了元素类的执行算法 通过这种方式 元素的执行算法可以随着访问者改变而改变 这种类型的设计模式属于行为型模式 根据模式 元素对象已接受访问者对象 这样访问者对象就
  • 二、树模型(3)

    GBDT 特征筛选方法 https blog csdn net yangxudong article details 53899260 GBDT 用于分类 树形结构为什么不需要归一化 因为数值缩放不影响分裂点位置 对树模型的结构不造成影响
  • 【Verilog】二、Verilog基础语法

    文章目录 前言 一 简单的Verilog知识 1 1 Verilog端口定义 1 2 Verilog的标识符 1 3 Verilog的逻辑值 1 4 Verilog的数字进制 1 5 Verilog的数据类型 1 5 1 reg型 1 5
  • 吴恩达《深度学习专项》笔记(十二):目标检测与语义分割简介 (YOLO, U-Net)

    这节课中 我们要学习计算机视觉中最重要的任务之一 目标检测任务 我们会先认识目标定位和关键点检测这两个比较简单的任务 慢慢过度到目标检测任务 之后 我们会详细学习目标检测的经典算法YOLO 最后 我们会稍微认识一下语义分割任务及适用于此问题
  • 老猿Python部分代码样例

    专栏 Python基础教程目录 专栏 使用PyQt开发图形界面Python应用 专栏 PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 PyQt编程实战 通过eventFilter监视QScrollArea的widget 的P
  • Vue3 基础语法

    文章目录 1 创建Vue项目 1 1创建项目 1 2 初始项目 2 vue3 语法 2 1 复杂写法 2 2 简易写法 2 3 reactive 对象类型 2 4 ref 简单类型 2 5 computed 计算属性 2 6 watch 监
  • python并发编程学习笔记--初识多线程 day01

    请求网页信息 blog spider py import requests 生成列表 from bs4 import BeautifulSoup urls f http www cnblogs com p page f https www
  • AD20——批量快速放置元件管脚

    在使用Altium Designer 20创建元件库时 对于管脚较多的元件可采用批量放置的操作以节省时间 具体操作如下 1 首先放置第1个管脚 选中该管脚并Ctrl C将其复制 2 点击编辑 E gt 阵列式粘贴 Y 弹出如下界面 参数说明
  • android中的(singleLine)单行显示 none start middle

    http www cnblogs com firecode archive 2012 04 23 2466711 html 第一个button 什么都没写 后面省略号 android ellipsize none none就没有省略号了 a
  • QT5.12编译安装

    1 引言 QT是一个跨平台的编译软件 本文将介绍在linux内核操作系统下对于QT5 12的编译安装过程及QT编译程序的配置过程 2 编译安装 2 1 下载源码 推荐使用国内镜像网站下载 国内著名的几个 Qt 镜像网站 中国科学技术大学 h
  • 【ZJCPC2023 第20届 浙江省赛】赛后总结

    感叹 线下比赛环境挺不错的 可以白嫖杭师大一天的饭 还有传统的气球环节 看到别人AC了6道题手上抓着6个气球出考场就大受震撼 尤其是中学生的打星队 实际情况 由于本科和专科分开评奖 所以看起来二本那些落后的 没有教练的学生给人的刻板印象就是
  • 跟我一起写 Makefile(六)

    跟我一起写 Makefile 六 本文来自于CSDN 陈皓博主 网址http blog csdn net haoel article details 2891 详细内容请参考其经典文章 跟我一起写makefile 陈皓
  • 链表面试常见题目

    1 反转链表 头插法 2 合并两个有序链表 3 链表倒数第k个节点 连个节点一个先走 k步 然后两个一起走 走到第一个节点 next为null 4 从尾到头打印 链表 借助栈或者递归 5 复杂链表复制 1 借助map存储 O n 空间 2
  • 串口字符串-HEX格式

    介绍 串口通信过程中 通常涉及一个数据的模拟过程以及数据发送过程 一般来说 我们会发送一串指令给下位机 68 05 00 84 01 02 03 例如这种 我们明白 这是我们 将相应的字符转换成 hex 字符显示 用于表示ascii 字母的
  • @Transactional 失效问题

    Transactional配置起来是简单方便 但是坑也相当多 下面就记录下这些坑 1 service类标签添加在了接口上 查阅资料说接口的方法上可以加也不建议这样用 但实际中这么出现事务失效 2 Transactional 注解只能应用到
  • CSS3 - flex属性

    前言 CSS属性 flex 规定了弹性元素如何伸长或缩短以适应flex容器中的可用空间 这是一个简写属性 用来设置 flex grow flex shrink flex basis flex grow 属性 定义项目的放大比例 默认为0 即
  • MongoDB max 获取最大值 (Golang)

    某个集合 要获取某个字段的最大值 有两种办法 一个是用sort 另一个是用聚合 Aggregate 下面是代码演示 sort var ID uint64 func initIDEx clientOptions options Client
  • count(distinct 多个字段)

    select count distinct col1 col2 col3 from table 但是 这样是不允许的 因为count是不能统计多个字段的 虽然distinct是可行的 有种比较直接的方法就是把消除重复后在统计查询 selec
  • ubuntu 20.4 + openswan 实现点对点VPN

    需求背景 多个IDC机房或者办公地点 不同地址位置 用linux系统和软件 组一个局域网 共享网络资源 需求环境 ubuntu 20 4 openswan 实现点对点VPN 需要技能 熟悉ubuntu 会用日常网络指令 了解网络结构 理解私
  • 使用C++实现Flutter Windows插件

    上周实现的Flutter条形码插件已经发布到https pub dev packages flutter barcode sdk 平台相关部分只包含了Android的Java代码 这周新增用于Windows的C 代码 后续计划还包含iOS和