Flutter C++插件:Linux桌面应用开发

我的Flutter一维码,二维码插件已经支持Android,Windows和Web。这篇文章继续添加新的平台:Linux。

Flutter插件下载

https://pub.dev/packages/flutter_barcode_sdk

学习资源

  • https://flutter.dev/desktop
  • https://github.com/google/flutter-desktop-embedding
  • https://github.com/flutter/samples/tree/master/experimental/desktop_photo_search

使用Dart和C++开发Flutter Linux插件

要开发Linux插件,需要把开发平台切换到Linux。我使用统信UOS。

在当前的插件项目中添加Linux插件的模板:

flutter create --template=plugin --platforms=linux .

命令执行之后会生成如下的目录结构:

- include/
- CMakeLists.txt
- flutter_barcode_sdk_plugin.cc

接下来创建一个lib目录。把下载的Dynamsoft Barcode Reader C++ 压缩包里的*.so库文件拷贝到目录中。然后在CMakeLists.txt中配置link_directories, target_link_librariesflutter_barcode_sdk_bundled_libraries

cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME "flutter_barcode_sdk")
project(${PROJECT_NAME} LANGUAGES CXX)

# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "flutter_barcode_sdk_plugin")

link_directories("${PROJECT_SOURCE_DIR}/bin/") 

add_library(${PLUGIN_NAME} SHARED
  "flutter_barcode_sdk_plugin.cc"
)
apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES
  CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE
  "${CMAKE_CURRENT_SOURCE_DIR}/include")

target_link_libraries(${PLUGIN_NAME} PRIVATE flutter "DynamsoftBarcodeReader")
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)

# List of absolute paths to libraries that should be bundled with the plugin
set(flutter_barcode_sdk_bundled_libraries
  "${PROJECT_SOURCE_DIR}/lib/"
  PARENT_SCOPE
)

到此,动态链接库的编译链接已经没有问题了,开始写C++代码。打开flutter_barcode_sdk_plugin.cc找到flutter_barcode_sdk_plugin_handle_method_call()函数。这里是Dart通向C++的入口。我们先构造基本的接口名判断代码:

static void flutter_barcode_sdk_plugin_handle_method_call(
    FlutterBarcodeSdkPlugin* self,
    FlMethodCall* method_call) {
  g_autoptr(FlMethodResponse) response = nullptr;

  const gchar* method = fl_method_call_get_name(method_call);
  FlValue* args = fl_method_call_get_args(method_call);

  if (strcmp(method, "getPlatformVersion") == 0) {
    struct utsname uname_data = {};
    uname(&uname_data);
    g_autofree gchar *version = g_strdup_printf("Linux %s. Dynamsoft Barcode Reader version: %s", uname_data.version, self->manager->GetVersion());
    g_autoptr(FlValue) result = fl_value_new_string(version);
    response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
  } 
  else if (strcmp(method, "setLicense") == 0) {
    response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
  }
  else if (strcmp(method, "decodeFile") == 0) {
    response = FL_METHOD_RESPONSE(fl_method_success_response_new(results));
  }
  else if (strcmp(method, "decodeFileBytes") == 0) {
    response = FL_METHOD_RESPONSE(fl_method_success_response_new(results));
  }
  else if (strcmp(method, "decodeImageBuffer") == 0) {
    response = FL_METHOD_RESPONSE(fl_method_success_response_new(results));
  } 
  else if (strcmp(method, "setBarcodeFormats") == 0) {
    response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
  } else {
    response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
  }

  fl_method_call_respond(method_call, response, nullptr);
}

然后要解决数据类型转换及封装。Linux的头文件和Windows上的完全不同,不能直接使用。可以参考example/linux/flutter/ephemeral/flutter_linux/fl_value.h里的定义。

以下是Dart传入参数到C++的类型转换:

String转换

FlValue* value = fl_value_lookup_string(args, "license");
const char* license = fl_value_get_string(value);

Int转换

 value = fl_value_lookup_string(args, "width");
 int width = fl_value_get_int(value);

Bytes转换

FlValue* value = fl_value_lookup_string(args, "bytes");
unsigned char* bytes = (unsigned char*)fl_value_get_uint8_list(value);

和Windows一样,所有的解码实现都封装在barcode_manager.h中。不同的是返回类型:

FlValue* WrapResults() 
{
    FlValue* out = fl_value_new_list();

    TextResultArray *results = NULL;
    reader->GetAllTextResults(&results);
        
    if (results == NULL || results->resultsCount == 0)
    {
        printf("No barcode found.\n");
    }
    else
    {
        for (int index = 0; index < results->resultsCount; index++)
        {
            FlValue* map = fl_value_new_map ();
            fl_value_set_string_take (map, "format", fl_value_new_string(results->results[index]->barcodeFormatString));
            fl_value_set_string_take (map, "text", fl_value_new_string(results->results[index]->barcodeText));
            fl_value_set_string_take (map, "x1", fl_value_new_int(results->results[index]->localizationResult->x1));
            fl_value_set_string_take (map, "y1", fl_value_new_int(results->results[index]->localizationResult->y1));
            fl_value_set_string_take (map, "x2", fl_value_new_int(results->results[index]->localizationResult->x2));
            fl_value_set_string_take (map, "y2", fl_value_new_int(results->results[index]->localizationResult->y2));
            fl_value_set_string_take (map, "x3", fl_value_new_int(results->results[index]->localizationResult->x3));
            fl_value_set_string_take (map, "y3", fl_value_new_int(results->results[index]->localizationResult->y3));
            fl_value_set_string_take (map, "x4", fl_value_new_int(results->results[index]->localizationResult->x4));
            fl_value_set_string_take (map, "y4", fl_value_new_int(results->results[index]->localizationResult->y4));
            fl_value_set_string_take (map, "angle", fl_value_new_int(results->results[index]->localizationResult->angle));
            fl_value_append_take (out, map);
        }
    }

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

  FlValue* DecodeFile(const char * filename) 
  {
      FlValue* out = fl_value_new_list();
      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();
  }

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

  FlValue* 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();
  }

全部搞定。由于Dart的代码是和Windows共用的,所以不需要修改上层代码。现在运行Linux桌面读码程序:

flutter run -d linux

Flutter C++插件:Linux桌面应用开发

视频

<iframe allowfullscreen="true" data-mediaembed="bilibili" id="FjLjJLXC-1621931869941" src="https://player.bilibili.com/player.html?aid=290386360"></iframe>

Flutter开发统信UOS桌面读码App

源码

https://github.com/yushulx/flutter_barcode_sdk

上一篇:电信用户流失预测任务


下一篇:ML之xgboost:利用xgboost算法(sklearn+7CrVa)训练mushroom蘑菇数据集(22+1,6513+1611)来预测蘑菇是否毒性(二分类预测)