Flutter Barcode Plugin for Linux: from Dart to C++

For the past several weeks, the Flutter plugin of Dynamsoft Barcode Reader has covered Windows, Android and Web. This week, we go through the plugin implementation for Linux. Similar to Windows, we combine Dart and C++ code for Linux. The unexpected thing is the C++ data types of Flutter defined for Linux change a lot. So it still takes a little bit of time to make the plugin work.

Pub.dev Package

https://pub.dev/packages/flutter_barcode_sdk

SDK Activation

Learning Resources for Flutter Desktop Development

Step-by-step Flutter Linux Plugin Development

To initialize the plugin template for Linux, we need to switch the coding environment to Linux firstly, and then run the following command to add the template to the existing plugin project:

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

Here is the Linux plugin folder generated by the above command:

- include/
- CMakeLists.txt
- flutter_barcode_sdk_plugin.cc

It is a CMake project as well. We need Dynamsoft Barcode Reader libraries for linking. Therefore, the next step is to create a lib folder and copy DynamsoftBarcodeReader/Lib/Linux/*.so files to the folder. Afterwards, we configure link_directories, target_link_libraries and flutter_barcode_sdk_bundled_libraries in CMakeLists.txt:

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
)

So far, the build configuration is settled. Let’s get started to code.

We open flutter_barcode_sdk_plugin.cc and find the flutter_barcode_sdk_plugin_handle_method_call() function.

The method name should be familiar to you if you have built plugins for other platforms.

Referring to the methods implemented for Windows, we can construct the skeleton:

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);
}

Since the code logic is almost the same to Windows, as long as we know the data types and corresponding calling methods, it will be easy to implement the plugin for Linux. The relevant header file is located at example/linux/flutter/ephemeral/flutter_linux/fl_value.h.

According to the comments of the header file, we can get the ways of converting Dart arguments to C++ types for Linux:

  • String

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

      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);
    

In the meantime, the function return type and data wrapping method can be changed in barcode_manager.h as follows:

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

Now, the Flutter barcode plugin for Linux has been finished. Since the Dart code for Windows also works for Linux, there is no extra code needed for the example project. We just need to run the example directly by setting Linux as the device:

flutter run -d linux

flutter barcode scanner

The tutorial of how to build Flutter barcode plugin for iOS and macOS is coming next week.

Flutter Barcode SDK Download

https://pub.dev/packages/flutter_barcode_sdk

Source Code

https://github.com/yushulx/flutter_barcode_sdk