It's been almost a year of my professional Flutter career and I have experienced many packages and utilities. This post is about the ones I use frequently and the reasons why I chose them.
State management
Good topic to bring at family gatherings. Well, apart from politics and religious beliefs. Each one has his own opinion—this one is mine and you should take it with a grain of salt.
flutter_bloc
BLoC is the only state management solution I tried that scaled well for larger apps.
BLoC is defined with states and events. You are forced to precisely define all state possibilities before anything else. I prefer it this way because when you get to writing logic which handles these states it is fairly straightforward. Also, it is hard to get some weird undefined state (like when the user logs out and you still display his username).
provider
I started with Provider
which is recommended as the "state management"1
solution on official Flutter docs. It does one thing well and fast: passing a
widget to its descendants. I would use it in simple projects but in my
experience it does not scale well.
Once you have multiple providers and want them to communicate (parent provider notifies child provider to change state) things get less optimal. You can use proxy providers but it brings a lot of unnecessary boilerplate.
Immutability
Immutable classes/states works nicely with BLoC. Rather than changing (mutating) old state you create new one.
freezed
Freezed generates immutable classes for you. You define an abstract class with factory constructors. Freezed then uses code generation to create respective implementations. Following is a snippet from its docs.
@freezed
abstract class Union with _$Union {
const factory Union.loading() = Loading;
const factory Union.error(String message) = ErrorDetails;
}
Together with instantiable classes, you get various methods which make life
easier, like when
or map
.
Union state = Union.loading();
state.when(
loading: () => CircularProgressIndicator(),
error: (message) => Text(message),
);
Dependency injection
flutter_bloc
For simple scoped dependency injections I use RepositoryProvider
from
BLoC.2 The only downside to this is missing extension methods auto-complete
support in Android Studio. You need to manually import BLoC to see the
repository
method on context
.
var repository = context.repository<UserRepository>();
print(repository.name);
I personally don't like that it's called repository provider. But it is just a nitpick.
injectable
In some more complicated cases I prefer to use injectable
which is a code
generator for get_it
. You add annotations like @singleton
to your classes
and injectable
automatically registers them.
What I find useful is the support for registering third party modules (e.g.
shared_preferences
instance) and async registration.
Routing
auto_route
Automatically generates named routes for you. I like that you define all routes at a single place (you can have multiple routers). Also, you can get navigators by their name which comes handy with features like the bottom navigation bar.
Persistence
floor
My personal favorite. Well, I would like it to be my favorite. It tries to work
exactly like the Room
library on Android. You define entities (classes) and
annotate them. floor
will then generate the required SQL code for you.
Streamed queries are a joy to use and together with StreamBuilder
makes your
life better.
However, floor
misses few advanced features like type adapters (WIP
apparently), database encryption or SQLite extensions. You might manually setup
the latter 2 things but it is not as easier as in moor
.
I haven't tried moor
yet, because I never liked the API. However, it seems to
be have more features and development (due to larger community).
hive
First persistence library I used. Its simplicity is a superb feature but lacks development at this very moment. Still useful for quick NoSQL persistence (offline cache of JSON objects or similar).
Other
rxdart
Once you go the Stream way you never go back. The rxdart
comes handy when you
already use streamed data (queries in floor
or bloc
). You will not need it
most of the time because Dart's standard Stream library has already enough
features. But once you need to add more complex things like debouncing, it can
save you a lot of time.