In the previous, we explain how to make a login screen, but then what? Totally feel hastily ending Well, it does not, the next step is for everyone to explain in detail, this may not log in which the bird story. First look at the process of a log ~
analysis
Possible above gif
map is not very clear, that the above shows two functions:
- Splash page color conversion
- Animated login page
There is no feeling like this login it seems pretty good, ha ha ha, then analyze in detail the mystery of which ~
routing
Generally, we will be involved in the page to jump to the route, the route is to jump from one page to another page of the process, such as on the Android Activity
or IOS in the ViewController
jump.
In Flutter in so routing uses Navigator
to manage, in other words it is to make these otherwise relatively independent individuals form a perfect whole. So Navigator
is the direct management of that page? Of course not, in fact, it is a management Route
target, and provides related management stack method, such as:
- Navigator.push (stack)
- Navigator.pop (pop)
While the ability to create a direct navigator
, but it is generally not recommended to use directly, we often pass WidgetsApp
or MaterialApp
to create. Remember the first article of the time, just as we mentioned, Flutter offers many widgets, follow can help you build Material Design
your application. Material application to start MaterialApp widget, the widget created some useful widget at the root of the application, which includes a Navigator
, which is managed by Widget stack logo string (ie, page routing stack). Navigator allows your application to smooth transitions between pages. So our application launch general wrote:
void main() {
runApp(MaterialApp(home: MyAppHome()));
}
So, home
it points to a page that is the bottom of our stack routing, that MaterialApp
in the end is how to create the underlying routing it? It follows the following principles:
const MaterialApp({
Key key,
this.navigatorKey,
this.home,
this.routes = const <String, WidgetBuilder>{},
this.initialRoute,
this.onGenerateRoute,
this.onUnknownRoute,
//省略无关代码
...
})
- The first to use our
home
points to - If it fails, it will use
routes
the routing table - If the routing table is empty, it will call
onGenerateRoute
- If all of the above fail, it
onUnknownRoute
will be called
So if you want to create Navigator
, it must have a four or more is used.
MaterialPageRoute
Generally, we can use MaterialPageRoute
to route:
Navigator.push(context, MaterialPageRoute<void>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('My Page')),
body: Center(
child: FlatButton(
child: Text('POP'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
));
In this manner quite clear, which is the use of a build
way to stack (or the stack). As can be seen, when I click on POP
a button when they can use this page to a stack, you can go back to our home
page. Generally, however, we did not go back to the previous page, in the last chapter , when you use Scaffold
the AppBar
can be added directly to a return, fundamental research that ultimately returns the call
Navigator.pop(context);
When we need the time to return with a return value, you can use something like the following way, then you can not use this time Scaffold
of AppBar
the return, because it will not return any results.
Navigator.pop(context, true);
pushNamed
The above is to be routed through a dynamic way, we can also use a static way to route that pushNamed
is routed from the literal meaning of a page by name, then the name come from? This requires the use above us in MaterialApp
the routes
routing table a.
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primaryColor: Color(0xFFFF786E),
primaryColorLight: Color(0xFFFF978F),
accentColor: Color(0xFFFFFFFF)
),
home: Start(),
debugShowCheckedModeBanner: false,
routes:{
"scaffold_page":(context)=>ScaffoldTest(),
"snack_page":(context)=> SnackTest(),
"login_page":(context)=> LoginPage(),
"start_page":(context)=> Start(),
}
);
}
}
Navigator.pushNamed(context, "snack_page");
Well, we can not carry parameters? Of course you can argue
void _showBerlinWeather() {
Navigator.pushNamed(
context,
'/weather',
arguments: <String, String>{
'city': 'Berlin',
'country': 'Germany',
},
);
}
Objects can also carry a self-defined travel ~~
class WeatherRouteArguments {
WeatherRouteArguments({ this.city, this.country });
final String city;
final String country;
bool get isGermanCapital {
return country == 'Germany' && city == 'Berlin';
}
}
void _showWeather() {
Navigator.pushNamed(
context,
'/weather',
arguments: WeatherRouteArguments(city: 'Berlin', country: 'Germany'),
);
}
Of course, there are other ways:
- pushReplacementNamed and pushReplacement replace the current page
- popAndPushNamed current page out of the stack, stack a new page
- All pages pushNamedAndRemoveUntil and pushAndRemoveUntil push and close before a new page
Animation
In the previous gif
figure we can see the splash screen pages have different variations (picture blur effect is not obvious), and click the login time, the button looks like there are changes color at different times, then this is how to achieve it ? Of course, our animation ~~
AnimationController
AnimationController
Used to control the animation forward to play a reverse play and stop the animation and other operations. Default AnimationController
is in a linear animation playback.
Note that the use AnimationController
requires a combination of time TickerProvider
, because only in the TickerProvider
case can configure AnimationController
configuration parameters vsync
. TickerProvider
It is an abstract class, so we generally use its implementation class TickerProviderStateMixin
and SingleTickerProviderStateMixin
.
So, these two methods are different?
If the entire life cycle, only one AnimationController
, then use SingleTickerProviderStateMixin
, as in this case, its efficiency is relatively much higher. Conversely, if there are multiple AnimationController
, it is to use TickerProviderStateMixin
.
Note that, if AnimationController
time does not need to use, be sure to release it out, or may cause a memory leak.
class StartState extends State<Start> with SingleTickerProviderStateMixin {
AnimationController colorController;
@override
void initState() {
// TODO: implement initState
super.initState();
colorController = new AnimationController(
vsync: this, duration: new Duration(seconds: 3));
}
@override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
//省略部分代码
...
),
);
}
@override
void dispose() {
// TODO: implement dispose
colorController.dispose();
super.dispose();
}
}
Animation
With animation controllers After that, we need to animate oh. But we can see that Animation
in itself is an abstract class, so we need to realize is that it's class. We can use directly Tween
or its subclasses to achieve a Animation
in AnimationController
provides one drive
method, which is used to doing it? This is used to link a Tween
to Animation
and returns an Animation
instance.
Animation<Alignment> _alignment1 = _controller.drive(
AlignmentTween(
begin: Alignment.topLeft,
end: Alignment.topRight,
),
);
Why use Tween
it? Tween
Is a linear interpolation, you can implement a complete change process
class Tween<T extends dynamic> extends Animatable<T> {
Tween({ this.begin, this.end });
T begin;
T end;
@protected
T lerp(double t) {
assert(begin != null);
assert(end != null);
return begin + (end - begin) * t;
}
@override
T transform(double t) {
if (t == 0.0)
return begin;
if (t == 1.0)
return end;
return lerp(t);
}
@override
String toString() => '$runtimeType($begin \u2192 $end)';
}
Tween
The structure provides two parameters, a beginning bengin
, an end end
, that allows animation can be varied within this range, of course, it also provides a number of sub-categories, such as: ColorTween
, SizeTween
, IntTween
and CurveTween
so on
ColorTween
Two color variations can be achievedSizeTween
You can implement twosize
changesIntTween
Changes may be achieved between the two values intCurveTween
Non-linear changes in the motion can be achieved
CurvedAnimation
CurvedAnimation
The curve is a (non-linear) change applied to another animation, if you want to use Curve
the application to Tween
call the above can be used directly CurveTween
, or may not CurvedAnimation
.
final Animation<double> animation = CurvedAnimation(
parent: controller,
curve: Curves.ease,
);
It should be a two parameter control is animated, that is, we AnimationController
, the other is curve
, in the end is that it describes the changes according to what kind of curve. In Curves
the process of providing a lot of changes, are interested in children's shoes can go look at yourself ~
Summarize here:
- AnimationController control the entire animation play, stop and other operations
- Change interval Tween animation
- CurvedAniamtion control changes in a nonlinear animation
Splash screen animation to achieve
要实现一个动画的,首先肯定需要上面所说的AniamtionController
和 Animation
,有这个还不够,还需要一个可以根据
在闪屏页面中,我们的动画是颜色根据时间不同的进行变化,那肯定会用到我们的Tween
,这里是颜色的变化,所以使用到了ColorTween
。
@override
void initState() {
// TODO: implement initState
super.initState();
colorController = new AnimationController(
vsync: this, duration: new Duration(seconds: 3));
colorAnimation = colorController
.drive(ColorTween(begin: Color(0xFFFF786E), end: Color(0xFFFFA07A)));
}
一般我们对AniamtionController和Animation的初始化在initState()
方法中,然后就需要在动画的运行过程中将widget
进行更新,就会使用到我们的setState()
colorAnimation = colorController
.drive(ColorTween(begin: Color(0xFFFF786E), end: Color(0xFFFFA07A)))
..addListener(() {
setState(() {});
});
那么接下来就是让整个动画跑起来了~~
Future<Null> playAnimation() async {
try {
await colorController.forward();
await colorController.reverse();
} on TickerCanceled {}
}
这里使用到了dart
语言中的异步,有两个特点:
await
返回一定是Future
,如果不是会报错await
所在的方法必须在有async
标记的函数中运行。
上面的意思就是让动画先正向进行,然后在反向进行~~
但是发现动画写完之后运行,但是没有任何作用,这是因为你没有将动画的变化应用到widget
上
@override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
decoration: BoxDecoration(color: colorAnimation.value),
child: Center(
...
//省略无关代码
),
),
);
}
在上述代码中的BoxDecoration(color: colorAnimation.value)
就是将颜色的值作用于整个Container
上,所以颜色就随之变化而变化。
在动画结束的时候不是要进行路由跳转到下一个页面的嘛?这就需要在对动画的监听,当动画结束的时候就进行跳转,就需要修改colorAnimation
colorAnimation = colorController
.drive(ColorTween(begin: Color(0xFFFF786E), end: Color(0xFFFFA07A)))
..addListener(() {
if (colorController.isDismissed) {
Navigator.pushAndRemoveUntil(context,
new MaterialPageRoute(builder: (context) {
return LoginPage();
}), ModalRoute.withName("start_page"));
}
setState(() {});
});
这里需要注意的是,在判断结束的时候,这里使用的是colorController.isDismissed
,没有使用colorController.isCompleted
是因为在正向动画完成的时候就会调用,还没让这个动画流程运行完成~~
如果需要完整代码,就可以来这儿。
登录动画实现
这里和上面是一样的实现动动画,但是直接使用的是Tween
,而且使用了另一种将Tween
关联到Animation的方式,而且使用
@override
void initState() {
// TODO: implement initState
super.initState();
_animationController = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 1500));
_buttonLengthAnimation = new Tween<double>(
begin: 312.0,
end: 42.0,
).animate(new CurvedAnimation(
parent: _animationController, curve: new Interval(0.3, 0.6)))
..addListener(() {
if (_buttonLengthAnimation.isCompleted) {
if(isLogin){
Navigator.pushNamedAndRemoveUntil(context, "snack_page",ModalRoute.withName('login_page'));
}else{
showTips("登录失败");
}
}
setState(() {});
});
}
One thing to note here, using curve
Shi Interval
, this role is animated show based on the time interval you provide. As specified above, the size of the animation time is 1500ms, then only 1500 * 0.3 = 500 ms time to start and finish in 1500 * 0.6 = 900ms time.
Then the next change directly see the animation of the widget
process
InkWell(
onTap: login,
child: Container(
margin: EdgeInsets.only(top: 30),
height: 42,
width: _buttonLengthAnimation.value,
decoration:BoxDecoration(borderRadius: radius, color: colorWhite),
alignment: Alignment.center,
child: _buttonLengthAnimation.value > 75? new Text("立即登录",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: colorRegular))
: CircularProgressIndicator( valueColor:
new AlwaysStoppedAnimation<Color>(colorRegular),
strokeWidth: 2,
),
),
),
① When you click the login button to start the animation, and the width of the button to start the change
width: _buttonLengthAnimation.value,
② When the animation is also greater than the value of 75, the middle is displayed Text
, but if the time is less than or equal to 75, then it is a child's progress is a circularCircularProgressIndicator
child: _buttonLengthAnimation.value > 75? new Text("立即登录",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: colorRegular))
: CircularProgressIndicator( valueColor:
new AlwaysStoppedAnimation<Color>(colorRegular),
strokeWidth: 2,
),
),
In fact, this is the whole animation process, I just made a judgment on the animation operation, when the login fails, let the animation button to return to the original state, and prompts for login failure. If the login is successful, it jumps directly to the new page ~
to sum up
Here we structured to facilitate the consolidation of memory
- There are many routes in the way, you can choose according to different situations, it is commonly used
push
andpushNamed
, if it ispushNamed
then it must be inMaterialApp
the routing table settings. - With the use of animation in general need
TickerProvider
to use fit, ifState
it can be used directly in the implementation classSingleTickerProviderStateMixin
orTickerProviderStateMixin
. - If only one
AnimationController
is usedSingleTickerProviderStateMixin
, on the contrary, to useTickerProviderStateMixin
. - Build animation with
AnimationController
,Tween
orCurveAnimation
related. AnimationController
When no longer needed must be releaseddispose
, otherwise it may cause memory overflow.