Daily Cooking Quest Animation — Part 1

Daily Cooking Quest Animation — Part 1Exploring Animation in FlutterXuzan LamaBlockedUnblockFollowFollowingJul 8Animation is an integral part of building a mobile application.

It gives the app a refined look which determines the quality of your application.

With the introduction of flutter, mobile application development has had a major change.

With its cross-platform code base, crisp animation, and because it is easy to learn and use, it is one of the growing platforms.

In this article, we will be exploring flutter animation by trying to replicate Dribbble UI of a daily cooking quest by Hoang Nguyen.

Dribbble UI by Hoang Nguyen.

For this tutorial, We will be using the following widgets to accomplish our target.

If you are not familiar with these, you can check the Flutter Widgets section to know more.

Widgets used:Container, Row, ColumnStack and PositionedAlignGesture detectorTransformFor animation:Animated BuilderAnimated ContainerHeroGetting StartedLet’s create a new flutter project named ui_challenge_cooking.

You can directly create flutter project from IDE.

From terminal,flutter create ui_challenge_cookingAfter all the build process is completed, you are greeted with main.

dart file which includes auto-generated flutter code.

Let’s remove this code and replace it with the following code.

Here, Homepage is our stateful widget where we will be coding our application.

Let’s create homepage.

dart in lib/ui/ folder structure.

First, create homepage.

dart.

The folder structure I used is lib/ui/homepage.

dart.

Then, write a basic animation boilerplate code.

By pressing stanim and TAB, IDE will generate this code.

I changed SingleTickerProviderStateMixin with TickerProviderStateMixin as we will be using more than one animation inside this file.

Note: You can download assets from my GitHub and include assets in pubspec.

yaml file.

Let’s go through what will we be doing in this tutorial step by step,Create food details model where all the information such as cooking time and its assets path is providedAlign plate to the center_vertical side of the screen with only half of it displayingAdd rotate animation to the plate with a swipe gestureAdd background animation as shown in the UIAdd detail of food and it’s text animationStep 1: Create a food modelHere, dummy list named foodList is created at the bottom, which we will be working on.

Step 2: Align platehomepage.

dartHere, to use animation, we are wrapping widget with AnimationBuilder widget and providing animation as _controller which controls the animation.

The stack widget is used for _buildPlate() method as other animation such as background, text detail will be added inside the stack.

An inside _buildPlate() method, align widget with alignment centerRight to move the plate to the center right of the screen.

As we will only need to show half of the plate, we are wrapping align widget to Transform.

translate with offset (80.

0, 0.

0).

You can learn more about offset in flutter widget link.

Step 3: Adding rotate animation with the swipe gestureCreate clockwise and anticlockwise animation so that rotate animation will take place according to swipe gesture.

Tween animation is needed to control animation to value.

We need a flag to determine, whether user swipes anticlockwise direction or clockwise.

Rotation value will change the rotation of the plate.

Animation<double> _clockWiseRotationAnimation; Animation<double> _antiClockWiseRotationAnimation; Tween<double> _antiClockWiseRotationTween; Tween<double> _clockWiseRotationTween; bool isClockwise = false; double rotationValue = 0.

0;initialize those values in initState().

@overridevoid initState() { super.

initState(); _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 800)); _clockWiseRotationTween = Tween<double>(end: 2 * pi); _clockWiseRotationAnimation = _clockWiseRotationTween.

animate( CurvedAnimation(parent: _controller, curve: Curves.

elasticOut)); _antiClockWiseRotationTween = Tween<double>(end: -2 * pi); _antiClockWiseRotationAnimation = _antiClockWiseRotationTween.

animate( CurvedAnimation(parent: _controller, curve: Curves.

elasticOut)) .

addListener(() { setState(() { rotationValue = isClockwise ? _clockWiseRotationAnimation.

value : _antiClockWiseRotationAnimation.

value; }); });}Here, the curved animation with elastic out is used for the overshoot effect after the end of animation.

The listener is added in animation to change rotation value state.

Now, let’s create gesture detection with rotate animation.

