DEV Community

Fernando Martín Ortiz
Fernando Martín Ortiz

Posted on

Flutter: Initial impressions

Flutter has been with us for a couple of years so far. Version 2.0 has been released recently, and I thought this is a good time to jump on and start doing something to learn it.

Today, I'd like to share some links and first impressions on the Flutter ecosystem. This is going to be a personal opinion, so I'm totally open to discuss and admit my errors if you think anything of this isn't true.

Dart

Let's start with some words about the language. I honestly didn't like Dart at the beginning, when I first read about it some years ago.
Dart started as a language that compiled to Javascript, and was advertised as a nice integration to the web ecosystem, especially to Angular, which is still there but not being actively recommended by the AngularDart team.

Dart is easy to learn. To me, it has been much easier and simpler to learn than Kotlin, for instance. It has the dynamic features any language needs, in order to inter operate with Javascript, but at the same time, recent additions like null safety seems to have improved the developer experience.

Async Dart

Dart has async/await support using Future<T> type values, that are like Javascript Promise, or Apple Combine's Future. A Future is a value holder that will eventually fulfilled with an actual value, and we can prepare some action for when it happens. A simple example of a Dart's Future is as follows:

Future<String> echo(String message, { required int afterSeconds }) async {
  await Future.delayed(Duration(seconds: afterSeconds));
  return message;
}

void main() async {
  print("Will start");
  final message = await echo("Hello", afterSeconds: 1);
  print(message);
}
Enter fullscreen mode Exit fullscreen mode

In that example, the message "Will start" will appear immediately, and the "Hello" message will appear a second after that.

Something that surprised me a lot, and that I'd love to have in any other language I have to work with (Swift, I'm looking at you) is async* streams generators. AFAIK, Dart streams are like ReactiveX Observable streams, but you can generate those streams using functions marked as async*. So for instance, this:

Stream<int> generateNumbers({required int upperBound}) async* {
  for (int i = 1 ; i <= upperBound ; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

void main() async {
  await for (var number in generateNumbers(upperBound: 10)) {
    print("Number: $number");
  }
}
Enter fullscreen mode Exit fullscreen mode

In an async* function, we can await for Future values and Stream values, and also yield values. The values we yield in an async* function will be added to the generated Stream.

After that, we can await for those values to be obtained, using a await for cycle.

There are many other useful functions in the Stream class, but having Stream embedded in the language, not depending on an external third-party library is sweet.

Inheritance

Dart doesn't have interfaces. Everything is a class, and you can implement or extend them, but there is no protocol or interface or something similar.

Mixins are a nice addition, I guess. I haven't played much with them, but seems like protocol-oriented programming in Swift.

So far, I prefer Swift or Kotlin more classical inheritance models. However, I'll need to experiment more on these techniques, before saying much more about it.

Access modifiers

There is no private keyword. What? Dart doesn't have private methods or attributes. If you want something to be private to the file where you are, you just start its identifier with an underscore. So _name is private, and name is public. Something similar to how Ruby operates, maybe? I'm just not used to it, although being honest, I haven't needed much more so far.

Dartpad

The Dartpad is a simple Playground-like web page that you can use to test the Dart syntax and quickly test some ideas and Dart libraries.

You can test the previous Future/Stream examples by copy/paste them on a Dartpad here: https://dartpad.dev/?null_safety=true

A declarative UI

Nothing new here. Declarative UIs are here to stay, and that's great news. Thinking more on the description of the elements and their relationships is much better (and less error prone in general), in my opinion, than "micromanage" the views, explaining them step-by-step how to do whatever I need them to do.

Flutter follows the same approach that React popularized in the past, and that SwiftUI, Jetpack Compose and many others are doing in the present.

For those who aren't totally sure what a declarative UI is, the Flutter docs do a short by well written introduction to it. I recommend you to read the entire State management section, as it introduces some nice concepts like ephemeral vs app state that I enjoyed.

Everything is a Widget

Really. Everything is a Widget.

I found it somewhat strange at the beginning. You don't have styles as in React or view modifiers as in SwiftUI. Everything is a widget. If you'd like to modify something or position a Widget in a specific way, you need to embed it into another Widget and use its container properties to modify it. So you have Widgets for Rows, Columns, to Center it, to Expand it, etc.

This could be hard to maintain, but Flutter IDEs does some great work in formatting the code.

Something that is great with these Widgets is that the Layout is implemented using Flexbox, which is a widely used layout spec. If you used React Native in the past, it's not that hard to start developing UIs in Flutter.

Somewhat limited state management

The state management in Flutter is implemented in a similar way to how React does it, but it's curious that you have to create a State<Widget> and associate it to a StatefulWidget instead of just having a single class.

Something I didn't particularly like about Flutter state management implementation is that providing values to an entire subtree, as you'd do using Context in React, or EnvironmentObject in SwiftUI, is not as easy in Flutter. The generally accepted way to do so is by using a third party library like Provider or BLoC.

BLoC

The Pattern

BLoC is an architectural pattern as well as a library. As an architectural pattern, BLoC is very similar to most modern frontend architectures that aren't Redux oriented.

bloc_architecture_01

Very similar to what the Android architecture guide suggests, the BLoC architecture is based on the same principles.

The BLoC architecture is composed on the following components:

  • View: The Flutter widgets tree.
  • BLoC: A "Business Logic Component". Maybe there are more specific and fine-grained explanations to what a BLoC actually is. To me, is a fancy name for a view model, with the difference that a BLoC only exposes a single stream of state.
  • Repository: An also well known pattern, a Repository in the BLoC architecture is an object that knows where to go to get data, and how to merge different data streams into a single stream of valuable information.
  • Data Provider: An object that is the nearest to the actual data sources, like a REST service, or a database.

A view can use several BLoCs, a BLoC can use several repositories, and a Repository can query several data providers.

Pretty standard.

The library

Everybody stand up. Here comes the greatest state management library I've seen in a while.

As far as I've read about it, and experimented on it, the Bloc library is just fantastic. It's fantastic.

You get two main components: Bloc and Cubit.

A Cubit is the simplest choice, that are state holders, which exposes their read-only state, and functions you can use to modify their values. And not only that. Both Cubit and Bloc exposes Stream values you can listen to, combine them together, and use them to feed other components.

A BLoC is similar to a Cubit, but the main difference is that its state isn't modified in public functions. Instead, a BLoC is fed with streams of events, which it transforms in an output streams. This seems to be useful to combine other Cubit and BLoC objects.

What I find useful about this library, is that when you inject a BLoC or Cubit using a BlocProvider, the BLoC or Cubit is available in the entire subtree.

Also, the BLoC library comes with good test tooling, to test using the Arrange-Act-Assert Unit testing style.

The community is great and there are very useful resources on the internet. I'd like to recommend the Flutterly series on Bloc, which I found very nice.

To sum up

To be honest, I haven't done muuuch yet, just a simple TMDB client during the last couple of days, a decent amount of documentation reading, and watched a couple of nice videos about it.

So far, I'm in the top of the hype curve, so these words could bit me in the future, but for now, I can say Flutter is a promising technology and I'd like to learn much more about it!

Expect more posts in the near future.

Top comments (0)