Flutter Beginners (c) - how to log in animation

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 ~

Login failed

analysis

Possible above gifmap 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 Activityor IOS in the ViewControllerjump.

In Flutter in so routing uses Navigatorto manage, in other words it is to make these otherwise relatively independent individuals form a perfect whole. So Navigatoris the direct management of that page? Of course not, in fact, it is a management Routetarget, 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 WidgetsAppor MaterialAppto create. Remember the first article of the time, just as we mentioned, Flutter offers many widgets, follow can help you build Material Designyour 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, homeit points to a page that is the bottom of our stack routing, that MaterialAppin 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 homepoints to
  • If it fails, it will use routesthe routing table
  • If the routing table is empty, it will callonGenerateRoute
  • If all of the above fail, it onUnknownRoutewill be called

So if you want to create Navigator, it must have a four or more is used.

MaterialPageRoute

Generally, we can use MaterialPageRouteto 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 buildway to stack (or the stack). As can be seen, when I click on POPa button when they can use this page to a stack, you can go back to our homepage. Generally, however, we did not go back to the previous page, in the last chapter , when you use Scaffoldthe AppBarcan 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 Scaffoldof AppBarthe 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 pushNamedis routed from the literal meaning of a page by name, then the name come from? This requires the use above us in MaterialAppthe routesrouting 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:

  1. pushReplacementNamed and pushReplacement replace the current page
  2. popAndPushNamed current page out of the stack, stack a new page
  3. All pages pushNamedAndRemoveUntil and pushAndRemoveUntil push and close before a new page

Animation

In the previous giffigure 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

AnimationControllerUsed to control the animation forward to play a reverse play and stop the animation and other operations. Default AnimationControlleris in a linear animation playback.
Note that the use AnimationControllerrequires a combination of time TickerProvider, because only in the TickerProvidercase can configure AnimationControllerconfiguration parameters vsync. TickerProviderIt is an abstract class, so we generally use its implementation class TickerProviderStateMixinand 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 AnimationControllertime 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 Animationin itself is an abstract class, so we need to realize is that it's class. We can use directly Tweenor its subclasses to achieve a Animationin AnimationControllerprovides one drivemethod, which is used to doing it? This is used to link a Tweento Animationand returns an Animationinstance.

Animation<Alignment> _alignment1 = _controller.drive(
  AlignmentTween(
    begin: Alignment.topLeft,
    end: Alignment.topRight,
  ),
);

Why use Tweenit? TweenIs 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)';
}

TweenThe 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, IntTweenand CurveTweenso on

  • ColorTween Two color variations can be achieved
  • SizeTweenYou can implement two sizechanges
  • IntTween Changes may be achieved between the two values ​​int
  • CurveTween Non-linear changes in the motion can be achieved

CurvedAnimation

CurvedAnimationThe curve is a (non-linear) change applied to another animation, if you want to use Curvethe application to Tweencall 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 Curvesthe process of providing a lot of changes, are interested in children's shoes can go look at yourself ~

Animation relations
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

要实现一个动画的,首先肯定需要上面所说的AniamtionControllerAnimation,有这个还不够,还需要一个可以根据
在闪屏页面中,我们的动画是颜色根据时间不同的进行变化,那肯定会用到我们的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 curveShi 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 widgetprocess

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 pushand pushNamed, if it is pushNamedthen it must be in MaterialAppthe routing table settings.
  • With the use of animation in general need TickerProviderto use fit, if Stateit can be used directly in the implementation class SingleTickerProviderStateMixinor TickerProviderStateMixin.
  • If only one AnimationControlleris used SingleTickerProviderStateMixin, on the contrary, to use TickerProviderStateMixin.
  • Build animation with AnimationController, Tweenor CurveAnimationrelated.
  • AnimationControllerWhen no longer needed must be released dispose, otherwise it may cause memory overflow.

Guess you like

Origin blog.51cto.com/10687520/2416858