To create a gesture detection function,Widget _buildGestureDetection() {// this positioned widget will add gesture detection to right side // of screen to center of screenreturn Positioned( top: 0.

0, bottom: 0.

0, left: MediaQuery.

of(context).

size.

width / 2, right: 0.

0, child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, onVerticalDragEnd: _onVerticalDragEnd, onVerticalDragUpdate: _onVerticalDragUpdate, child: Container( color: Colors.

transparent, ), ), );}void _onVerticalDragStart(DragStartDetails details) {}void _onVerticalDragUpdate(DragUpdateDetails details) { print(details.

delta.

dy); isClockwise = details.

delta.

dy < 0; // here if swipe is upward, details.

delta.

dy is -ve.

Check log to // verify}void _onVerticalDragEnd(DragEndDetails details) { rotationValue = 0.

0; _antiClockWiseRotationTween.

begin = rotationValue; _clockWiseRotationTween.

begin = rotationValue; if (!_controller.

isAnimating) { _controller.

forward(from: 0.

0); } _changeFood();}At the end of the drag end, we will need to change the index so that the food item is changed.

void _changeFood() {// If anticlockwise, increase index and vice versa.

setState(() { if (!isClockwise) { currentIndex = currentIndex < foodList.

length – 1 ? currentIndex + 1 : 0; } else { currentIndex = currentIndex > 0 ? currentIndex – 1 : foodList.

length – 1; } });}Here, we will add _buildGestureDetection() function inside the stack we created before in the build function.

Stack( fit: StackFit.

expand, children: <Widget>[ _buildPlate(), _buildGestureDetection(), ],),.

After all this code, it should look like this.

Try and check if the plate is animating and changing.

Up to this process, it will look like this,Step 4: Change background on item changeHere for background, we will be using the animated container.

So, let us set a flag if the background is black or not.

And the height of the black background is set as 0.

0 at first which will be used in animated container height.

bool isBgBlack = false;double blackBgHeight = 0.

0;and on function _onVerticalDragEnd, toggle it.

