I’ve seen a cool landing page effect on UIMovement these days, as follows:
I thought it was cool, so I implemented it myself. The result is as follows:
Here’s a step by step analysis of how it’s done.
Demand analysis
First, let’s take a look at what needs to be done:
- The first and most obvious requirement is to click “Register” to pop up the Dialog
- A delay after a Dialog is popped to pop the contents of the Dialog
- The caption inside the Dialog has two colors
- Clicking the “Accepter” button will change color and shrink the rebound and display the OK icon
- Click “Accepter” button and all other text in Dialog is “white masked”
- After the “Accepter” button is animated, dismiss the current dialog and move the logo up
- Jump to the second page and the text pops up in waves
- Text pop-up after the display dialog box and pop-up keyboard
Begin to implement
With the requirements understood, here is the step-by-step implementation.
1. Click “Register” to pop up Dialog
One thing to note here:
When we use the showModalBottomSheet, the default background is white, which means that the rounded corners we set ourselves don’t work,
So to give this BottomSheet a background, we have this argument in the showModalBottomSheet method:
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (context) {
Future.delayed(Duration(milliseconds: 50), () {
_animationController.forward();
});
returnAnimatedUserAgreement( animation: _animation, ); });Copy the code
Set a backgroundColor and it’s ok.
2. Delay the contents of a Dialog after it pops up
Here I write an “AnimatedWidget” that animates the transparency and position of the Widget in the Dialog:
return Container(
height: 270,
padding: EdgeInsets.all(30),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30), color: Colors.white),
child: Opacity(
opacity: _opacityTween.evaluate(animation),
child: Stack(
children: <Widget>[
SingleChildScrollView(
child: Container(
child: UserAgreementDialog(),
margin: EdgeInsets.only(top: _offsetTween.evaluate(animation)),
),
)
],
),
),
);
Copy the code
If you are careful, you may notice that there are some problems with the above code:
Why SingleChildScrollView?
Because the animation I used to change the position was “dynamically change the value of the Container Margin”,
So if you don’t use the ScrollView, it will overflow.
3. The caption inside the Dialog has two colors
The requirement to have two colors is relatively simple, just use “TextSpan”.
I won’t post the code.
4. Clicking the “Accepter” button will change color, shrink and bounce back and display the OK icon
The point is, this feature is relatively complex, but it is also relatively simple to write once we understand the requirements.
First of all, let’s split this function point as well:
- It changes color when you click the button
- When clicked, it changes back to its original color and shrinks to a circle
- The animation displays the OK icon after turning into a circle
One step at a time.
1. It changes color when you click the button
There is no need to think too much about this function. Since there is a click gesture, GestureDetector will definitely be used.
Then use the GestureDetector’s onTapDown parameter, which is called back on “click and press” :
onTapDown: (d) {
setState(() {
btnColor = btnColors[1];
});
Copy the code
There’s nothing more complicated than changing the color of the buttons.
2. After clicking, it will change back to its original color and shrink to a circle
How to deal with post-click? Or didn’t click?
GestureDetector also wrapped it for us:
- OnTapUp: Callback when clicked lift
- OnTapCancel: Callback when a click is cancelled
First we deal with the unclick:
onTapCancel: () {
setState(() {
btnColor = btnColors[0];
});
}
Copy the code
Just change the color back.
Then handle the logic at lift, which also has two logic at lift:
- The button will shrink to a circle
- The OK icon pops up when zoomed out to a circle
Let me start with the first point:
There is a rebound effect when zooming out to a circle, so you can’t use AnimatedContainer, you have to use Animation for this effect.
So I used AnimatedBuilder to wrap the Widget.
And then point number two:
How to pop ok icon when zooming out to a circle?
We can use IndexStack to toggle index at the beginning of the zoom animation, because the OK icon starts with 0 zoom, so there is no icon on the page, so we can do the animation later.
The Widget code is as follows:
AnimatedBuilder(
animation: _widthAnimation,
builder: (BuildContext context, Widget child) {
return Container(
width: _widthAnimation.value,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(40)),
color: btnColor),
margin: EdgeInsets.only(top: btnMargin),
height: btnHeight,
child: IndexedStack(
alignment: Alignment.center,
index: index,
children: <Widget>[
Text(
'Accepteer',
style: TextStyle(fontSize: 18),
),
ScaleTransition(
scale: _scaleTween.animate(_animation),
child: Image.asset(
'images/ok.png',
width: 35,
height: 35() [() [(). },),Copy the code
5. Other text in Dialog is “white shaded” when clicking “Accepter” button
The default parameter of the Container is foregroundDecoration. We only need to set the color we want to mask in this parameter.
However, it should also be noted that if the mask color is “transparent color” at the beginning, the overall color will be black. I have not studied the specific reason, but if you know, you can tell me.
Click the button after the effect is complete, the code is as follows:
onTapUp: (d) {
Future.delayed(Duration(milliseconds: 60), () {
setState(() {
foregroundColor = Colors.white70;
btnColor = btnColors[0];
index = 1;
});
_widthController.forward();
Future.delayed(Duration(milliseconds: 200), () {
_controller.forward().then((va) {
Navigator.pop(context);
});
});
});
}
Copy the code
6. After the animation ends, dismiss the current dialog and move the logo up
This is relatively easy, we just need to put an AnimatedContainer on top of the logo,
Then listen to see if the dialog has been dismissed, and if so adjust the margin value.
The code is as follows:
setState(() {
logoMargin = 100;
});
Copy the code
This happens to give the dialog an animation that moves down, and the logo moves up, which is what we want.
7. Skip to page 2, where the text pops up in waves
How to pop the text in a wave?
The first thing that comes to mind is animation, because only animation can have this kind of rebound effect,
So that’s a lot of text, and each one has to be animated?
The answer is yes.
Well, now that we know, we’ll just have to go through the motions.
As you can see, each text goes from transparent to opaque and changes its position,
Let’s start by encapsulating an AnimatedWidget.
The code is as follows:
class AnimatedStrWidget extends AnimatedWidget {
final Tween<double> _opacityTween = Tween(begin: 0, end: 1);
final Tween<Offset> _offsetTween =
Tween(begin: Offset(0.3), end: Offset(0.0));
final Widget child;
AnimatedStrWidget(
{Key key, @required Animation<double> animation, @required this.child})
: super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return Opacity(
opacity: _opacityTween.evaluate(animation) < 0
? 0
: _opacityTween.evaluate(animation) > 1
? 1: _opacityTween.evaluate(animation), child: SlideTransition( position: _offsetTween.animate(animation), child: child, ), ); }}Copy the code
There are also two things to note here:
- Transparency can’t be negative and can’t be greater than 1, because our effect is to have a rebound effect, so we need to make a judgment.
- Tween
the value here is a multiple of the entire height, so don’t think of it as a pixel value.
Then we can have fun:
void startAnim() async {
for (int i = 0; i < strs.length; i++) {
Future.delayed(
Duration(
milliseconds: i * 100, ), () { _strController[i].forward(); }); }}Copy the code
Text pop-up effect time is 600ms, set here to make an animation every 100ms,
This effect is better, more like a wave pop.
8. After the text pops up, the dialog box is displayed and the keyboard pops up
Active keyboard pop-ups we’re all familiar with, using FocusNode,
Here, we just need to determine when the last animation is done, and then pop the hidden keyboard, and pop the keyboard is ok.
The code is as follows:
_strPositionAnimation[strs.length - 1].addStatusListener((status){
if(status == AnimationStatus.completed){
setState(() {
opacity = 1; FocusScope.of(context).requestFocus(myFocusNode); }); }});Copy the code
conclusion
It took me an evening to implement the page, and I have to say, it was quite a bit.
Implementing such a cool landing page is complicated.
My implementation here is not perfect and looks a little “anxious” compared to the original.
But it doesn’t matter, just change the duration of the animation.
Again, sort out the needs, what is easy to do.
The code has been uploaded to GitHub: github.com/wanglu1209/…
I also created a “Flutter communication group”. I can add my personal wechat account “17610912320” to the group.
Recommended reading:
Flutter | WReorderList one can specify two item swap position of components
Flutter | how to implement a water ripples spread Widget
A Stepper step Flutter | custom components