Article
Case Study: Capturing Quality Images for ML Algorithm
In this article, we provide a brief overview of Flutter, a relative newcomer to cross-platform frameworks, consider why you may want (or may not want) to use it for your project, how it compares with other cross-platform frameworks, and share some things we have learned using it to develop medical device software apps at Orthogonal.
Let’s say you’ve decided to invest in a cross-platform development tool. You’re looking for the most efficient way to code a mobile application that will work on both iOS and Android without having to program it twice. If you want to develop cross-platform apps – for any number of industries or uses – Flutter is an excellent choice. This blog seeks to give an overview of Flutter: how it works, what sets it apart from other cross-platform development tools, and why we here at Orthogonal like using it. It’ll give you all the relevant information as to why Flutter is worth learning and using.
We’re all familiar with the two most popular smartphone operating systems: iOS by Apple (iPhone) and Android by Google (various OEMs, such as Google, Samsung, etc). iOS is more popular in the United States, but Android dominates worldwide. Developing for each platform requires languages and tools specific to each ecosystem. So, to make a software application which runs on both, developers traditionally needed to maintain two separate code bases, each one native to iOS or Android. Moreover, due to the inherent differences in the design paradigms of each, the look and feel of an app running on one platform may be noticeably different than the version of the app running on the other.
Cross-platform frameworks were created to help solve this problem: Code once in a single language using a single development environment, and from this single code base, be able to deploy to and run on both iOS and Android smartphones. This helps significantly reduce development resources and time and establishes a more consistent UI/UX. Though native development still currently dominates the industry, cross-platform development’s appealing advantages have helped it grow in popularity. In a 2020 Statista of 19,696 developers, 1/3 reported using cross-platform frameworks1, and of these, 39% reported using Flutter, right behind React Native at 42%. By 2021, Statista’s survey of 31,743 total worldwide respondents shows that Flutter has taken the lead at 42% over React Native at 38%. This is remarkable given that Flutter was released only in late 2018.
Google released the first stable version of Flutter as an open-source project in December 2018, which at the time only supported iOS and Android. With the release of next major stable version Flutter 2 in early 2021, support has been extended to many more platforms, including web browsers, macOS, Linux, and Windows2.
Developing in Flutter offers a significant force multiplier, since coding a single app will in principle enable it to run on all these platforms. Google has spent considerable time and resources developing Flutter to succeed where other cross-platform development tools have struggled. Their investment in Flutter is clear in its flexibility, power and ease of use.
But don’t just take it from us. Let’s hear what Google has to say about Flutter in their own words:
Before Flutter, there were many cross-platform frameworks available, including React-Native, Cordova, Ionic, and Xamarin. Flutter entered the cross-platform ecosystem trying to solve problems differently than what came before, offering important advantages.
Flutter development uses the Dart language, which is also a Google open-source project. It compiles to native code: 32-bit and 64-bit ARM machine code for iOS and Android, JavaScript for web browsers, and Intel x64 for desktop devices. In practice, this makes it possible for code written in Dart to potentially execute at a native-like level of performance. This sets Flutter apart from JavaScript-based cross-platform frameworks such as React-Native, which are not compiled to native code and thus may have performance limitations.
Flutter does not use the native platform’s SDKs to render UI/UX elements. Rather, it uses Skia5, the hardware-accelerated 2D graphics engine that powers Chrome and Android, to draw directly to the platform canvas. The practical benefit of this design is a glitch-free, jank-free graphics experience at native speeds (e.g., 60+ Hz refresh rate). Jank is a term which refers to interruptions in graphics rendering which noticeably degrade the UI/UX. Apps created with JavaScript-based frameworks such as React-Native are prone to jank, because the computational bridge between JavaScript and the platform SDKs and resources are computationally expensive and hence slower. Flutter bypasses this limitation by eliminating the need for a bridge and achieves native-like performance by rendering directly to the platform canvas.
Flutter gives developers pixel-perfect control of the canvas. Because it uses Skia instead of the native platform’s SDK, all platform UI constraints are eliminated, giving developers the freedom to composite UI elements without limits. At the same time, to accommodate any requirements to conform to platform specific UI, Flutter offers a library full of standard UI elements (called widgets) in both iOS (Cupertino) and Android (Material) design paradigms, which include pixel-perfect reproductions of native UI elements. Or developers can create their own custom widgets. This extraordinary flexibility makes Flutter ideal for projects which specify non-conforming, brand-first design styles which value consistency across platforms.
Flutter allows developers to create apps with a single code base to run on multiple platforms, but also gives them the development tools to optimize productivity. This makes Flutter ideal for both longer term Enterprise applications, as well as for fast prototyping. Development builds are compiled using Just-In-Time compilation, which enables fast, stateful reloads of code changes without the need to re-compile and re-deploy the app, allowing developers to see the results of code changes without needing to restart the app or lose the app’s state, which normally cost a substantial amount of time. On the other hand, release builds are compiled using Ahead-Of-Time compilation which is optimized for the fastest native output and smallest runtime.
Choosing to develop cross-platform always involves trade-offs. Despite its many advantages, Flutter may not be the best development framework option for some important reasons.
Flutter was designed to provide performance and UI/UX as near to native apps as possible, important advantages which help make it stand out among other cross-platform frameworks. Nevertheless, if true native performance and conformance to platform specific UI/UX are required, then coding the app natively should be considered. For example, if the UI/UX needs to strictly conform to Apple’s Cupertino and/or Android’s Material Design paradigms, then this would be best achieved with a native app. Likewise, native apps should be preferred for applications requiring the utmost speed and memory performance or optimal utilization of platform services, such as for high-resolution AR/VR and gaming.
The file size of an app coded with Flutter is necessarily larger than a comparable native app. Depending on the code complexity, the difference can be significant, possibly several times larger. With earlier framework versions, a Flutter app would have been larger than a comparable React Native app, but later version improvements have helped reverse the situation. Nevertheless, native apps will always be smaller and better optimized. Hopefully, improvements in compiler optimizations will continue to decrease Flutter apps size.
Since most apps are relatively small, under one hundred megabytes, app size may not be a big factor in most cases. But it may become an important consideration for apps which are significantly larger, for example, over several hundred megabytes. More code requires more processing, which impacts performance.
Flutter is still relatively young compared to other cross-platform frameworks. Some find the novelty of a new framework an opportunity to engage and innovate, but there are practical costs to consider when choosing a relatively less mature framework. For instance, developers using Flutter may expect frequent framework updates with significant changes, which require more effort for them to maintain their code to avoid breaking changes. Moreover, though continuously growing, the Flutter community is still relatively small and thus fewer libraries are available compared to other frameworks.
Dart predates Flutter, originally created by Google to be an improvement on JavaScript, but is still relatively new. Most developers should find learning Dart straightforward, since it is a C-style language with familiar syntax, usage, and constructs. However, a learning curve always exists when learning anything new, and some may simply rather not want to invest in learning Dart due to constraints on time, effort, or motivation. Other cross-platforms use more popular languages, such as JavaScript (React Native, Cordova, Ionic) or C# (Xamarin) and may thus appear to be more initially accessible. Likewise, the market demand for Flutter developers is still low, so developers may be less motivated to learn Dart and Flutter considering available career opportunities.
At the organization level, finding enough talent with existing Dart expertise to form a solid Flutter team may involve extra effort and resolve. More likely, an organization may need to resort to asking developers unfamiliar with Dart to learn it to be able to work with it – requiring investment in development resources to get past a learning curve with the accompanying risk of uncertain market demand for a young, relatively unproven technology. Fortunately, all signs, including the above-mentioned Statista survey, suggest Flutter and Dart are rapidly gaining popularity and acceptance. Companies of all sizes are trusting it to be the basis of their flagship apps as well as more innovative apps7. The future does look bright for Flutter.
Right now, you’re probably wondering something along the lines of, “Flutter sounds really great, but what makes it worth using?” Understanding some of the principles behind how smartphone apps work natively and in cross-platform frameworks helps to highlight Flutter’s distinctions. Figure 4 summarizes the key points in the following discussion.
The center of the figure represents the essence of the smartphone platform with the following main parts:
On the right side of the figure are JavaScript frameworks such as:
The left side of the figure depicts apps which are compiled to native machine code, which include:
Here’s how all of the tools, platforms and languages above stack up:
React Native apps are written in JavaScript, which requires a bridge to access native UI/UX components and platform services. Being relatively inefficient, this bridging may cause significant performance lags; for instance, janky UI, a characteristic disadvantage of these types of apps.
WebView apps are also written in JavaScript. They are effectively webapps, relying on the native web-view application of the platform for UI/UX, as well as JavaScript bridging to access platform services. Thus, these types of apps generally have the same level of performance, UI conformity, and UX responsiveness as web browsers, which tend to be noticeably poorer compared to natively written apps.
Native apps are the gold standard of apps, written in Swift and/or Objective-C for iOS and Kotlin and/or Java for Android and compiled to native machine code. They are developed with the languages and tools in an environment most true to the platforms where they will run, using the intended platform widgets and services as intended. Thus, they have the best performance and memory profiles possible.
Written in C#, Xamarin Native apps are also compiled to native machine code, and also use native UI/UX components and platform services. Performance is thus at near-native levels with exactly conforming UI/UX. However, due to the nature of the compilation process, the resulting app runs less efficiently, requiring more memory and processing than its native counterpart. Because it uses native UI/UX components, developers are limited to using platform-specific design paradigms. Moreover, some time may be needed before Xamarin updates to the latest features and functionality of the constantly evolving native frameworks. (Another version of Xamarin called Forms also uses C#, but it works more like the JavaScript-based React Native, described above.)
Flutter’s Dart code is compiled to native machine code. Flutter uses the Skia engine to directly render to the platform’s canvas and handles events. Performance is free of jank and at near-native levels. This makes it perform better compared to JavaScript based apps. Although it does not use native UI/UX components, it does make available pixel-perfect replications of them if desired. Otherwise, developers are free to design their own to pixel-perfect precision. This makes it more versatile than Xamarin apps. Flutter uses platform channels which utilize fast messaging processes to access platform services, again resulting in fast performance and responsiveness. All of this results in a smooth, near-native experience and offers all the advantages of cross-platform development.
At Orthogonal, we are using Flutter to code medical device applications. Overall, our experience has been positive. We have found Flutter to be versatile, flexible, reliable, straightforward to use, and saves time, costs and resources in cross-platform development – making it very appealing to our clients. While challenges exist with using any framework, our talented development team has so far not encountered any which were insurmountable or provided an inferior experience compared to fully native development.
Thanks to its familiar C-style syntax and constructs, we found Dart and Flutter intuitive and easy to learn. Many tutorials and videos can be found online which can get developers up to speed relatively quickly. Google has also provided clear, accessible, and even entertaining documentation and videos to support learning the language and framework8. A sizable community exists to provide support via sites such as Stack Overflow, GitHub, and Reddit.
Google conducts a quarterly survey about the state of Flutter. Over 7,000 developers responded to May 2021 survey10, and 92% of those respondents said they were positively satisfied with Flutter; the core framework and ease of using Dart were rated positively by 92% and 88% of respondents respectively. This is a testament to Google’s efforts to make the framework intuitive and accessible.
Because Flutter is relatively young, development is actively ongoing, and framework updates occur frequently. Some changes are quite significant and not backwards compatible, such as the null safety feature introduced in Flutter 2. With each major change, extra effort is required to keep our projects’ code up to date. The same is required for each library dependency, for which we may have to wait on their maintainers to update. Along with such updates is the accompanying overhead of thorough regression testing. All of this adds time and effort to the development load.
To reduce the frequency of possible breaking changes and consequently delaying normal development, we have found it helpful to fix the SDK at a certain version until attention can be devoted to upgrading and testing. Several minor versions may be skipped in the process. This practice is common and particularly helpful when working with rapidly growing frameworks such as Flutter. For example, early adopters of iOS Swift likewise learned to live with frequent and significant changes during its growth.
The motto of cross-platform frameworks is: “Code once, run everywhere.” In practice, it is more like, “Code once, debug everywhere.” With Flutter, we were pleased to find that most functionality runs uniformly without requiring any platform specific customization. However, some core services such as Bluetooth connectivity, permissions flow, security levels, networking, and persistence simply work differently in each platform, and thus require platform specific coding. Many of the differences are transparently handled by the third-party libraries we depend on, but some require direct attention.
For example, we found specific Bluetooth connection errors occurred only in Android and varied even between different Android phone brands. This is understandable given the greater variety of Android OEMs and a fact of life in Android development. Android also provides a greater level of detail of states during user permissions prompts for location services and Bluetooth connectivity. Furthermore, due to iOS’s stricter security, the iOS version of the app cannot automatically enable Bluetooth for the user, thus requiring different user flows to handle this case. Another example is that the device firmware update (dfu)11 library by Nordic which we use advertises the dfu device name differently depending on platform. We have also encountered instances when a version upgrade of a dependency broke functionality in Android but not in iOS.
Fortunately, Flutter provides a simple way to determine which platform the app is running on, so straightforward conditional branches may be used to handle platform differences, for example, “If running on Android, do this; otherwise, do that.” Overall, in our projects relatively little code is devoted to resolving platform differences, but differences do exist, so extra time for careful testing, debugging, and fixing issues when they arise should be budgeted.
Third-party library dependencies (packages) significantly reduce development time by leveraging the work of the Flutter community to handle operations common to many applications. There are two kinds of packages: 1) a Dart package, which is more general, written in Dart, and uses only Flutter for cross-platform functionality, and 2) a plugin package, which provides platform specific functionality and, in addition to Dart, can be written directly in native languages, such as Swift and Objective-C for iOS, and Kotlin and Java for Android, as well as all the other supported platforms. Where plugins may not exist or not be ideal for certain requirements which require platform specific services, the framework makes it easy for developers to write their own plugins and make them available to the community12.
Given Flutter’s being relatively young, the number and variety of packages are smaller than for some other frameworks, though growing. Nevertheless, a variety of options currently exists to support much of the basic functionality needed in our applications.
Particularly important for us is Bluetooth Low Energy (BLE) support since our apps commonly use BLE to connect to medical devices. Several packages provide easy to use and reliable BLE support, such as Flutter Reactive BLE13. Examples of other plugins we use are Dio14 for networking, Dime15 for dependency injection, Hive16 for local persistence, and Protobuf17 for data serialization.
We declare package dependencies as Software of Unknown Provenance (SOUP) to satisfy FDA regulatory requirements as part of our quality management documentation.
We hope you’re as convinced as we are that Flutter is one of the best cross-platform development tools out on the market today. As Flutter continues to grow, it will only become more versatile and stable as it matures. It is an impressive and highly ambitious framework, offers distinct advantages over existing cross platform frameworks, but also straightforward to learn and use for all types of projects. We look forward to continuing to use it.
If you’re curious about creating an app, we would love to hear from you about your project ideas and explore whether we can help.
David Lin leads the software engineering discipline at Orthogonal. He has extensive experience in the technical implementation of complex scientific, mathematical, computational, e-commerce, and internet projects. He is highly conversant with the exacting engineering needs of high-availability software, having previously worked at BMW on its connected auto software solutions and at Microsoft as a developer of a web browser installed on millions of cell phones. David first honed his quantitative computing skills while earning a Ph.D. in Physics and Astronomy from Northwestern University.
References 1 Liu S. Cross-platform mobile frameworks used by global developers 2021 | Statista. Statista. https://www.statista.com/statistics/869224/worldwide-software-developer-working-hours/. Published 2021. Accessed October 25, 2021. 2 Supported platforms. Flutter.dev. https://flutter.dev/docs/development/tools/sdk/release-notes/supported-platforms. Published 2021. Accessed October 25, 2021. 3 GitHub - flutter/flutter: Flutter makes it easy and fast to build beautiful apps for mobile and beyond. GitHub. https://github.com/flutter/flutter. Published 2021. Accessed October 25, 2021. 4 Dart. GitHub. https://github.com/dart-lang. Published 2021. Accessed October 25, 2021. 5 Skia. Skia. https://skia.org. Accessed October 25, 2021. 6 Moore K. kevmoo - Overview. GitHub. https://github.com/kevmoo/. Accessed October 25, 2021. 7 Showcase. Flutter.dev. https://flutter.dev/showcase. Accessed October 25, 2021. 8 Flutter documentation. Flutter.dev. https://flutter.dev/docs. Accessed October 25, 2021. 9 Star history. Star-history.t9t.io. https://star-history.t9t.io/#facebook/react-native&flutter/flutter. Published 2021. Accessed October 25, 2021. 10 Lee J, Hasnany M. What can we do better to improve Flutter? — Q2 2021 user survey results. Medium. https://medium.com/flutter/what-can-we-do-better-to-improve-flutter-q2-2021-user-survey-results-1037fb8f057b. Published 2021. Accessed October 25, 2021. 11 flutter_nordic_dfu | Flutter Package. Dart packages. https://pub.dev/packages/flutter_nordic_dfu. Published 2021. Accessed October 25, 2021. 12 Developing packages & plugins. Flutter.dev. https://flutter.dev/docs/development/packages-and-plugins/developing-packages. Accessed October 25, 2021. 13 Flutter Reactive BLE | Dart packages: https://pub.dev/packages/flutter_reactive_ble. Published 2021. Accessed October 25, 2021. 14 dio | Dart Package. Dart packages. https://pub.dev/packages/dio. Published 2021. Accessed October 25, 2021. 15 dime | Dart Package. Dart packages. https://pub.dev/packages/dime. Published 2021. Accessed October 25, 2021. 16 hive | Dart Package. Dart packages. https://pub.dev/packages/hive. Published 2021. Accessed October 25, 2021. 17 protobuf | Dart Package. Dart packages. https://pub.dev/packages/protobuf. Published 2021. Accessed October 25, 2021
Related Posts
Article
Case Study: Capturing Quality Images for ML Algorithm
Article
How Design Can Improve Ratings for Medical Device Apps
Article
Bluetooth Trends in Smartphones: Effects on Medical Devices
Article
Climbing the Mountain of Regulatory Documentation for SaMD