Flutter 动效实战 —— 视差轮播效果

Flutter 动效实战 —— 视差轮播效果

Android小彩虹2021-08-20 19:17:48280A+A-

首先我们来看一下本文要实现的效果:

第一眼看到这个效果时,按照我们的惯性思维,通常会这样做:

  1. 获取到 ViewPager
  2. 设置监听器或代理来响应滚动事件
  3. 获取 onscreen 的 View,计算并设置其 translation

但是在 Flutter 中我们要转变思路,因为 Flutter 是声明式 UI 框架,每一帧的变化都需要生成一个新的 Widget Tree,所以我们只需要关心视图与视图之间的关系,然后在构造 Widget Tree 时计算好数值带入构造方法中即可。

要达到上面的效果,小字 Text 在页面滑动时要比整体移动速度快一倍,所以小字的 translate X 为 \tt{pageWidth / 2 * progress}

这里涉及两个要点:在 Flutter 里如何获取 Widget 的大小,如何监听页面滚动。

首先是获取 Widget 大小,由于 Flutter 中的 Widget 都是自适应大小的,所以对于一个 Widget,它的大小要么尽可能大(如 PageView),要么尽可能小(如 Text),所以我们能获取到的只是一个范围(约束)。如要获得某一 Widget 的尺寸范围,只需给它包裹一层 LayoutBuilder 的 Widget,就像这样:

new LayoutBuilder(builder: (context, constraints) => new XXXWidget());

通过 constrains 即可获得 XXXWidget 的最大和最小尺寸了。由于 PageView 会尽可能大地占满屏幕,所以 constrains.maxWidth 即为 PageView 在运行时真实的宽度。

其次是如何获取滚动事件,Flutter 内建了 Notification 机制,一个 Widget 可以通过 Notification 将一个事件冒泡到 Widget Tree 的上层节点。

监听一个 Notification 的方法是用 NotificationListener 包裹需要监听事件的子视图,具体的使用过程在下文会讲到。

Flutter 会根据平台自动应用一些动画和视觉效果,例如 PageView 在 iOS 上会产生越界回弹效果(也就是视频所展示的),而在 Android 上会呈现波纹的效果,这个效果可以通过指定滚动视图的 ScrollPhysics 来实现。

下面我们来看一下最终实现的代码:

class PageDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new _PageDemoState();
}

class _PageDemoState extends State<PageDemo> {
  final PageController _pageController = new PageController();
  double _currentPage = 0.0;

  @override
  Widget build(BuildContext context) => new Scaffold(
    appBar: new AppBar(
      title: const Text('Page Demo'),
    ),
    body: new LayoutBuilder(builder: (context, constraints) => new NotificationListener(
      onNotification: (ScrollNotification note) {
        setState(() {
          _currentPage = _pageController.page;
        });
      },
      child: new PageView.custom(
        physics: const PageScrollPhysics(parent: const BouncingScrollPhysics()),
        controller: _pageController,
        childrenDelegate: new SliverChildBuilderDelegate(
          (context, index) => new _SimplePage(
            '$index',
            parallaxOffset: constraints.maxWidth / 2.0 * (index - _currentPage),
          ),
          childCount: 10,
        ),
      ),
    )),
  );
}

为了获取滚动的进度,我们可以指定 PageController,它是 ScrollController 的子类,用于管理滚动视图的状态,它同时也暴露了一系列属性和方法用于控制滚动视图,在构建 PageView 时手动指定一个 PageController 便可以之后获取 PageView 的状态。

需要注意的是,NotificationListener 在调用 onNotification 时并不会刷新视图,因此我们还需要调用根视图的 setState 来刷新视图状态。

接下来实现轮播页面:

class _SimplePage extends StatelessWidget {
  _SimplePage(this.data, {
    Key key,
    this.parallaxOffset = 0.0
  }) : super(key: key);

  final String data;
  final double parallaxOffset;

  @override
  Widget build(BuildContext context) => new Center(
    child: new Center(
      child: new Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          new Text(
            data,
            style: const TextStyle(fontSize: 60.0),
          ),
          new SizedBox(height: 40.0),
          new Transform(
            transform: new Matrix4.translationValues(parallaxOffset, 0.0, 0.0),
            child: const Text('Yet another line of text'),
          ),
        ],
      ),
    ),
  );
}

这里我们直接使用 StatelessWidget 即可,通过接受构造方法的参数,将 translation 传递给 Transform 元素即可。

小结

通过这个例子我们可以看出,在 Flutter 中,所有的效果都是以声明式的方法实现的,很少会用到 OOP 的方式,你几乎不会看到像 setOpacity、setScale 的这类方法。另外 Flutter 广泛地使用了各种层次结构来实现各种扩展效果,比如变换、滤镜、大小约束这样的效果都是通过为内容视图包裹一层装饰视图来实现的,具体的就要多看官方文档了。总而言之,在 Flutter 中实现复杂的界面效果还是非常容易的。

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们