DEV Community

Xiao Ling
Xiao Ling

Posted on • Originally published at dynamsoft.com

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.

What You Should Know About Dynamsoft Barcode Reader

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 .
Enter fullscreen mode Exit fullscreen mode

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

- include/
- CMakeLists.txt
- flutter_barcode_sdk_plugin.cc
Enter fullscreen mode Exit fullscreen mode

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
)

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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();
  }
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Discussion (0)