Migrate from Getx to Riverpod/GoRouter/Others in Flutter
Background
Getx is a good Flutter framework to resolve the different kinds of pain points, It’s a beginner-friendly framework, so beginners are more like coding on “Getx” instead of “Flutter”.
3 years ago, I jumped into Flutter for a toy-project as a backend developer and was surprised by the Getx, so simple and easy to understand/use. I migrated that toy-project from the Flutter built-in state management/router to Getx.
Now, I’m creating a new toy-project and the initial version is almost done, still based on Getx.
Before moving it to Beta testing, I’m facing the challenges:
…
Riverpod is a new beginner-killer lib to manage the state and dependencies.
go_router, an official lib for router navigation.
As a beginner, I’ll use this article to note my migration progress, it may not cover all Getx use cases.
Also, feel free to point out my misunderstanding or mistakes, or if you have any new cases to enhance the list.
Below cases are based on this baseline implicitly:
Principle
- Change the code page by page and the app should be runnable, always.
- Not rewrite all of them or recreated a new one.
Case by case
Main entry
the ProviderScope
(from Riverpod for initialization) and GetMaterialApp
(from Getx for initialization) can work together.
so putting them together will make the app runnable during the migration.
Rx variables and Obx
Use NotifierProvider
to replace the Rx
variables,
use ref.watch
to replace the Obx
reactive widget.
Before:
After:
If use flutter_hooks
with riverpod, the code can be simplified more.
Option for multiple variables
Use a class to centralize the variables and logics.
It’s called State
in the provider and it should be immutable.
To build the immutable class, built_value is one of the options.
Service (long run instance)
Usually GetxService
is used to define a long run instance.
This instance may be shared by multiple pages, we can add a few lines to generate a long run riverpod provider.
This provider will be used in the migration for riverpod, and existing code base can still work with GetxService
.
Before:
After:
Class structure
Before:
the Getx controller can be referred as class attribute.
to avoid too many nested widgets in the build
method,
I normally use getter
to build small widgets and then combine them in the build
.
After:
1.Wrap within Consumer
In riverpod, the ref
(WidgetRef) is not available as class attribute, an alternative option is to wrap the widget within Consumer
.
However, if you have too many widgets, you have to wrap all of them.
2.Nested functions
Dart allows you to define functions inside other functions, the inner (nested) function is private to its outer function.
The nested function can access the variables at the outer function.
Wrapping Consumer
as getter
is used in abstract
class/widget and children class/widget can share the same widget.
For example, the passwordTextField
can be defined in an abstract
widget, the children widget Login
and Signup
widget can use it.
Nested functions can only be accessed by outer function, can’t be shared to children classes.
Async loading
Normally, I use Getx to read the data asynchronously with job_done flag. The flag is a Rx
variable, it can update the widget tree.
(this may not be the best practise, feel free to tell me the best way to use Getx for async reading)
In riverpod
, we can rely on AsyncValue
with watch
method to rewrite above case.
also can use switch
instead of when
on AsyncValue
, like this example from the official doc.
To be continued
Updates
Created on 2023-10-24
Updates on 2023-10-26: add multiple variables case
Updates on 2023-10-26: add getxservice case
Updates on 2023-12-03: add class structure
Updates on 2023-12-05: add async loading case