Drawer Animation in Flutter
I recently watched Marcin Szałeks video about complex UI where he creates some awesome animations using flutter. In this post we are going to create one of those beautiful animations.
First lets take a look at the project structure before adding animations:
home_form.dart
@override
Widget build(BuildContext context) {
return Stack(
children: [
// Works as Drawer
Scaffold(
backgroundColor: const Color(0xff22a6b3),
body:const DrawerData(),
),
// Works as Main Screen
Scaffold(
backgroundColor:Colors.white,
appBar: PreferredSize(
preferredSize: Size.fromHeight(200),
child: Container(
color: const Color(0xffee5253),
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 24, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
icon: Icon(Icons.menu),
),
Padding(
padding: const EdgeInsets.only(top: 24,bottom: 12),
child: Text('Welcome',style: Theme.of(context).textTheme.headline4,),
),
const Text('Improve your language skill\nby selecting one of the following languages',
style: TextStyle(
fontSize: 17,
fontStyle: FontStyle.italic
),)
],
),
),
),
),
body: const DummyData(),
) ],
);
}
We have a Stack
with two child, the first one works as the drawer and the second one works as the main page. Without the animation and movement we can only see the second child on screen:
And the result we want is something like this:
In order to achieve this goal we should:
- Make main screen smaller.
- Move the main screen to the right.
- Create a smooth transition.
Make main screen smaller
Transform
is a widget that applies a transformation before painting its child, you can find more about this widget in here. So lets wrap our main content with a Transform
widget:
Stack(
children: [
Scaffold(
backgroundColor: const Color(0xff22a6b3),
body:const DrawerData(),
),
Transform(
transform: Matrix4.identity()
..scale(0.6),
alignment: Alignment.center,
child: Scaffold(),
)
],
);
The transform
is the matrix to transform the child during painting and by using scale
we made the child 60% of the real size, if you want to find out more about 4D Matrix take a look at this codemagic post. Now our app must look like this:
Move the main screen to the right
Moving the screen is not a hard task, with the help of previously created Tranform
widget we move our child widget:
final rightSlide = MediaQuery.of(context).size.width * 0.6;
...transform: Matrix4.identity()
..translate(rightSlide)
..scale(0.6),
We should consider that changing the order of the transform value may change the result
And the current state of the app:
Create a smooth transition
To make a smooth transition to the right side of the screen we need AnimationController
, it’s a class to handle animations like:
- Play an animation forward or in reverse, or stop an animation.
- Set the animation to a specific value.
- Define the upperBound and lowerBound values of an animation.
- Create a fling animation effect using a physics simulation.
To handle duration and vsync
we also need to use a Mixin
:
class _HomeFormState extends State<HomeForm>
with SingleTickerProviderStateMixin { AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
} @override
void dispose() {
_animationController.dispose();
super.dispose();
}
vsync
allows us to mute, slow or fast forward our animations.
We also need a method to handle the open/close animation:
_toggleAnimation() {
_animationController.isDismissed
? _animationController.forward()
: _animationController.reverse();
}
Where:
- Forward: Starts running this animation forwards (towards the end).
- Reverse: Starts running this animation in reverse (towards the beginning).
Now all we need is wrap our Stack
with a AnimatedBuilder and change the scale
and translate
value with the _animationController
related value, so:
return AnimatedBuilder(
animation: _animationController,
builder: (context,child){
// Related Scale and Translate values
double slide = rightSlide*_animationController.value;
double scale = 1-(_animationController.value*0.3);
return Stack(
children: [
Scaffold(
backgroundColor: const Color(0xff22a6b3),
body:const DrawerData(),
),
Transform(
transform: Matrix4.identity()
..translate(slide)
..scale(scale),
alignment: Alignment.center,
child: Scaffold(),
)
],
);
},
);
We also change the drawer menu icon with an AnimatedIcon
for a better UI design:
IconButton(
onPressed: ()=>_toggleAnimation(),
icon: AnimatedIcon(
icon: AnimatedIcons.menu_close,
progress: _animationController,
),
)
And that’s all we need for a smooth transition:
You can find the final code on Github!