Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 10, 2022 06:26 am GMT

[2020] Explaining Flutter

This is my article originally posted on Medium in 2020.

Personally, I came to the Flutter game kind of late, earlier this year in January to be precise, and when I did, the community was already pretty saturated and filled with opinionated ways of writing Flutter applications. In a sense, this was a good thing, because I tend to jump on a wow, something new train quite a bit, and getting thrown into the well-established ecosystem was definitely exhilarating, but also somewhat intimidating at first.

Among many reasons, which I wont explore into details as wed sink into a rabbit hole, was the fact that GitHub is packed with sample applications, demos, and libraries. As per usual, the preponderance of those are written by developers who dont mind too much about code legibility, have little to no interest in making their code clean, and often times are incongruous. Who am I to blame here, right? Thats how its always been

We could go back and forth here on whether or not its the fault of the community failing to enforce best practices onto developers, or maybe its just the market not caring about it since well have to rewrite it anyway.

The Good, the Bad and the Ambiguous

When you begin looking at the Dart programming language, and thats the analogy Ive personally used many times, it gives me the feeling of JavaScript on steroids, attempting to bridge the gap between Java/C# developers, web developers, and Pythonistas.

It succeeds and fails in staggering proportions. While its oftentimes a graceful escape from rigid statically typed world, its also a curse slapping dynamic every time the returning type is obscure.
Not to mention the elephant in the room: access modifiers.

As somebody with strong knowledge of Java, it has always perplexed me just how can you talk about clean architecture in a language that fails to offer nearly all of the viable components required to write idiomatic object-oriented code. By default, everything is public, but prefix your member with an underscore and suddenly you have a private member.

Not to mention the absolute confusion with const and final keywords. Of course, you can read the documentation and realize that the former creates deeply transitively immutable objects. But for the preponderance of developers often do not care about these slight differences. In my opinion, they should.

A little bit of confusion also arises over Darts lack of true interfaces. Classes implicitly define an interface, therefore allowing you to implement any class. If you really want to protect yourself, simply write an abstract class so it cant be instantiated. But then, are we supposed to extend or implement these classes? Well, luckily extend still creates a more specific version of a class, while implements allows us to create our own implementation without inheriting code from said class. So cheers, at least that is familiar!

But here comes the fun part. +We dont have a recommended architecture._

Diversity of Architectures

Everyone from the Android world knows that MVVM is pushed forward by Google, and while you can play around with some MVP/MVC, there is a strong consensus that MVVM is the predominant pattern we stick to.

Personally, I began using BLoC, but it surprised me on so many levels as to how this pattern is both simplifying as well as complicating my development. The idea of having sinks and streams is pretty common in the reactive programming world, and while BLoC heavily relies on them, their implementation is beyond perplexing in the beginning.

The idea is to represent your events and states in the form of classes, each extending a base abstract class, or (usually in case of states) you can simply create enums if you do not require any special operations on data to be executed. This sounds relatively easy when explained this way, but it took me quite some time to realize the most optimal approach simply by relying on documentation. However, once familiar with them, you realize the raw power of combining excellent Dart Streams with rxdart and flutter_bloc package. Suddenly:

  • You have full control over how the state is passed down the widget tree.
  • You can implement additional logic in classes representing events (or states) that perform operations on data when needed.
  • It allows for the reusability and greater separation of concern, by separating business logic aspect of the application from the UI, while also additionally fragmenting access to data into providers and repositories. Former allows for raw access to data (either in a database or via calls to an API) while the latter is just a wrapper around them.
  • Having multiple BLoCs would be an equivalent of having multiple ViewModels in the Android world, each tasked with feeding dedicated type of data to the UI component.

I never managed to find this solution mentioned anywhere, but for some reason, people forget that repositories should be used as singletons, simply because during its initialization, we create connections with the database, or read configuration files, etc. Darts somewhat amusing factory constructors or Java-like static instance/private constructor combinations are both valid options. Personally, I prefer to stick with factory constructors for my singletons.

But BLoC does not solve all answers by itself, one of the things I was missing in the beginning, was a solid dependency injection framework. Today, we do have inject.dart, but if you check out the official repository, as we speak, it looks poorly maintained. So, back to square one!

