Working with mutable collections is always risky because of their possible side effects. Dart proposes An editable ListView as an integrated solution, but it is not 100% reliable because it may fail at runtime. Instead, we chose to use The package fast_immutable_collections, who offers a compile-time secure immutable list called IList (as well as iSet for sets and iMap for maps).
We currently use IList for all of our apps because it's a simple replacement for List. In addition, it has few dependencies and supports a wide range of Dart versions, allowing us to also use it in our plugins without the risk of dependency conflicts.
Not only is IList less error-prone, it also offers cleaner syntax when used with other unchangeable states (like freezed classes) avoiding the need for defensive copying. Additionally, IList includes practical methods such as RemoveDuplicates and IntersectsWith. The only downside we found is that the syntax for creating a constant list (const ilistConst ([])) is slightly laborious.
OUR POINT OF VIEW
With very few drawbacks and a significant positive impact on quality and maintainability, we have integrated fast_immutable_collections to all of our Dart projects (both applications and plugins) and we strongly recommend that you do the same.
HISTORY 2022
It's a new blip this year.
In order to reduce the complexity of an SDK project, it is possible to split it into several independent packages. These packages are developed and versioned together, so we often speak of “mono-rest”. This architecture makes it possible to separate the development, testing and deployment of each brick into independent processes, but this modularity adds a major problem: how to manage interdependencies between packages, both in local development and once published?
In Flutter, and more generally in Dart, this problem is solved by Melos, which offers a command line tool for these “mono-rests.” It solves dependencies with local linking, allows the launch of commands on all packages (analyze, test, custom scripts...) and automatically proposes the versioning and publication of each package on pub.dev, which makes it a perfect tool to easily integrate with a CI/CD.
Last year, we adopted a reserved posture on Melos. Since then, the documentation has improved greatly, numerous bugs have been resolved, and version 3 has improved the syntax used for configuring Melos. For debugging, a logging system allows you to easily find out in which package your scripts encountered a problem.
Since Melos is developed by Invertase, a strong player in the Flutter and Dart open source ecosystem, we are confident that the library will be continuously improved and updated to meet the needs of the community. Invertase developers also use Melos to develop their own tools, like FlutterFire.
OUR POINT OF VIEW
The ease of development guaranteed by Melos and the absence of a mature alternative, decided us to adopt this library for our Dart and Flutter SDK projects. We recommend Melos to all package developers, especially those working on complex projects with multiple packages and development teams.
HISTORY 2022
Assess - See the Tech Radar 2022
When developing a Flutter project, whether it's an application or a package, we can quickly have to interface with native code. To enable developers to meet this need, Flutter offers the PlatformChannel system: communication channels between the platform's native code and Flutter's dart code.
This approach, which has been well documented by Flutter, has two problems:
To overcome these limitations, Flutter offers the package Pigeon. This code generation solution makes it possible to automatically create all the building blocks of a PlatformChannel, ensuring the typing of the data transferred. This approach therefore saves considerable time on generating the code, which is necessary for the operation of the PlatformChannel. At the same time, it guarantees the typing of the data exchanged on both sides, thus preventing numerous bugs.
There are some limitations such as the lack of support for event channels and the incompatibility with desktop platforms. Improvements are also possible on the somewhat cumbersome syntax for managing enums or supporting collections of non-zero objects.
These limitations are also present without the use of a pigeon and we are not seeing a more effective alternative to meet this need. In addition, some future developments, such as the use of Ffigen allowing native languages to be called directly from the dart code, promise to offer an even more efficient and complete solution.
OUR POINT OF VIEW
After the development of two packages and nearly a year and a half of use, we highly recommend Pigeon.
HISTORY 2022
Adopt - See the Tech Radar 2022
In Flutter, global state management is done using InheritedWidget and ChangeNotifier, but these APIs are very verbose and low level. Provider was created to simplify their use, and naturally, the Flutter community has largely adopted this solution. More recently, the creator of Provider introduced a new state management package: Riverpod.
Riverpod was created to address 3 Provider limitations:
In addition, Riverpod has many qualities such as its minimalistic syntax and its ability to be easily mockable and testable. Riverpod is constantly evolving with the support of new features such as asynchronous state support, integrated refactoring tools, and adapted lint rules.
Finally, the author of the package announced great developments for the future, in particular the persistence of the state and the support of the Static Meta Programming.
OUR POINT OF VIEW
After using it on several projects in production, Riverpod became the state management solution that we choose by default for our mobile application projects at BAM.
HISTORY 2022
Trial - See the Tech Radar 2022
The Dart language relies heavily on its excellent development experience (DevX), ensured by its familiar syntax and its powerful integrations with the most well known IDEs. like VSCode or Android Studio. With more than 220 lint rules created by the Dart and Flutter teams, the linter is one of the most remarkable features of these integrations. However, sometimes all of these rules are not enough to enforce specific team agreements.
In this situation, it is possible to use the custom_lint library.
Thanks to a relatively easy to use API, this library makes it possible to write custom rules for the Dart analyzer. These rules can then be added to the analyzer in the analysis_options.yaml file, in the same way as regular rules. Since each rule created with custom_lint is a Dart package, it is possible to publish them on pub.dev and thus share them with the entire community.
Created very recently, custom_lint cannot be adopted blindly. Several limitations remain annoying in integrating the library into certain projects. It is particularly difficult, if not impossible, for mono-rests that contain multiple Dart or Flutter packages that do not share the same dependencies.
Since the package is actively under development, its dependencies are very often updated and impose constraints that are sometimes difficult to follow, on projects with numerous dependencies that often have a longer update cycle. To take full advantage of custom_lint, it is necessary to have intermediate knowledge of the analyzer library. The latter is of a very low level and has a significant entry cost.
OUR POINT OF VIEW
After using it for several months on a Flutter application project, and on an SDK project, we think that custom_lint is a promising library, which should quickly become an essential reference in the community. Due to the lack of a reliable alternative and the limitations mentioned above, we decided to put custom_lint in “trial”. We intend to follow its evolution very closely in the coming months.
HISTORY 2022
It's a new blip this year.
The first navigation API in Flutter, called Navigator, is relatively simple. To navigate to a page, you need to push it onto the navigation stack. To go back, you need to remove it from the battery. Despite its simplicity and popularity, the Navigator has shortcomings and limitations:
With web support, the Flutter team introduced a new navigation API: Router. Unlike its predecessor, which works imperatively, the Router is a declarative API.
However, this API is of an extremely low standard. To use it, the use of a library is necessary.
Several options are available, such as RouteMaster and Beamer, labeled Flutter Favorite. On our projects, we use goRouter. Initially independent, GoRouter has since been maintained by the internal Flutter team. This library supports URL navigation, deeplinks without additional configuration and allows you to easily write navigation tests. There is even an extension, based on code generation, that allows you to type different routes.
Overall, our experience with GoRouter has been positive, although we are noticing issues when using embedded browsers.
OUR POINT OF VIEW
We recommend that you use GoRouter but with a certain degree of care while keeping an eye on the serious alternatives available, hence the positioning in “trial”.
HISTORY 2022
It's a new blip this year.
The ability to retrieve data exposed by a server, via an API, is a key feature for any mobile application.
In recent years, the GraphQL standard has gradually established itself as an alternative to REST, within the community. Today, applications developed in Flutter can also exchange their data with a GraphQL API, in particular with the package graphql_fluttEr (with 3.1k github stars to date).
The graphql_flutter package provides seamless integration with GraphQL, allowing developers to define queries and mutations using GraphQL strings. In addition, it offers the possibility of caching, polling/rebroadcasting, and supporting optimistic results, which are very useful features to improve the performance and user experience of the application.
The graphql_flutter package is also compatible with the package graphql_codegen, which allows the generation of code for the strong typing of manipulated objects and the support of fragments. This feature is particularly useful for large projects that require code that is robust as well as easily maintainable and reusable.
Although graphql_flutter encourages you to make api calls directly in your widgets or via Flutter hooks, it is quite possible to use it with other state management solutions without too much difficulty. However, using GraphQL client streams in these state management solutions can be complex.
OUR POINT OF VIEW
Our experience using GraphQL in Flutter has improved significantly since last year and we are now more confident using it on projects.
HISTORY 2022
Hold - See the Tech Radar 2022
Every client application interfaces with one or more servers, which make it possible to retrieve the data it displays, and to send it the information that the user enters. With Dart, defining API clients that enable these interactions can be tedious and require a large amount of boilerplate code. This is particularly the case for creating a JSON/DART (de) serialization of the exchanged data.
The solution OpenAPI Generator makes it possible to automatically generate the dart code responsible for interfacing, with an API that complies with OpenAPI 3 standard.
This involves generating an API contract.yaml file from the back code, in order to use it to generate the client. It is also possible to create this file manually if the API meets the standard.
This approach offers numerous benefits:
After having tested this approach on several production projects, we noticed a major obstacle to the implementation of this generation: some OpenAPI specifications are not natively supported by the Dart language, specifically objects that may be of several types (oneOf and allOf). A dart client generated via the OpenAPI generator will therefore not support these types. It is then necessary to define the API models with a library based on code generation such as Freezed.
OUR POINT OF VIEW
Even if we use an OpenAPI generator on each of our projects in production, this significant limitation, which we have encountered several times on our projects, pushes us to downgrade this solution to a “trial”.
HISTORY 2022
Adopt - See the Tech Radar 2022
Flutter uses Skia as a cross-platform rendering library for designing user interfaces. Skia is a powerful library, optimized for modern GPUs, providing Flutter with high performance on mobile platforms. It's also Chrome's graphics rendering engine.
By using shaders, programs that manipulate graphics and visual effects, Skia can produce sophisticated effects and smooth transitions within the user interface.
However, the shader compilation process, carried out at runtime, can cause jank problems (slowdowns and jerks) when running a Flutter application, especially on iOS.
To remedy these problems, a common solution is to pre-compile the shaders at build time. This approach is currently limited to iOS.
On Android, this method is not compatible with all smartphone models that use different graphics drivers. This pre-compilation step is time-consuming because it requires going through the entire application on a physical device to “save” the pre-compiled shaders, which makes it difficult to scale.
That's why Flutter introduced Impeller, a new graphics engine, designed to replace Skia and solve jank problems. Impeller is able to compile a smaller, simpler set of shaders. It can also rely on native graphics APIs like Vulcan on Android and Metal on iOS, to take advantage of the power of the phone's graphics processor.
Recently available on iOS, its use makes it possible to obtain impressive graphics performances. It has now been enabled by default on iOS apps since Flutter 3.10. On our mobile applications, we have observed a marked improvement in the fluidity of the animations. However, we noticed a few visual bugs, which were nevertheless quickly fixed by the Flutter team.
OUR POINT OF VIEW
Impeller clearly appears to be Flutter's future graphics engine. We recommend that you enable it on your projects, while remaining alert to the visual regressions that it may introduce.
HISTORY 2022
It's a new blip this year.
In mobile development, iteration cycles are much longer than in web development:
each new mobile application version must be validated by the stores (Apple and Google), thus extending the time between when an urgent patch is implemented and when it is available to users;
you have to wait for each user to decide to update the application to benefit from the patch;
In staging and QA environments, which can be deployed multiple times per day, mobile native build times and having to download new application versions add friction to deployment processes.
To address this problem and deploy minor changes without going through the store, applications developed in React Native have been benefiting from solutions such as: “over-the-air” update for several years now. CodePush, Expo Updates that allow you to send application updates developed in javascript, without going through the stores. This solution is highly appreciated by developers and users, but it is not available for applications developed in Flutter.
In early 2023, the creator of Flutter, Éric Seidel announced the creation of an open source project called Shorebird, which will allow updates to Flutter applications to be deployed without going through the stores. To develop this solution, he was quickly joined by Felix Angelov, whose name is familiar to Flutter developers since he is the author of the BLoC and Mason packages.
At the time of writing this blip, the Shorebird solution is still in the early stages of development and only works for Android. It is therefore too early to decide on its adoption.
OUR POINT OF VIEW
Our CodePush experience makes us want to benefit from a similar solution for our Flutter applications. In addition, the reputation of the two developers involved in the project reassures us about the quality of the solution that will be proposed.
HISTORY 2022
It's a new blip this year.
Retrouvez l'avis de nos experts sur les techniques, plateformes, outils, langages et frameworks associés aux principales technologies mobiles que nous utilisons au quotidien chez BAM : React Native, Flutter et Native.