{"id":4861,"date":"2021-10-14T09:22:13","date_gmt":"2021-10-14T07:22:13","guid":{"rendered":"https:\/\/www.basyskom.de\/?p=4861"},"modified":"2025-02-06T08:11:51","modified_gmt":"2025-02-06T07:11:51","slug":"flutter-ffi-plugins-how-to-use-open62541-in-flutter-applications","status":"publish","type":"post","link":"https:\/\/www.basyskom.de\/en\/flutter-ffi-plugins-how-to-use-open62541-in-flutter-applications\/","title":{"rendered":"Flutter FFI Plugins &#8211; How to use open62541 in Flutter applications"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"4861\" class=\"elementor elementor-4861\" data-elementor-post-type=\"post\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-2b12427 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"2b12427\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-2ff2463\" data-id=\"2ff2463\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-d86c67b elementor-widget elementor-widget-heading\" data-id=\"d86c67b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Motivation<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e66c322 elementor-widget elementor-widget-text-editor\" data-id=\"e66c322\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In recent days, Google&#8217;s liberally licensed UI toolkit Flutter is getting a lot of attention. While the original 1.0 release of Flutter only supported Android and iOS devices, the release 2.0 from March 2021 added official support for Web and Desktop as well. Additionally, there is an increased interest in running Flutter on embedded devices to implement embedded HMIs. Also Google recognized this and added &#8220;embedded devices&#8221; as an official target for Flutter lately, even relatively prominent on Flutter&#8217;s homepage. There are various very interesting projects underway. To name one have a look at this <a class=\"external-link\" href=\"https:\/\/github.com\/sony\/flutter-embedded-linux\" rel=\"nofollow noopener\" target=\"_blank\">Sony<\/a> project.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-373e893 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"373e893\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-bulb\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-634cb8c elementor-widget elementor-widget-heading\" data-id=\"634cb8c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Porting from Qt Quick \/ QML to Flutter?<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cf36ade elementor-widget elementor-widget-text-editor\" data-id=\"cf36ade\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Here at basysKom, one of our primary fields of interest are HMIs on embedded devices. That is why we are following Flutter&#8217;s development in this area very closely.<\/p><p>Recent experiments with internal projects showed that those developments are very promising. <a href=\"https:\/\/www.basyskom.de\/en\/2020\/how-to-run-flutter-on-an-embedded-device\/\" target=\"_blank\" rel=\"noopener\">In this post<\/a>\u00a0my colleague Jeremias explained already last year that Flutter applications can run on i.MX6 devices.\u00a0<\/p><p>This year we ported our industrial HMI showcase from Qt Quick to Flutter to see how this works out. The showcase is not very complex. Nevertheless, the project underlined that developing embedded HMIs with Flutter is quite practical and that the ecosystem and tooling is already very pleasant and mature. We were able to run this showcase application on various platforms like Linux, Android, Web and an i.MX8 device without too much hassle.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6100b00 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"6100b00\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-image-compare\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0422d47 elementor-widget elementor-widget-heading\" data-id=\"0422d47\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The missing part: Plugins<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-363b063 elementor-widget elementor-widget-text-editor\" data-id=\"363b063\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>However one thing was still missing. The original application is a Qt Quick-based HMI using Qt OPC UA to communicate with an OPC UA server. There is no direct OPC UA support in Flutter\/Dart. To deal with this we originally used a Node.js gateway which bridged OPC UA to HTTP and Websockets (which works like a charm with Flutter), so we were able to connect the Flutter application to the backend using these technologies. But of course it would be nicer to not take this detour and directly communicate via OPC UA.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d805084 elementor-widget elementor-widget-text-editor\" data-id=\"d805084\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>As there is no Dart implementation of OPC UA the question was whether it is possible to call native code from Flutter and use an existing library like\u00a0<a href=\"https:\/\/github.com\/open62541\/open62541\" target=\"_blank\" rel=\"noopener\">open62541<\/a> to implement the connection to our backend. Not only would this be nice for our showcase, but of course binding to native code is a rather important requirement for many applications, especially in the field of embedded HMIs. The two most obvious use-cases are the reuse of existing libraries and the implementation of performance-critical parts using adequate (meaning fastest possible) languages.<\/p><p>Fortunately, the release of Dart 2.12 \/ Flutter 2.0 brought us, in addition to the support of more platforms, also a library called <code data-no-translation=\"\">dart:ffi<\/code> that makes foreign function interfaces possible for Dart and Flutter. This is an official part of Dart now, and it is provided and maintained by the Dart team.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6b8ed76 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"6b8ed76\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-section-link\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-470d5f5 elementor-widget elementor-widget-text-editor\" data-id=\"470d5f5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>After doing some experiments with <code data-no-translation=\"\">dart:ffi<\/code> and porting some parts of our showcase, we decided to share our experiences and write this blog post. Interested readers can play with a small example that\u00a0I created; you can find in our\u00a0<a href=\"https:\/\/github.com\/basysKom\/example_flutter_open62541\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>.<\/p><p>It is based on an existing <a href=\"https:\/\/github.com\/open62541\/open62541\/tree\/master\/examples\" target=\"_blank\" rel=\"noopener\">example server\u00a0<\/a> shipped with the Qt OPC UA module to have a simple server providing some values for our UI.<\/p><p>In the following sections I will briefly present my findings and walk through the essential aspects of the implementation.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-16c76b1 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"16c76b1\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-5063cc0\" data-id=\"5063cc0\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-a93d6a5 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"a93d6a5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-highlighter\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e530cb4 elementor-widget elementor-widget-heading\" data-id=\"e530cb4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Concept<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a1842e2 elementor-widget elementor-widget-text-editor\" data-id=\"a1842e2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>To get started with <code data-no-translation=\"\">dart:ffi<\/code> you can have a look at the\u00a0<a href=\"https:\/\/dart.dev\/guides\/libraries\/c-interop\" target=\"_blank\" rel=\"noopener\">official documentation and good examples<\/a>. Getting an idea of how it works and trying out the examples yourself does not take too long and is quite interesting already.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ceb7f91 elementor-widget elementor-widget-text-editor\" data-id=\"ceb7f91\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Having a closer look at the examples, you will notice that they all work in a synchronous manner: the native code is called from Dart synchronously and there are no things running in the background in parallel. <span class=\"inline-comment-marker\" data-ref=\"a8dcf3a2-cf20-46db-87c9-4053ddececec\">Transferred to our task, in which we want to receive data updates from the backend, thi<\/span>s means that we would need to implement some polling in order to get new data from the backend. In fact, this is the first thing that I tried. Implementing this pattern is rather straightforward and can be adapted from the examples. However, it is important to note at this point that the Dart profiler can have an influence on the execution of syscalls in debug mode. This has caused a strange problem for me when querying values. For more details on this, have a look at my question and the corresponding answer in <a class=\"external-link\" href=\"https:\/\/github.com\/dart-lang\/sdk\/issues\/38832\" rel=\"nofollow noopener\" target=\"_blank\">this issue<\/a>.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3022b4f elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"3022b4f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-graduate-cap\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b1f637b elementor-widget elementor-widget-text-editor\" data-id=\"b1f637b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p><span class=\"inline-comment-marker\" data-ref=\"707e5d24-85a1-44ca-8f6b-8bd9d06b130a\">One important characteristic of OPC UA is the client server communication consisting of service calls with\u00a0asynchronous replies. For HMI applications, the most important asynchronous operation is monitoring node attributes in the server for changes by using server-side polling and only reporting changed values to the client.<\/span>\u00a0For us, one important question was whether it is possible with <code data-no-translation=\"\">dart:ffi<\/code> to implement this asynchronously as well.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a67a97b elementor-widget elementor-widget-text-editor\" data-id=\"a67a97b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Searching for this topic very quickly leads to <a class=\"external-link\" href=\"https:\/\/github.com\/dart-lang\/sdk\/issues\/37022\" rel=\"nofollow noopener\" target=\"_blank\">this<\/a> interesting discussion on GitHub. The bottom line is that it is possible to implement asynchronous callbacks from C to Dart. Specifically this means that you can start a background thread in your native library that deals with OPC UA and calls back into Dart whenever there are updates. Because of Dart&#8217;s design this is not a matter of course. For concurrent tasks Dart uses so called isolates. A very nice summary of Dart isolates can be found <a class=\"external-link\" href=\"https:\/\/www.youtube.com\/watch?v=vl_AaCgudcY\" rel=\"nofollow noopener\" target=\"_blank\">here<\/a> but the main issue is that there must only be one thread per isolate and having a background thread from your native library manipulating state in Dart&#8217;s main isolate would break a fundamental assumption of Dart, so it is not an option here.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-44f670a elementor-widget elementor-widget-text-editor\" data-id=\"44f670a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>However, the ffi team came up with a solution using Dart&#8217;s ports mechanism that is originally used to implement a message passing mechanism between isolates. In this use case the mechanism is used to put a so-called microtask into the queue of Dart&#8217;s event loop. So the callback does not run in the background thread, but a message to run some specific code is sent to Dart. When the microtask is then executed, it runs in the thread of Dart&#8217;s main isolate. In practice the implementation is a bit tricky and not as straightforward as the synchronous way of interoperation. However, the ffi team <span class=\"inline-comment-marker\" data-ref=\"3935c8dd-87a2-4d3f-86c7-5b92e98f9e64\">is planning<\/span> to improve this in the future to make the syntax more concise and reduce the amount of required boilerplate code.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5f7f357 elementor-widget elementor-widget-text-editor\" data-id=\"5f7f357\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>To illustrate the idea behind this process, here is a simplified sequence diagram highlighting the most important points and how the various steps relate to each other.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-cb32730 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"cb32730\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-504862c\" data-id=\"504862c\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-361c035 elementor-widget elementor-widget-image\" data-id=\"361c035\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"2438\" height=\"1950\" src=\"https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd.png\" class=\"attachment-full size-full wp-image-4875\" alt=\"flutter open62541 sequence diagram\" srcset=\"https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd.png 2438w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd-300x240.png 300w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd-1024x819.png 1024w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd-768x614.png 768w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd-1536x1229.png 1536w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd-2048x1638.png 2048w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/flutter_ftti_open62451_sqd-560x448.png 560w\" sizes=\"(max-width: 2438px) 100vw, 2438px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-e9975d9 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"e9975d9\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f59c8e4\" data-id=\"f59c8e4\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-37b0265 elementor-widget elementor-widget-heading\" data-id=\"37b0265\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Setup<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a0c4a0c elementor-widget elementor-widget-text-editor\" data-id=\"a0c4a0c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>If you did not use Flutter before you can install it with only a few steps (see the <a class=\"external-link\" href=\"https:\/\/flutter.dev\/docs\/get-started\/install\" rel=\"nofollow noopener\" target=\"_blank\">official documentation<\/a>). The installation already includes <code data-no-translation=\"\">dart:ffi<\/code> (and the package ffi which contains useful utility functions) so no further steps are necessary.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8ab6f03 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"8ab6f03\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-checkout-2\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3b2e4b5 elementor-widget elementor-widget-text-editor\" data-id=\"3b2e4b5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>If you want to use <code data-no-translation=\"\">dart:ffi<\/code> in your Flutter application, you have to create a Flutter plugin. As mentioned before, ffi is a Dart library which can also be used without flutter. In this case you would create a regular Dart package. However, to create a Flutter plugin you can use the <code data-no-translation=\"\">flutter<\/code> command line tool and give the target platforms as a parameter like this:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2e6d0a3 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"2e6d0a3\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-bash' data-no-translation=\"\">flutter create --platforms=linux,android --template=plugin waterpump_ffi_plugin <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cb17684 elementor-widget elementor-widget-text-editor\" data-id=\"cb17684\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Conveniently, this command already generates a sample Flutter application that uses the plugin in the <code data-no-translation=\"\">example<\/code> subdirectory. Note that the dependency in the respective <code data-no-translation=\"\">pubspec.yaml<\/code> file points to the parent directory instead of a pub.dev package.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0fad1fd elementor-widget elementor-widget-text-editor\" data-id=\"0fad1fd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The next step in order to make use of a native library is to integrate the library into your plugin. To do this you have to adapt the respective project files, which is a platform dependent task that you need to carry out for all your supported platforms. I tested my application on the android and linux platforms. For android you can follow the steps from the <a class=\"external-link\" href=\"https:\/\/flutter.dev\/docs\/development\/platform-integration\/c-interop\" rel=\"nofollow noopener\" target=\"_blank\">documentation<\/a>. For linux there is no precise documentation yet but if you are familiar with CMake you can add the necessary stuff in <code data-no-translation=\"\">linux\/CMakeLists.txt<\/code>. I did my tests using a shared library, but according to the\u00a0<a class=\"external-link\" href=\"https:\/\/flutter.dev\/docs\/development\/platform-integration\/c-interop#dynamic-vs-static-linking\" rel=\"nofollow noopener\" target=\"_blank\">documentation<\/a> you can also use static ones.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2bcf8e2 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"2bcf8e2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-shipping-address\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-31dde7e elementor-widget elementor-widget-text-editor\" data-id=\"31dde7e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>You have to decide where to put your native code. I created a directory <code data-no-translation=\"\">native_code<\/code> in my project directory. But be aware that if you are planning to also compile for iOS devices (which I didn&#8217;t test) you should put your code into the\u00a0<code data-no-translation=\"\">ios<\/code> directory because the toolchain requires this and the other toolchains are flexible enough in this concern.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-292b242 elementor-widget elementor-widget-text-editor\" data-id=\"292b242\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>To make use of asynchronous callbacks, you also need to get the dart sdk and add some header and source files from <code data-no-translation=\"\">sdk\/runtime\u00a0<\/code>to your build. Another important point is that you have to use a C++ compiler if you adhere to the asynchronous examples provided by the Dart<br \/>team because they are using lambdas to hand over the work to be done from the library to Dart (see Function <code data-no-translation=\"\">MyCallbackNonBlocking<\/code> <a class=\"external-link\" href=\"https:\/\/github.com\/dart-lang\/sdk\/blob\/a4afb06\/runtime\/bin\/ffi_test\/ffi_test_functions_vmspecific.cc#L495\" rel=\"nofollow noopener\" target=\"_blank\">here<\/a>).\u00a0<\/p><p>This in turns requires you to deal with the name mangling introduced by the C++ compiler, otherwise <code data-no-translation=\"\">dart:ffi<\/code> will not find your functions. To do this you can mark your functions with a macro like (see <a class=\"external-link\" href=\"https:\/\/flutter.dev\/docs\/development\/platform-integration\/c-interop\" rel=\"nofollow noopener\" target=\"_blank\">https:\/\/flutter.dev\/docs\/development\/platform-integration\/c-interop<\/a>):<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ad1df76 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"ad1df76\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-c' data-no-translation=\"\">#define EXTERN_C extern &quot;C&quot; __attribute__((visibility(&quot;default&quot;))) __attribute__((used))\nEXTERN_C void myFunction(); <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1852093 elementor-widget elementor-widget-text-editor\" data-id=\"1852093\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>To integrate open62541, you need to also get those sources. I tested my application using the latest stable version 1.2. As part of my first steps I always compiled everything manually and just passed the path to the\u00a0<code data-no-translation=\"\">.so<\/code> file in my dart code. Of course this is not sufficient for a platform independent solution using different toolchains. So what you have to do here is to add open62541 as a dependency to your CMake files. As mentioned before, I tested my application only with shared libraries so in the case of open62541 you have to change that first in open62541 CMake file by setting the respective option to:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-bf9fd5d elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"bf9fd5d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-makefile' data-no-translation=\"\">option(BUILD_SHARED_LIBS &quot;Enable building of shared libraries (dll\/so)&quot; ON) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4d0ab9f elementor-widget elementor-widget-text-editor\" data-id=\"4d0ab9f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>I also had issues with the address sanitizer that I did not look into yet when using <code data-no-translation=\"\">flutter run<\/code> (<code data-no-translation=\"\">build<\/code>\u00a0was working though) so I had to disable this by commenting out some lines:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-528fed4 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"528fed4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-makefile' data-no-translation=\"\"># set(CMAKE_C_FLAGS &quot;${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}&quot;)\r\n# set(CMAKE_CXX_FLAGS &quot;${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}&quot;) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-405ba2a elementor-widget elementor-widget-text-editor\" data-id=\"405ba2a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>For more details about the setup have a look at the example application.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-3a9e360 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"3a9e360\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-3c66448\" data-id=\"3c66448\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-c595b78 elementor-widget elementor-widget-heading\" data-id=\"c595b78\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Implementation<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-89d5bb4 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"89d5bb4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<i aria-hidden=\"true\" class=\"hm hm-lotus\"><\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9b0f62c elementor-widget elementor-widget-text-editor\" data-id=\"9b0f62c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In this section I would like to go briefly through some details of my example implementation. Since we are about to use a foreign function interface to establish interoperation between C and Dart there are two perspectives here, the C and the Dart side. We will start with the native C\/C++ library.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-1a032c2 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"1a032c2\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-3775f8e\" data-id=\"3775f8e\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-50f2eef elementor-widget elementor-widget-heading\" data-id=\"50f2eef\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The native library<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-2d26cc9 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"2d26cc9\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f952e30\" data-id=\"f952e30\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-4714e92 elementor-widget elementor-widget-text-editor\" data-id=\"4714e92\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>As a starting point I used the open62541 example for asynchronous subscriptions that can be found <a class=\"external-link\" href=\"https:\/\/github.com\/open62541\/open62541\/blob\/master\/examples\/client_subscription_loop.c\" rel=\"nofollow noopener\" target=\"_blank\">here<\/a> and adapted that to my needs. In our example you can find the code in <code data-no-translation=\"\">native_code\/waterpump_ffi_lib.{h,cpp}<\/code>.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-9aa5dc4 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"9aa5dc4\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-04fc61b\" data-id=\"04fc61b\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-86e1383 elementor-widget elementor-widget-text-editor\" data-id=\"86e1383\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The first thing we need to adapt is the initialization. As this demo is only an experiment and by no means a generic open62541 wrapper, our C library has to contain some specifics regarding the business logic. In concrete terms this means that we need to add the subscriptions for the nodes we are interested in. I limited this to a very minimum that we want to use in our Flutter application: The current time, the fill state of the tanks and the target fill of tank 2.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-aea3e45 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"aea3e45\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-0d859a4\" data-id=\"0d859a4\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-f81552b elementor-widget elementor-widget-text-editor\" data-id=\"f81552b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Whenever there is an update to one of those nodes, a handler function (for example <code data-no-translation=\"\">handler_currentTimeChanged<\/code>) is called, which in turn triggers the callback to dart. These subscriptions and handler functions need to be initialised, which is done in the function <code data-no-translation=\"\">stateCallback<\/code> in this example.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-237bb5a elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"237bb5a\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-1f8faf8\" data-id=\"1f8faf8\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-93e7055 elementor-widget elementor-widget-text-editor\" data-id=\"93e7055\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The second thing is to move the open62541 endless loop from the main function to a regular function and provide a public function that can be called from the Dart side to start the background operation. For this I used a <code data-no-translation=\"\">std::thread<\/code> and provided the function that contains the open62541 endless loop.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-425f599 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"425f599\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-b9e028d\" data-id=\"b9e028d\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-4ebc4f6 elementor-widget elementor-widget-text-editor\" data-id=\"4ebc4f6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The final steps are to define the library interface for Dart (see functions marked with <code data-no-translation=\"\">EXTERN_C<\/code>) and to add things necessary for asynchronous callbacks from the ffi examples. I adapted those to make the code a bit more generic. For a real project one would of course put much more thought into this part; but to show how it works in general, this should be fine.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-642bcb2 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"642bcb2\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-a9e9bb0\" data-id=\"a9e9bb0\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-f8d7266 elementor-widget elementor-widget-text-editor\" data-id=\"f8d7266\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>What you can see in the handler functions is that everything that is \npassed over to Dart is allocated on the heap. So what must be taken care\n of in the context of the handler functions is memory management. Memory\n that is allocated here must be cleared later after everything has been \nprocessed, which means, that we need to do this in Dart. More on this \npoint in the next section about the Dart side.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-2aac38f elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"2aac38f\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-1618a78\" data-id=\"1618a78\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-5c8880c elementor-widget elementor-widget-text-editor\" data-id=\"5c8880c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In the concept section we briefly looked at how the mechanism for asynchronous callbacks works in general. Here I would like to add some technical details. First of all the ffi team introduced the typedef<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-15c763d elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"15c763d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-c' data-no-translation=\"\">typedef std::function&lt;void()&gt; Work; <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-148a189 elementor-widget elementor-widget-text-editor\" data-id=\"148a189\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>which is an abstraction used to hold a lambda that scopes a pointer to the data and a pointer to the Dart function to be called from the main Dart thread &#8211; the actual callback that will process the updated data on the Dart side. The body of the lambda just calls the callback function with the data as parameter. This lambda is then passed over to <code data-no-translation=\"\">notifyDart<\/code> which puts the lambda in a <code data-no-translation=\"\">Dart_CObject<\/code> and uses the function <code data-no-translation=\"\">Dart_Post_CObject_DL<\/code> (both of which are from the Dart SDK mentioned in the setup section) to actually add it as a microtask to Dart&#8217;s event queue.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-eca44ef elementor-widget elementor-widget-text-editor\" data-id=\"eca44ef\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>This whole construct might seem a bit confusing at first. But, as \nmentioned before, hopefully the ffi team will improve this in the future\n so it will get a little more convenient to use.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-cb79de6 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"cb79de6\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-e2be0b8\" data-id=\"e2be0b8\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-9cc1b16 elementor-widget elementor-widget-heading\" data-id=\"9cc1b16\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The Dart side<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-dd54754 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"dd54754\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-3322dee\" data-id=\"3322dee\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-9cfce8e elementor-widget elementor-widget-image\" data-id=\"9cfce8e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t<figure class=\"wp-caption\">\n\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"875\" height=\"410\" src=\"https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/1-YWLkxromkAfNXyro145prw.png\" class=\"elementor-animation-bob attachment-full size-full wp-image-4865\" alt=\"basysKom, HMI Dienstleistung, Qt, Cloud, Azure\" srcset=\"https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/1-YWLkxromkAfNXyro145prw.png 875w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/1-YWLkxromkAfNXyro145prw-300x141.png 300w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/1-YWLkxromkAfNXyro145prw-768x360.png 768w, https:\/\/www.basyskom.de\/wp-content\/uploads\/2021\/08\/1-YWLkxromkAfNXyro145prw-560x262.png 560w\" sizes=\"(max-width: 875px) 100vw, 875px\">\t\t\t\t\t\t\t\t\t\t\t<figcaption class=\"widget-image-caption wp-caption-text\">Image source: https:\/\/medium.com\/swlh\/common-bottom-navigation-bar-made-easy-flutter-199c9f683b29)<\/figcaption>\n\t\t\t\t\t\t\t\t\t\t<\/figure>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-6316706 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"6316706\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-dcb8e13\" data-id=\"dcb8e13\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-1ac5d54 elementor-widget elementor-widget-text-editor\" data-id=\"1ac5d54\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>On the Dart side there are also some initialization steps necessary that are implemented in the class that was added by the <code data-no-translation=\"\">flutter create<\/code> command.\u00a0<\/p><p>These are:<\/p><ul><li>Initialization of the Dart API, which is implemented as a synchronous call to a C function, which in turn calls <code data-no-translation=\"\">Dart_InitializeApiDL<\/code> from the Dart SDK<\/li><li>Initialization of the Dart port that is used to communicate between the background and the Dart thread<\/li><li>Looking up the functions from the C library used by Dart and storing a corresponding Dart function<\/li><li>Registering the callback functions, meaning that the respective Dart callback functions are communicated to the C library<\/li><li>Starting the background thread using the function mentioned in the previous section<\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-188017b elementor-widget elementor-widget-heading\" data-id=\"188017b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">ffigen<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6f902ed elementor-widget elementor-widget-text-editor\" data-id=\"6f902ed\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>This is a good place to briefly discuss another tool from the ffi ecosystem called <code data-no-translation=\"\">ffigen<\/code>. <code data-no-translation=\"\">ffigen<\/code> is a tool to generate bindings for Dart. It uses (besides other parameters) C header files to create a Dart class containing all the boilerplate necessary.\u00a0<\/p><p>This is a very nice tool, but unfortunately it does not cover every use case yet. In the context of my first (exclusively synchronous) experiments with open62541 I tried to automatically generate appropriate bindings using <code data-no-translation=\"\">ffigen<\/code>, but the tool does not handle <code data-no-translation=\"\">inline<\/code> functions &#8211; of which open62541 makes heavy use.\u00a0<\/p><p>So thinking about that and asking the very supportive team behind <code data-no-translation=\"\">ffigen<\/code> there are two options to deal with this. Either write a tool (or extend\u00a0<code data-no-translation=\"\">ffigen<\/code> itself, as proposed <a href=\"https:\/\/github.com\/dart-lang\/ffigen\/issues\/235\" rel=\"nofollow noopener\" target=\"_blank\">here<\/a>) to handle this, or manually create a thin wrapper in C that implements necessary abstractions and at least use <code data-no-translation=\"\">ffigen<\/code> to create bindings for that.\u00a0<\/p><p>For the asynchronous example covered in this post, we can also not benefit from the tool because we are not using plain C here. In the future there might be support for C++ as well as discussed <a href=\"https:\/\/github.com\/dart-lang\/ffigen\/issues\/212\" rel=\"nofollow noopener\" target=\"_blank\">here<\/a>. So <code data-no-translation=\"\">ffigen<\/code> is definitely an interesting project you should keep on your watchlist but we will not discuss it any further in the context of this post.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-afd6d7b elementor-widget elementor-widget-heading\" data-id=\"afd6d7b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Using the plugin<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7fc0e37 elementor-widget elementor-widget-text-editor\" data-id=\"7fc0e37\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Apart from the initialization, two things need to be done in our binding class.\u00a0<\/p><p>The first is implementing the callbacks to process the data updates and providing the data to the actual app using your plugin. As mentioned before, one important thing to be aware of when implementing the callbacks is memory management. Data has been allocated in the C handler functions and is eventually used in the Dart callbacks we are talking about here.<\/p><p>So after the data has been processed, we need to free the respective memory. This can be done with <code data-no-translation=\"\">dart:ffi<\/code> using the top-level constant <code data-no-translation=\"\">malloc<\/code> which is an <code data-no-translation=\"\">Allocator<\/code> providing the method <code data-no-translation=\"\">free<\/code> to release memory on the heap.\u00a0<span style=\"color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), PT+Sans+Narrow; font-weight: var( --e-global-typography-text-font-weight );\">In the callbacks we take the data from the heap, create appropriate Dart data structures or variables and release the allocated memory.\u00a0<\/span><\/p><p>After that the plugin needs a way to distribute the data to your app. For this I used Dart streams which can be used conveniently by stateful widgets in your Flutter app. But of course there are plenty of more sophisticated options in the Flutter universe to implement this<em>.<\/em> From here you can just instantiate the plugin in your app and listen to the respective streams. But don&#8217;t forget to do that within <code data-no-translation=\"\">setState()<\/code>\u00a0<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6dfd597 elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"6dfd597\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon\">\n\t\t\t<svg aria-hidden=\"true\" class=\"e-font-icon-svg e-fas-smile-wink\" viewbox=\"0 0 496 512\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M0 256c0 137 111 248 248 248s248-111 248-248S385 8 248 8 0 119 0 256zm200-48c0 17.7-14.3 32-32 32s-32-14.3-32-32 14.3-32 32-32 32 14.3 32 32zm158.5 16.5c-14.8-13.2-46.2-13.2-61 0L288 233c-8.3 7.4-21.6.4-19.8-10.8 4-25.2 34.2-42.1 59.9-42.1S384 197 388 222.2c1.7 11.1-11.4 18.3-19.8 10.8l-9.7-8.5zM157.8 325.8C180.2 352.7 213 368 248 368s67.8-15.4 90.2-42.2c13.6-16.2 38.1 4.2 24.6 20.5C334.3 380.4 292.5 400 248 400s-86.3-19.6-114.8-53.8c-13.5-16.3 11.2-36.7 24.6-20.4z\"><\/path><\/svg>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-b39d111 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no\" data-id=\"b39d111\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-bbef256\" data-id=\"bbef256\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-24978c5 elementor-widget elementor-widget-heading\" data-id=\"24978c5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Conclusion<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8b99e82 elementor-widget elementor-widget-text-editor\" data-id=\"8b99e82\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In summary we can say that in addition to the generally pleasant experience using Flutter and its ecosystem of tools and packages we were positively surprised with the maturity of <code data-no-translation=\"\">dart:ffi<\/code>. Of course this kind of task is much more straightforward with a framework like Qt &#8211; because there is no language barrier &#8211; but it is nice to see that it is already possible to combine Flutter with native code and profit from Flutter&#8217;s tooling, its platform support and its liberal BSD license.\u00a0<\/p><p>Although this demo project is only an example, and real projects will probably bring up some more challenges, it is interesting to see that it works in general and that more work is put into both: better support for more platforms and the interoperation to native code.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>In this blog we explain how to provide OPCUA open62541 as a ffi plugin to Dart and use it in Flutter. We highlight the pros&#8217; and give you the cons.<\/p>","protected":false},"author":18,"featured_media":3980,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1,2,803,7,802,183],"tags":[223,122],"class_list":["post-4861","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein","category-blog","category-flutter","category-general","category-newsletter-2025-01","category-open62541","tag-flutter","tag-open62541"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/4861","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/users\/18"}],"replies":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/comments?post=4861"}],"version-history":[{"count":26,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/4861\/revisions"}],"predecessor-version":[{"id":10801,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/4861\/revisions\/10801"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media\/3980"}],"wp:attachment":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media?parent=4861"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/categories?post=4861"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/tags?post=4861"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}