void _onVerticalDragEnd(DragEndDetails details) { rotationValue = 0.

0; _antiClockWiseRotationTween.

begin = rotationValue; _clockWiseRotationTween.

begin = rotationValue; if (!_controller.

isAnimating) { _controller.

forward(from: 0.

0); isBgBlack = !isBgBlack; // new line setState(() { backBgHeight = isBgBlack?.MediaQuery.

of(context).

size.

height : 0.

0; // new line }); }}Here, if isBgBlack is true, we will be showing this black background covering the full width of the screen and if it is false, it is set to 0.

0.

Now, to implement this background.

On build, add _buildBlackBg() in Stack at the top.

Stack( fit: StackFit.

expand, children: <Widget>[ _buildBlackBg(), _buildPlate(), _buildGestureDetection() ],).

Create _buildBlackBg()Widget _buildBlackBg() { return Positioned( top: 0.

0, left: 0.

0, right: 0.

0, child: AnimatedContainer( duration: Duration(milliseconds: 400), color: Color(0xFF384450), height: blackBgHeight), );}Now, on swipe, the background will change alternately.

Step 5: Add food item detailsAs all the food detail in the UI has some text with icon on the left.

Let us make a general widget called FoodAttributes.

Create food_attributes.

dart inside the ui folder.

This takes text, icon, textColor, and opacity for animation.

We need two types of animation, one for textColor when the background is set to black, and one for its opacity, if you noticed, the text will be invisible first and then visible in the UI.

AnimationController _textOpacityController;Animation<double> _textOpacityAnimation;AnimationController _textColorController;Animation<Color> _textColorAnimation;@overridevoid initState() { super.

initState(); .

_textOpacityController = AnimationController(vsync: this, duration: Duration(milliseconds: 400));_textOpacityAnimation = Tween<double>(begin: 1.

0, end: 0.

0).

animate(_textOpacityController) .

addStatusListener((status) { if (status == AnimationStatus.

completed) { _changeFood(); _textOpacityController.

reverse(); } });_textColorController = AnimationController(vsync: this, duration: Duration(milliseconds: 400));_textColorAnimation = ColorTween(begin: Colors.

black, end: Colors.

white) .

animate(_textColorController);}@overridevoid dispose() { _controller.

dispose(); _textOpacityController.

dispose(); _textColorController.

dispose(); super.

dispose();}Here, two controllers are defined, _textOpacityController and _textColorController as these animations are independent of plate animation controller i.

e _controller.

_textOpacityController is of half of the time than other two animations.

So, we will add a listener and reverse animation when an animation is completed.

Also, we will call _changeFood() function inside it and remove it from _onVerticalDragEnd().

It will give a smoother transition between animation.

Update _onVerticalDragEnd function.

It should look like this,void _onVerticalDragEnd(DragEndDetails details) { rotationValue = 0.

0; _antiClockWiseRotationTween.

begin = rotationValue; _clockWiseRotationTween.

begin = rotationValue; if (!_controller.

isAnimating) { _controller.

forward(from: 0.

0);_textOpacityController.

forward(from: 0.

0);!isBgBlack?._textColorController.

forward(from: 0.

0) : _textColorController.

reverse();isBgBlack = !isBgBlack; setState(() { blackBgHeight = isBgBlack?.MediaQuery.

of(context).

size.

height : 0.

0; }); }}This new line will control the text color.

It animates text color to white if the background is black and vice versa.

Now, let’s implement the food item detail.

Add _buildText() function to stack,.

Stack( fit: StackFit.

expand, children: <Widget>[ _buildBlackBg(), _buildText(), _buildPlate(), _buildGestureDetection() ],),.

Now create _buildText() function,Widget _buildText() { return Positioned( top: 60.

0, left: 32.

0, right: 80.

0, bottom: 0.

0, child: ListView( children: <Widget>[ Text( "DAILY COOKING QUEST", style: TextStyle( color: Colors.

grey, fontSize: 24.

0, fontWeight: FontWeight.

bold), ), SizedBox( height: 12.

0, ), Opacity( opacity: _textOpacityAnimation.

value, child: Text( foodList[currentIndex].

foodName, style: TextStyle( color: _textColorAnimation.

value, fontSize: 22.

0, fontWeight: FontWeight.

bold), ), ), SizedBox( height: 60.

0, ), FoodAttributes( text: foodList[currentIndex].

cookingDifficulty, icon: Icons.

hot_tub, color: _textColorAnimation.

value, opacity: _textOpacityAnimation.

value, ), FoodAttributes( text: foodList[currentIndex].

cookingTime, icon: Icons.

timer, color: _textColorAnimation.

value, opacity: _textOpacityAnimation.

value, ), FoodAttributes( text: foodList[currentIndex].

foodEffect, icon: Icons.

spa, color: _textColorAnimation.

value, opacity: _textOpacityAnimation.

value, ), FoodAttributes( text: foodList[currentIndex].

foodType, icon: Icons.

four_k, color: _textColorAnimation.

value, opacity: _textOpacityAnimation.

value, ), SizedBox( height: 50.

0, ), Opacity( opacity: _textOpacityAnimation.

value, child: Text( foodList[currentIndex].

foodDescription, style: TextStyle( color: _textColorAnimation.

value, fontWeight: FontWeight.

w600, fontSize: 16.

0, ), ), ) ], ), );}Finally the homepage.

dart file will look like this,This will give the following result,Concluding Part 1We covered just the basic introduction of how Flutter animation can be used.

As I am also a beginner in flutter, this tutorial may have many flaws.

If you have any suggestions, criticism, and questions, please do comment below.

I would love you hear from you.

In the next tutorial, We will be adding a floating action button.

Pressing it will lead to a detail page using Hero animation.

This detail page will again contain some more animations.

You can see branch part 1 in my GitHub repo for this part 1 tutorial.

Master contains all of the completed code.

https://github.

com/Xuzan10/ui_challenge_cooking.

. More details

Leave a Reply