Updated 28 April 2023
In this article, we are going to learn about how we can shake effect in flutter.
Below is the demo of how our widget will look.
Check out more about our Flutter app development.
1. We are first going to create an animation controller class.
We are going to need an AnimationContoller
to achieve the effect that we want, so we will create a separate abstract state class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
abstract class AnimationControllerState<T extends StatefulWidget> extends State<T> with SingleTickerProviderStateMixin { AnimationControllerState(this.animationDuration); final Duration animationDuration; late final animationController = AnimationController(vsync: this, duration: animationDuration); @override void dispose() { animationController.dispose(); super.dispose(); } } |
2. Create a Custom Shake Widget
This widget will be responsible to shake our other widgets.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class ShakeError extends StatefulWidget { const ShakeError({ required this.child, required this.duration, required this.shakeCount, required this.shakeOffset, Key? key, }) : super(key: key); final Widget child; final double shakeOffset; final int shakeCount; final Duration duration; @override // ignore: no_logic_in_create_state State<ShakeError> createState() => ShakeErrorState(duration); } |
This requires multiple params:
child
: This is the widget that we want to shake
duration
: Shake duration.
shakeCount
: How many times our widget should shake.
shakeOffset
: Shake distance of the widget.
3. Use the animation with AnimatedBuilder
and Transform.translate
(to translate our widget back and forth)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@override Widget build(BuildContext context) { return AnimatedBuilder( animation: animationController, child: widget.child, builder: (context, child) { final sineValue = sin(widget.shakeCount * 2 * pi * animationController.value); return Transform.translate( offset: Offset(sineValue * widget.shakeOffset, 0), child: child, ); }, ); } |
4. Add a status listener to reset our AnimationController
.
This is important as we want to show this error multiple times and if we do not reset our controller then we will not be able to see this effect again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@override void initState() { animationController.addStatusListener(_updateStatus); super.initState(); } @override void dispose() { animationController.removeStatusListener(_updateStatus); super.dispose(); } void _updateStatus(AnimationStatus status) { if (status == AnimationStatus.completed) { animationController.reset(); } } |
5. Add a shake()
method.
This method will be responsible for the shake effect. This method should be public.
1 2 3 |
void shake() { animationController.forward(); } |
6. We will use GlobalKey
to control the shake effect (or to shake our widgets).
Flutter provides GlobalKey
that we can use according to our needs with or without the state.
In this case, we will be needing the state so that we can initiate the shake during any taps.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
class _HomeScreenState extends State<HomeScreen> { late GlobalKey<FormState> _formKey; late GlobalKey<ShakeErrorState> _shakeState; late TextEditingController _textEditingController; @override void initState() { _formKey = GlobalKey(); _shakeState = GlobalKey(); _textEditingController = TextEditingController(text: ""); super.initState(); } @override void dispose() { _formKey.currentState?.dispose(); _shakeState.currentState?.dispose(); _textEditingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( appBar: AppBar( title: const Text("Shake Error"), ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), child: Form( key: _formKey, child: Column( children: <Widget>[ ShakeError( key: _shakeState, child: TextFormField( controller: _textEditingController, decoration: const InputDecoration( border: OutlineInputBorder(), label: Text("Text..."), ), validator: (value) { if (value?.isEmpty == true) { return "Value can't be empty"; } return null; }, ), duration: const Duration(milliseconds: 500), shakeCount: 3, shakeOffset: 10, ), ElevatedButton( onPressed: () { _formKey.currentState?.validate(); if (_textEditingController.text.isEmpty) { _shakeState.currentState?.shake(); } }, child: const Text("Submit"), ), ], ), ), ), ), ), ); } } |
That’s it, now shake any widget that you want, just properly manage keys, and also don’t forget to dispose of them once you are done.
In this article, we have learned how we can achieve shake effect in flutter.
Hope you have liked this. Head over here to learn more about flutter.
Reference: https://codewithandrea.com/articles/shake-text-effect-flutter/
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
4 comments
I’m lost on the third step, where i have to put build, ShakeError or ShakeErrorState?
Why is ShakeErrorState empty?, as far as I know, it is not possible to leave a State class empty.
Hi there!
Thanks for your comment! I totally get how the third step can be confusing. You’ll want to use
ShakeErrorState
in your widget to manage its state. It can start off empty if you don’t need any additional functionality right away—it’s just a placeholder for now.I hope this helps.