Is Flutter suitable for enterprise-class companies?
In order to answer this question, it is worth starting with at least a rough idea of basic technology expectations needed to create a solution targeted primarily at mobile devices:
- We don't want technology to limit us at any stage of the application life cycle and stand in the way of making our development vision a reality. This includes both the functional capabilities and the range of devices on which the application will ultimately run.
- We don't want the technology to be niche, unpopular, with symbolic and short-lived support and a small community of specialists.
- We want the technology to let us create beautiful, fast and stable applications that have a chance of making their mark on the market.
- We want the technology costs to be as low as possible, without compromising on quality or functional concessions. What’s also at the top of our interests is the speed of delivery of the first stable version of the application.
Will Flutter meet the above requirements? More than 5 years after the release of its first stable version, the Flutter ecosystem has grown strongly. By ecosystem here I mean, among other things, the availability of libraries, the supply of programmers on the job market, the activity of newsgroups or the stability of the tools used by programmers. It is no longer a niche solution that we treat more as a curiosity, but a mature tool for building a complete product. This is evidenced by the many successful applications developed using it, including those we build at iteo. Below I will try to introduce the subject a little better.
Why bet on Flutter?
Every technology has its advantages and disadvantages. Personally, however, I believe that in the case of Flutter, the pros in 2023 are far more numerous, and many of them translate directly into addressing the needs mentioned in the introduction. Below, I will select a few of them and introduce them a bit.
Flutter offers developers a very convenient way to create applications. In a nutshell, the whole thing is built from reusable components (so-called widgets). One of the main assumptions guiding the design of the solution’s architecture is the introduction of independence between its various layers. Without going into technical details, this results in the ability to replace any elements of the application with particular ease, without risking undesirable behavior in other parts of the application. Why is this important? One of the biggest advantages of this approach is that if, for example, support for the library in use ends, we can, in most cases, painlessly replace it with another. It is also natural that at some stage of the application’s life cycle we will want to make some changes that may involve major visual alterations or adding new, often complex modules. It is also worth mentioning that we can integrate Flutter with applications (or parts of them) written natively. This is important because when we need to perform something very specific, we don’t have to worry that Flutter doesn’t enable it.
By choosing a technology that supports avoiding the creation of a thicket of dependencies and providing many gateways out of emergency situations, we can weave further plans for the development of our system without fear. By doing so, we also drastically reduce the risk of being limited by technology in the future. Flutter is just such a technology, enabling all the things described above and working well even in advanced projects.
Flutter, despite its short presence on the market, is doing very well in this aspect. Over the past five years it has managed to build a stable position. According to the 2021 Stack Overflow Developer Survey, Flutter was already the second most popular platform among developers. In 2022, it generated practically twice as many queries on Stack Overflow as its biggest competitor, React Native:
In Google Trends we can nicely see the moment when Flutter started to become more popular than React Native (April 2020):
It is estimated that in 2020, Flutter was used worldwide by more than 2 million developers who created more than 500,000 apps, with a total of 11 billion downloads. This also translates directly into the number of available libraries that we can use in projects. Every year there are more and more of them, a large part of which are actively developed by the community and have very good documentation. Currently, it is very easy to find blogs, discussion forums, topic groups or training materials that are related to Flutter.
High performance in a beautiful design
One of Flutter’s features is that all the elements you see when you launch the app on the device’s screen are drawn by it from scratch – pixel by pixel. This is a peculiar approach, since most other hybrid technologies use native components provided by the system. Self-drawing has advantages and disadvantages, but in this paragraph I will focus on what we gain by doing so (problems will be discussed in the section on risks).
Thanks to the fact that we don’t have to worry about introducing additional communication between code written in Dart (the programming language used to develop Flutter applications) and native interface components, the whole solution is compiled directly into machine code. Thus, despite working in hybrid technology, we gain very high performance of the created applications. For the end user, they will be in the vast majority of cases as fast as those written natively (that is, separately for Android and iOS).
The next benefit of self-drawing the interface is the great ease of creating smooth animations, which, when used well, definitely improve the end user’s perception of the overall solution. A consistent interface across all platforms is very suitable for all types of client and multimedia applications.
Thanks to the above-mentioned solutions, applications created in Flutter run smoothly even on much older devices, whose glory days are long behind them. Choosing this technology, we can safely assume that we will be able to target a very wide audience (however, pay attention to the minimum supported version of the operating system by the latest releases of the framework and the libraries used).
It’s also important to note that over the past few years of commercial work in Flutter, I’ve observed virtually no problems with its stability. If you keep an eye on what external dependencies you add to your project, and don’t leave a technology debt (such as using components marked “deprecated”), upgrades to new versions of the framework should pass painlessly.
The next feature of Flutter is to have a common code base (especially business logic; if we want the interface, can be written separately) for all platforms.
What does this actually mean? Among other things, we can mention such issues as:
Compared to applications written natively, we have one, not two (iOS and Android, or even additionally Desktop and Web) separate applications, which reduces the effort needed at each stage of production and implementation.
We reduce the risk of errors in the implementation of logic (where such problems are often difficult to detect, because they are based on seemingly minor differences between one platform and another).
Depending on the scale, the application can be handled by a single development team, which drastically reduces the risk of communication problems.
Less coordination meetings.
We do not need to prepare separate UI/UX for Android and iOS.
Simplified publication and update process.
Simplified issue tracking process.
The above-mentioned issues translate directly into an approximately 30% lower cost of development and maintenance of the hybrid mobile application to hit Android and iOS, compared to its native competition. Projects developed with Flutter are characterized by an optimal TCO, resulting mainly from the increased level of efficiency in the production process, such as the involvement of one developer to work in the indicated area, instead of two separate ones (for the case of Android and iOS).
When choosing Flutter, we don’t decide between the quality and cost of the solution. Of course, there are projects that are much better created from scratch as native (more on this in the section on threats), so it is important to simply choose the right technology, tailored to our needs. Nevertheless, I personally believe that this is a market minority and the number of modern hybrid applications will increase.
Risks and how we can address them
The old saying goes that every stick has two ends. This is also the case here. Among other things, our task in selecting technology for a project is precisely to manage the risks carried with each choice. Below I cite some selected risks, including those we often hear in discussions about the differences between hybrid and native technologies.
The problem actually occurs for every possible technology, although with varying frequency. At some point, it is likely that we will lack some useful library (or, for some reason, we will not want to use an existing one) and we will have to deal with this problem. As I wrote in this text, the availability of libraries for Flutter grows practically every day. Solutions known from native environments are ported to it, for example, or new ones dedicated typically to it are created. Nevertheless, due to its relatively short presence on the market, it may happen that we lack something dedicated to our needs.
Fortunately, nothing prevents us from creating such a solution on our own, or from using those available for native iOS and Android apps, and then through so-called Platform channels to communicate them with our code written in Dart. However, this already requires a bit more work and at least basic programming skills on the native side (which doesn’t change the fact that we still need less effort than creating two separate apps).
The good news is that all the standard libraries needed to create classic mobile applications in Flutter exist and are doing well.
Different Look & Feel
As I wrote earlier, Flutter draws all elements visible on the screen independently on a so-called canvas. This results in the fact that the components available for use by the programmer (e.g. buttons or navigation bars) may differ from the native ones. This can be particularly noticeable when, for example, a new version of the operating system comes out, with a slightly refreshed UI, and Flutter has not yet had time to adapt these changes.
The difference can also be seen in applications that combine Flutter screens with native ones. In extreme cases, it may be that one screen (Flutter) will look completely different from the next (native). There may also be differences in animations or icons.
The above problems are more likely to occur on iOS because Google is updating its Material Design adaptive component libraries in parallel for Android and Flutter. iOS components, on the other hand, have their own style and a Flutter app may or may not mimic it (such as Cupertino’s widget library).
Within applications developed with Flutter, we can address these issues in various ways. One example is to use only pre-made widgets from the Material Design palette throughout the application, regardless of the platform on which it runs. Another, actually the most common way out of this situation, is to create our own complete set of components that will always behave and look the same (e.g. InPost Fresh app). This allows us to avoid any inconsistencies, but also makes our application stand out from the competition.
Advanced hardware platform integrations, support for 3D or AR/VR graphics
For all basic hardware integrations like localization, camera, microphone, bluetooth or file system, there are official libraries available, and as a rule, we shouldn’t worry about it in any way. They work very well and the programmer doesn’t even have to wonder what’s going on underneath. However, if the integrations with the hardware platform are more demanding (e.g. embedded hardware, complex processing of photo or video footage, advanced use of the device’s sensors) and they are a large part of our application, we should consider whether Flutter is definitely the right choice for us. Of course, we are able to handle it, at least by writing extensive plug-ins and native screens, but personally, I don’t know if there is a point in having most of the code on the native side and only the common UI in Flutter.
The same goes for 3D graphics or at least AR/VR implementations. Flutter is incredibly fast when it comes to drawing a 2D image on a canvas (that is, all the classic interfaces and animations). On the other hand, when it comes to real-time image processing, the situation gets a bit complicated. We will probably again have to reach for native solutions and that’s where to leave most of the budget.
As long as the above elements are just one of the many modules of our application – it’s not a problem. It can be done natively and integrated with Flutter. On the other hand, if it is the core of our application, I would rather suggest going for native solutions.
Google's dropping of the project
Google is currently investing heavily in the development of the entire Flutter ecosystem, as well as developing some of its own applications using it (e.g. Google Pay, Google Classroom). The chance of abandoning a project that has so quickly gained the support of millions of developers around the world is, to put it bluntly, low. This is the natural order of things in the IT world – everything evolves quickly, including the technologies we use to create applications. In place of the older ones, come new and better ones. In 2023, Flutter is just that – new and better.
What demanding challenges have we faced?
The following selected examples are from real-life applications we have implemented for our clients. Each project carries slightly different risks, which we should consider individually.
Integration with Unity engine
The Unity engine, known for many games, does not provide dedicated support for Flutter, and as a result, there are no official and well-supported libraries that would allow us to comfortably use its capabilities. The topic of such integration came to us when Flutter was still in version 1.x (that is, around 2020), and in the IT world, more than two years is a long time.
In order to cope with the challenge, we created our own so-called fork of the library using native solutions, to which we made the required adjustments ourselves. This allowed us to achieve the desired effect, namely a simple character creator, allowing the user to edit his 3D avatar.
Advanced Bluetooth integration with sensor data synchronization
The biggest integration challenge we had (and a real test of Flutter’s capabilities in this area) was the opportunity to undertake creating an application dedicated to skiers. Its main task is to analyze the user’s skiing technique, as well as provide instructions and suggest specific exercises based on information provided from connected sensors.
The challenge was to use two tags strapped to the skis which were in constant connection with the phone. The phone itself, with the app running, was treated as the third tag. We first had to ensure that the appropriate channels were supported between the native libraries for handling the tags on the skis and the Dart code. Then, we collected real-time data from the phone from, among other things, the barometer, gyroscope, accelerometer and GPS – with GPS being the most important for us, since the tags themselves did not have it. At the time, for such demanding support for many of these sensors, we had to write our own implementation of it, as the libraries available for Flutter were not sufficient.
One of the biggest problems, typical for the entire mobile application market, was the fact that the data flowed in a slightly different way practically from each model of phone (a classic example of fragmentation). On the iPhone, for instance, there are only a few frequencies with which this data is emitted from the sensors (unfortunately, every model can vary). On the other hand, on Android, for example, there were interruptions in the data emitted, differences in the format of the data, and the handling and stability between products from different manufacturers could vary widely. Particularly problematic was equating the GPS track recording with the data that was returned from the tags on the skis (including segmented speed measurement, direction of travel, distance between turns, maximum overloads).
To deal with all these problems, one of the things we needed to do was to write a special engine that synchronized and equalized the frequency to that on which the tags themselves operated regardless of the phone model.
Integration with an engine written in C++
All the data downloaded and already synchronized in the paragraph above needed to be sent to a special independent engine written in C++. From time to time, the engine returned information about what was happening on the skis (including whether someone had turned, in what direction, at what speed, how much he was leaning). At the end of the downhill, each time the data had to be saved and shown to the user, and then synchronized with the database on the server. In addition, it is worth mentioning that the application had to cope very well in offline mode, since on the slopes we can often encounter prolonged lack of signal coverage.
In order to cope with this, we had to convert all the collected data into a special binary format, and only then pass it on. To achieve this, we used the dart:ffi package to create a special interface between Dart and C++ code, which allowed us to establish a two-way data exchange channel.
Advanced use of maps
One of the most interesting problems we had to deal with (in the same application) was drawing a descent route on a map. In theory a trivial task, yet in practice the recorded downhill routes often consisted of more than 1000 points over a very short distance. All these points had to be displayed as a continuous line of descent, and each of them could be independently selected by the user to check the data read there by the sensors.
This required us to write a special algorithm to optimize the handling of so many points on a small portion of the map. Without it, selecting a particular point, even on a very powerful device, took the app several seconds, and the latest (for those times) iPhones were already clipping when opening the preview of the saved downhill route.
So, who is this Flutter suitable for?
The last 5 years of Flutter’s presence on the market have shown how a well-thought-out technology is able to quickly gain enormous popularity. Particularly important in this case is that this popularity is not the result of intensive marketing or momentary “hype.” It is a popularity gained through the successful use of Flutter in a wide range of projects of varying scales: from simple MVP applications, to minor ones resulting from a momentary need, to complex, commercial systems used on a large scale that will be maintained for years to come.
So is Flutter suitable for enterprise-class companies? Of course! It has all the necessary features that a trustworthy technology should have. We just have to remember to be guided by our needs first and foremost when choosing it, and use it for those projects for which it was created. Its flexibility and simplicity allows us to use it in a project of really any scale. It doesn’t matter if you plan to create a banking application, a logistics app, one that helps manage production or an online store. Flutter will work well in any of these (and many other) cases.