animations 2.0.1 源码解读

727 阅读1分钟

animations 是一款flutter插件,提供了封装好的一些路由切换动画。最近闲来无事去看了一下它的代码,理解其关键部分代码,并实现一个精简版的animations插件。虽然不完善,但是总体思路还是对的。

首先看OpenContainer类,关键部分是传入两个Widget,一个openBuilder是打开后要展示的页面,closeBuilder被点击之后能触发动画的控件。

  Widget build(BuildContext context) {
    return OpenContainer(
      tappable: true,
      openBuilder: (context,VoidCallback _){
        return const _DetailsPage();
      },
 
      closedBuilder: closedBuilder,
      onClosed: onClosed,
      useRootNavigator: true,
    );
  }

OpenContainer类里面有个openContainer函数,用于切换到指定路由。

  Future<void> openContainer() async {
      await Navigator.of(context,rootNavigatowidget.useRootNavigator).push(_OpenContainerRoute<bool>(
      closedBuilder: widget.closedBuilder,
      openBuilder: widget.openBuilder,
      hideableKey: _hideableKey,
      closedBuilderKey: _closedBuilderKey,
      useRootNavigator: widget.useRootNavigator,
      transitionDuration: Duration(milliseconds: 3000))
  );
}

跟踪代码到类_OpenContainerRoute,看到其继承自ModalRoute类。继承此类事为了重写路由切换时的回调函数,以此来获取closeBuilder构建的控件的位置,上级路由的位置,用于指定路由切换的起点和终点。

class _OpenContainerRoute<T> extends ModalRoute<T> {
_OpenContainerRoute({
  required this.closedBuilder,
  required this.openBuilder,
  required this.hideableKey,
  required this.closedBuilderKey,
  required this.useRootNavigator,
  required this.transitionDuration,
});

那么重写的是哪个函数呢?关键位置如下:

TickerFuture didPush(){
  _takeMeasurements(navigatorContext: hideableKey.currentContext!);
}

调用了_takeMeasurements测量动画的起始位置。其中hideableKey标记的是closeBuilder构造的组件的位置。

起始位置有了,控制路由切换的动画就简单了,关键代码如下:

Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
    return Align(
      alignment: Alignment.topLeft,
      child: AnimatedBuilder(
        animation: animation,
        builder: (BuildContext context, Widget? child) {
          final Animation<double> curvedAnimation = CurvedAnimation(
            parent: animation,
            curve: Curves.fastOutSlowIn,
            reverseCurve:
            false ? null : Curves.fastOutSlowIn.flipped,
          );
 
          final Rect rect = _rectTween.evaluate(curvedAnimation)!;
          return SizedBox.expand(
            child: Container(
              color: Colors.transparent,
              child: Align(
                  alignment: Alignment.topLeft,
                  child:  Transform.translate(
                    offset: Offset(rect.left,rect.top),
                    child: SizedBox(
                      width: rect.width,
                      height: rect.height,
                      child: Material(
                        clipBehavior: Clip.antiAlias,
                        animationDuration: Duration.zero,
                        child: Stack(
                          fit: StackFit.passthrough,
                          children: [
                            openBuilder(context, (){})
                          ],
                        ),
                      ),
                    ),
                  )
              ),
            ),
          );
        },
      ),
    );
  }

运行效果:

下载.gif

完整代码:GitHub - obweix/mini_animations