Flutter Hooks Tutorial (Part 1): Flutter Animation using Hooks (useEffect and useAnimation Controller) www.bacancytechnology.com
Do you remember how we used to do programming before Flutter hooks weren’t part of Flutter? It was a bit difficult and overwhelming, right? Here’s a Flutter tutorial where we will explore pre-hooks and post-hooks Flutter life. We will learn how to use useEffect and useAnimationController in our demo app. Let’s get ready and build an application, then!
Basics of Flutter Animation
In this Flutter Hooks tutorial, we will learn how to install and build an animation demo using hooks in Flutter, and also, we will look at how we used to code before hooks weren’t introduced. Before moving on to the Flutter hooks tutorial, let’s discuss the basics of animation.
We need to do the below things for animation,
Animation controller Manage animation Listen for updates Manipulate animation
The default part is the “Animation,” There are two types of animation that Flutter supports.
Tween Animation– It is the short form of in-betweening. The animation must define the start and endpoint. The animation will have a start value and then reaches the end value through various intermediate values. Physics-based Animation– It allows you to interact with an app more realistically and interactively.
In simple words, an Animation controller is called whenever its value changes,
Ticker calls tick function every frame. After each tick, it provides a callback function after it is started. In our case, the ticker listener is the controller.
Flutter hooks have not received much visibility and acknowledgment, but that won’t make them less awesome. With the help of the flutter_hooks library, we can provide a robust way for managing the lifecycle of widgets by reducing code duplication and increasing code-sharing.
Flutter provides in-built hooks as shown below:
useEffect hook fetches data from a server and sets the fetch to the local state useState hook manages local states in apps useMemoized hook memoizes complicated functions to achieve optimal app performance useRef hook creates an object that has one mutable property.
useCallback hook cache a function instance. useContext hook obtain the BuildContext of the building HookWidget useValueChanged Hook watches a value and calls a callback whenever the value changes.
In this guide, we’ll use the useEffect hook for redirection after some duration, and useAnimationController hook to create an instance of AnimationController. Flutter, just like ReactJS, also gives you the opportunity of creating your custom hooks. We will discuss that later in the tutorial. Without further ado, let’s get started with the Flutter hooks tutorial.
Pre-Hooks Life: Animation without Flutter Hooks
First of all, let’s see the splash screen animation demo without implementing flutter hooks and using the stateful widget; it needs to be mixed in with a TickerProviderStateMixin.
And don’t forget to dispose of the animation controller.
class SplashScreen extends StatefulWidget { const SplashScreen(); @override _SplashScreenState createState() => _SplashScreenState(); } class _SplashScreenState extends State with TickerProviderStateMixin { late AnimationController _animationController; late AnimationController controller; late Animation< Offset > offset
@override void initState() { super.initState(); startTime(); _animationController = new AnimationController(vsync: this, duration: Duration(seconds: 3)); _animationController.repeat(reverse: false); controller = AnimationController(vsync: this, duration: Duration(seconds: 2)); offset = Tween< Offset >(begin: Offset(0.0, 1.0), end: Offset.zero).animate(controller); controller.forward(); }
startTime() async { var duration = Duration(seconds: 3); return Timer(duration, navigatePage); } Future navigatePage() async { Route route = MaterialPageRoute(builder: (context) => HomePage()); Navigator.of(context).pushReplacement(ro ute); } @override void dispose() { _animationController.dispose(); controller.dispose(); super.dispose(); } ... }
All of the above code is set up for animation. And then we will just apply these instances to our widgets/design. That is just easy to understand.
Post-Hooks Life: Installation and Flutter Hooks Example
How to Install Flutter hooks? Run the following command for installing flutter hooks flutter pub add flutter_hooks
The command will update pubspec.yaml file of the dependencies section with flutter_hooks: VERSION_NUMER_HERE.
OR you can directly update pubspec.yaml with flutter_hooks as shown below
dependencies: flutter: sdk: flutter flutter_hooks:
Save the file, and Flutter will install the dependency. Now, import the flutter_hooks library.
import 'package:flutter_hooks/flutter_hooks.dart';
How to Implement Animation using Flutter Hooks? The first change to make is to replace the Stateful Widget with Hook Widget. And then, to initialize the animation controllers, we will use the useAnimationController() method, a default method provided by the flutter_hook library. And we can remove all boilerplate code from initState and dispose method.
class SplashScreen extends HookWidget { @override Widget build(BuildContext context) { AnimationController _animationController = useAnimationController(duration: Duration(seconds: 4), initialValue: 1); _animationController.repeat(reverse: false); AnimationController controller = useAnimationController(duration: Duration(seconds: 3), initialValue: 1); controller.repeat(reverse: false, period: Duration(seconds: 4)); controller = useAnimationController(duration: Duration(seconds: 3), initialValue: 1); controller.repeat(reverse: false, period: Duration(seconds: 4));
Animation< Offset > offset = Tween< Offset >(begin: Offset(0.0, 1.0), end: Offset.zero).animate(controller); controller.forward(); useEffect(() { Timer.periodic(Duration(seconds: 4), (time) { Route route = MaterialPageRoute(builder: (context) => HomePage()); Navigator.of(context).pushReplacement(ro ute); }); }); return SafeArea( child: Scaffold( body: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0xFFFFFFFF),
Color(0xffE3F3FF)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: Stack( children: [ FadeTransition( opacity: _animationController, child: Center( child: FlutterLogo( size: 50, )), ), Align( alignment: Alignment.bottomCenter, child: SlideTransition( position: offset, child: Image.asset( 'assets/images/splash_bg.png', width: MediaQuery.of(context).size.width, fit: BoxFit.fill,
), )) ], ), ), )); } }
But the problem with the above code is there is no way to dispose the animation controller instances; we have a solution for this, called custom hooks.
Flutter Hooks Tutorial to Create Custom Hooks
The custom AnimationController hooks in Flutter will instantiate an AnimationController and return the ScrollController to use it from the UI. It will help in holding all the code, practically, which was previously stored in the State object of a StatefulWidget.
The most basic hook can be just a simple function. Let’s create one under hooks/controller_for_animation.dart. And also one for Animation< Offset >.
// hooks/controller_for_animation.dart. AnimationController useControllerForAnimation() { return use(_AnimControllerForAnimationHook()); } class _AnimControllerForAnimationHook extends Hook {
@override _AnimControllerForAnimationHookState createState() => _AnimControllerForAnimationHookState(); } class _AnimControllerForAnimationHookState extends HookState { late AnimationController _animController; @override void initHook() { _animController = useAnimationController(duration: Duration(seconds: 4), initialValue: 1); _animController.repeat(reverse: false); } @override AnimationController build(BuildContext context) => _animController;
@override void dispose() => _animController.dispose(); } Animation< Offset > useOffsetHook() { return use(_AnimOffsetForAnimationHook()); } class _AnimOffsetForAnimationHook extends Hook> { @override _AnimOffsetForAnimationHookState createState() => _AnimOffsetForAnimationHookState(); } class _AnimOffsetForAnimationHookState extends HookState, _AnimOffsetForAnimationHook> { late AnimationController _controller; late Animation< Offset > offset;
@override void initHook() { _controller = useAnimationController(duration: Duration(seconds: 3), initialValue: 1); _controller.repeat(reverse: false, period: Duration(seconds: 4)); offset = Tween(begin: Offset(0.0, 1.0), end: Offset.zero).animate(_controller); _controller.forward(); } @override Animation< Offset > build(BuildContext context) => offset; @override void dispose() => _controller.dispose(); }
All we did was move logic to this new class; also, there is one dispose method from which we can also dispose that controller, and now simply use that useControllerForAnimation. The method inside build method of the splash screen.
class SplashScreen extends HookWidget { @override Widget build(BuildContext context) { AnimationController _animationController = useControllerForAnimation(); Animation< Offset > offset = useOffsetHook(); ……. }
Also, create one hook widget inside hook/timer_widget.dart to redirect to a home screen after some duration. // hook/timer_widget.dart import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks_demo/home_page. dart'; bool useTimerToNavigate() { return use(const _NavigateTimer()); } class _NavigateTimer extends Hook { const _NavigateTimer();
@override ___NavigateTimerState createState() => ___NavigateTimerState(); } class ___NavigateTimerState extends HookState { late Timer _timer; bool isRedirect = false; @override void initHook() { super.initHook(); _timer = Timer(Duration(seconds: 4), () { Route route = MaterialPageRoute(builder: (context) => HomePage()); Navigator.of(context).pushReplacement( route); }); }
@override bool build(BuildContext context) { return isRedirect; } @override void dispose() { _timer.cancel(); super.dispose(); } }
And here is the final Splash Screen code! // splash_screen.dart class SplashScreen extends HookWidget { @override Widget build(BuildContext context) { AnimationController _animationController = useControllerForAnimation();
Animation< Offset > offset = useOffsetHook(); useTimerToNavigate(); return SafeArea( child: Scaffold( body: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0xFFFFFFFF), Color(0xffE3F3FF)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: Stack( children: [ FadeTransition( opacity: _animationController, child: Center( child: FlutterLogo( size: 50, ),
// child: Image.asset('assets/images/splashlogo.png'), )), Align( alignment: Alignment.bottomCenter, child: SlideTransition( position: offset, child: Image.asset( 'assets/images/splash_bg.png', width: MediaQuery.of(context).size.width, fit: BoxFit.fill, ), )) ], ), ), )); } } The entire source is available on the github: flutter-hooks-demo.
Conclusion
We can conclude that with the help of flutter hooks we can reuse that AnimationController and Animation< Offset > just by adding one line!
AnimationController _animationController = useControllerForAnimation();
So, this is how we can reduce the boilerplate code and make a smaller size code for the widget. And also make clean, easy and maintainable code. I hope the Flutter hooks tutorial was helpful to you. For more such Flutter Tutorials, feel free to visit the Flutter tutorials page and clone the github repository to play around with the code.
If you are looking for Flutter experts who can help you with your projects, contact us to hire Flutter developer. Since our developers have great problem-solving skills, we assure you of the absolute code quality and optimum application performance.
Thank You
www.bacancytechnology.com