In the Android world, you can play around with the mammoth of a dependency injection libraries (yes, Dagger), or opt for something a bit more lightweight, like Koin. Googles team is bringing in Hilt as well, so its definitely an interesting time to be an Android developer! But Flutter is not there yet, as the entire community seems to be saturated around people writing Medium articles on basic concepts that everyone can learn about reading fantastic official documentation, while on the other end are developers attempting day in and day out to create maintainable code despite the lack of opinionated architecture guidelines.

And yet, Flutter flourishes, despite no dependency injection framework or solution. Edit: we now have solutions such as injectable, get_it and kiwi, which were either not available when the article was first written or were not mature enough to be compared to established solutions from the native Android world.

Benefits Far Outweigh Drawbacks

When it comes to the development of applications that do not need to rely on native features, such as reading sensor data, and their only concern is to be a good looking fashion model, that has to perform some networking to contact an API and store some data locally in form of an SQLite database, Flutter is a perfect fit. The platform is specifically oriented towards delivering applications with a native touch to Android, iOS, web, and in recent efforts, even desktop.

Generally speaking, Flutter SDK has these three components:

  • At the platform level, Flutter provides a shell hosting a Dart VM. This shell is platform-specific, giving access to native APIs.
  • The engine implements Flutters core libraries, Dart runtime, and the compile toolchain.
  • The framework is ultimately what developers interact with.

Instead of finding native equivalents to Widgets (top-level building blocks), Flutter bypasses this step by relying on the Skia 2D rendering library. There is no use of WebViews or OEM widgets, instead, as mentioned above, it relies on the high-performant rendering engine to draw widgets. Most of the system is implemented in Dart.

Technical Overview

Developers are implicitly manipulating the render tree (a data structure that stores the geometry of the user interface) using widgets, instead of authoring render objects directly. This leads to the concept of aggressive composability, idea that widgets are built by composing other widgets, themselves built out of progressively more basic widgets. When it comes to rendering these components, Flutter relies on a series of algorithms and optimizations:

  • Sublinear layout: Flutter aims for linear performance for initial layout rendering while having a sublinear performance in the common case of subsequently updating an existing layout.
  • Sublinear widget building: after widgets are built, they are held by the element tree which retains the logical structure of the user interface, while also preserving the state associated with stateful widgets. A framework keeps a list of dirty elements (those whose state we modify) while skipping over clean elements.
  • Linear reconciliation: the decision whether or not to reuse elements, is done by examining the child list for each element independently using an algorithm with linear complexity. Side-note: this algorithm is not the same diffing algorithm that React uses, even though the complexity is supposedly the same.
  • Tree surgery: reuse of elements, which is a crucial step in Flutter development, allowing the use of non-local tree mutations associated with a GlobalKey, allowing developers to move a widget with such global key to an arbitrary location in the element tree, where the framework checks the hash table and reparents the existing element to its previous location, preserving the entire subtree

Back to the Bigger Picture

On Android, the engines C/C++ code is compiled with Android NDK, and with LLVM on iOS. In both cases, we are talking about an ahead-of-time compilation into native (ARM or x86) libraries. Those libraries are included in a runner project, built into APK/app bundle or .ipa. When application is launched, it delegates any rendering/input/event handling to the compiled native code, acting more as a game engine than a typical framework.

The Flutter team has been vehemently staying up-to-date whenever a new version of Android or iOS is released. The interop and plugin system allows developers to access these new features and capabilities immediately, in a worst-case scenario, by exposing them as platform channels. Platform channel is an asynchronous message passing system for custom integration of platform and third-party APIs. Out-of-the-box access to some platform-specific services and APIs works better than on competitor frameworks (such as React Native or Xamarin).

IDE Support

As an Android developer, I have always been somewhat reluctant to move away from Android Studio, or any IDE by JetBrains to open-source text editors like VSCode, simply due to the lack of functions and overall friction during the migration process. The issue with VSCode, at least in my opinion, is the extension hell: you can install a myriad of those, which is, personally, something Im not really fond of.

However, with time I found out that using VSCode exclusively for Flutter development has increased my productivity. One of the reasons is keeping my coding environment clean.

I hope you liked this article. I will be transitioning one more old article from Medium here to DEV. From now on, I'll be publishing articles on both platforms as an effort to diversify writing portfolio and try out new blogging platforms.


Original Link: https://dev.to/sunderee/2020-explaining-flutter-5cc7

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To