With previous experience working with React, moving to Flutter, hooks are things that I feel missing the most. Hooks help a lot in dealing with states and the life cycles of a component. Luckily, there is a package that brings hooks to Flutter: flutter_hooks. Flutter now feels so familiar.
The code written in this post is at my GitHub repo
Create a Stateful Widget
Well. we’re all (I mean those who write Flutter) know what it does and how to create it. The code snippet below shows a simple screen using the stateful widget. We have an int state _number that is initialized as 0;
class HookDemo extends StatefulWidget {
@override
_HookDemoState createState() => _HookDemoState();
}
class _HookDemoState extends State<HookDemo> {
int _number = 0;
@override
void initState() {
super.initState();
_number = 1;
print('hello');
}
@override
void dispose() {
super.dispose();
print('good bye');
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_number++;
});
},
child: Icon(Icons.add),
),
body: Center(
child: Text(
_number.toString(),
style: TextStyle(fontSize: 30),
),
),
);
}
}
We have initState which will be called at the very beginning this widget mounts, and for some silly reason, we set the _number to be 1 as well as printing “hello”. So that as soon as the screen is loaded, the Text widget in the body shows 1, and the console prints “hello”.
In addition, we have a dispose method which will be called when this widget is unmounted to release the resource or in this case, saying “good bye”.
We also have a FloatingActionButton which will increase the _number by one every time we tap it. Thanks to the setState method, it triggers the build method so that the widget is rebuilt to reflect the change of _number (the state).
At this point, you feel that you’re remembering how easy hooks are in React.js. And would using hooks here, in Flutter, can be as easy and less verbose?
Creating a hook widget
Let start by converting the previous example to a hook widget. It can be done as easily as extends the widget from HookWidget. Since a HookWidget doesn’t have initState or dispose, we can eradicate those 2 methods. Of course, we can’t use setState anymore, it’s now an orphan.
Don’t forget to import flutter_hooks import ‘package:flutter_hooks/flutter_hooks.dart’;
useState and useEffect
Now let’s revive the long-lost friend from React, useState. The usage is just as similar. In this case, we create a number state with the type of <int> and init value of <0>.
final number = useState<int>(0);
The other friend is useEffect, again, similar syntax. useEffect has 2 parameters, first one is the effect function that will be called the first time this widget is built and every time the dependency changes. The second parameter is an array of dependencies, which if any of the dependencies we put here changes, the effect function will be called. However, If we leave the array empty, the effect function is trigger only once when the widget mounts, kinda same effect as initState.
effect function returns another function, this is the dispose function which will be called at the time the widget unmounts.
useEffect(
() {
print('hello');
return () {
print('good bye');
};
},[]
);
Never, without a purpose, leave out the second parameter unless the effect function will be triggered every time any state changes.
Remember, all hooks must be declared inside the build method.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class HookDemo extends HookWidget {
@override
Widget build(BuildContext context) {
final number = useState<int>(0);
useEffect(
() {
print('hello');
return () {
print('good bye');
};
},
);
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => number.value++,
child: Icon(Icons.add),
),
body: Center(
child: Text(
number.value.toString(),
style: TextStyle(fontSize: 30),
),
),
);
}
}
Available hooks
If you think despite that similar to React hooks, it’s not enough to convince you to use Flutter hooks, there are more than just useState and useEffect. If you had a hard time building a widget with multiple controllers (AnimatationContrller, ScrollController,.. etc) with declaring and disposing, Flutter Hooks would be a no-brainer choice.
For example, create 3 AnimationController using hooks is as simple as this:
final controller = useAnimationController();
final controller2 = useAnimationController();
final controller3 = useAnimationController();
useAnimationController will automatically create and dispose the controller for you. Stress free!
Below are some of the available hooks. Moreover, you can create your custom hooks!!!
useEffect | Useful for side-effects and optionally canceling them. |
useState | Create variable and subscribes to it. |
useMemoized | Cache the instance of a complex object. |
useContext | Obtain the BuildContext of the building HookWidget . |
useValueChanged | Watches a value and calls a callback whenever the value changed. |
More can be found at flutter_hook
Conclusion
Flutter hooks in some way, make our life as flutter developers easier when we can get rid of verbose syntaxes and boilerplates. Or if you are like me, a former React developer, it brings you ease and joy and memories,…