前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入了解 Flutter 中的 PageView(含自定义特效)

深入了解 Flutter 中的 PageView(含自定义特效)

作者头像
Jimmy_is_jimmy
发布2024-05-05 08:28:41
1970
发布2024-05-05 08:28:41
举报
文章被收录于专栏:call_me_Rcall_me_R

原文链接:A Deep Dive Into PageView In Flutter (With Custom Transitions) - 原文作者 Deven Joshi

本文采用意译的方式

本文,我们首先看看 PageView 挂件的内容,然后为它自定义一些特效。

探索 PageViews

PageViews 是一个可以在屏幕上生成滚动页面的挂件。这可以是固定的页面列表或者构建重复页面的 builder 函数。PageView 的行为跟 ListView 的在构建元素的意义上类似。

PageViews 的类型有:

  1. PageView
  2. PageView.builder
  3. PageView.custom
PageView(默认构造器)

我们以固定列表页面为例,使其可滚动。

代码语言:javascript
复制
PageView(
  children: <Widget>[
    Container(
      color: Colors.pink,
    ),
    Container(
      color: Colors.cyan,
    ),
    Container(
      color: Colors.deepPurple,
    ),
  ],
)

上面的代码产生下面的效果:

PageView.builder

该构造器传入 itemBuilder 函数和 itemCount 属性,这和 ListView.builder 类似。

代码语言:javascript
复制
PageView.builder(
  itemBuilder: (context, position) {
    return _buildPage();
  },
  itemCount: listItemCount, // 可以是 null
)

就如 ListView.builder 那样,也是根据需求构建子挂件。

如果 itemCount 被设置为 null(或者不设计),页面的列表会被无限生成。

比如,下面代码:

代码语言:javascript
复制
PageView.builder(
  itemBuilder: (context, position) {
    return Container(
      color: position % 2 == 0 ? Colors.pink : Colors.cyan
    );
  },
)

下面是粉红色和青色交替的页面无限列表:

注意:PageView.custom 工作方式和 ListView.custom 一样,这里不再介绍。

Orientation

所有 Page Views 类型都可以有 水平方向 或者 垂直方向 的滚动页面。

代码语言:javascript
复制
PageView(
  children: <Widget>[
    // 这里添加子挂件
  ],
  scrollDirection: Axis.vertical,
)

上面代码效果:

PageSnapping

页面贴合(Page Snapping)允许我们将页面保留在干扰值上。我们可以通过关闭 pageSnapping 的属性来实现。在这种情况下,页面不会滚动到一个整数位置,而是像普通的 ListView 一样的行为。

代码语言:javascript
复制
PageView(
  children: <Widget> [
    // 添加子挂件
  ],
  pageSnapping: false,
)

ScrollPhysics

PageView 可以像 ListView 那样有自定义滚动行为。

代码语言:javascript
复制
PageView(
  children: <Widget>[
    // 添加子挂件
  ],
  physics: BouncingScrollPhysics(),
)

Controlling a PageView

PageView 可以通过添加 PageController 被程序控制。

代码语言:javascript
复制
// 在 build 方法外
PageController controller = PageController();

// 在 build 方法内
PageView(
  controller: controller,
  children: <Widget>[
    // 添加子挂件
  ]
)

滚动的位置,当前页面等通过使用控制器都可以被检测。

注意:controller.currentPage 返回一个 double 值。比如,当滑动页面时,该值逐渐从 1 变为 2,并且不会立即跳到 2。

添加自定义过渡到 PageViews

下面我们讨论使用 Transform + PageView 来添加一些自定义的页面过渡。这部分我们将广泛使用 Transform 挂件,我们推荐大家阅读关于此小挂件的多篇文章。

我们推荐 Deep Dive I wroteWM Leler's Transform article

Transform 1

设置

首先,我们使用一个基本的 PageView.builder

代码语言:javascript
复制
PageView.builder(
  controller: controller,
  itemBuilder: (context, position) {
  },
  itemCount: 10,
)

现在我们有 10 项。

我们使用了一个 PageController 和一个变量来保存当前页面 currentPage

定义 PageController 和变量:

代码语言:javascript
复制
PageController controller = PageController();
var currentPageValue = 0.0;

PageView 滚动时,更新变量

代码语言:javascript
复制
controller.addListener(() {
  setState(() {
    currentPageValue = controller.page;
  });
});

最后,我们构建 PageView

现在,我们检查三个条件:

  1. 如果页面是正在被滑动的页面
  2. 如果页面是正在被滑动到的页面
  3. 如果页面是一个离屏的页面
代码语言:javascript
复制
PageView.builder(
  controller: controller,
  itemBuilder: (context, position) {
    if (position == currentPageValue.floor()) {
    } else if (position == currentPageValue.floor() + 1) {
    
    } else {
    
    }
  },
  itemCount: 10,
)

现在,我们返回一样的页面,但是通过一个 Transform 小挂件来包裹,当我们滑动页面时实现页面效果。

代码语言:javascript
复制
PageView.builder(
  controller: controller,
  itemBuilder: (context, position) {
    if(position == currentPageValue.floor()) {
      return Transform(
        transform: Matrix4.identity()..rotateX(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blur : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        )
      );
    } else if (position == currentPageValue.floor() + 1) {
      return Transform(
        transfrom: Matrix4.identity..rotateX(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blue : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        ),
      );
    } else {
      return Container(
       color: position % 2 ? Colors.blue : Colors.pink,
       child: Center(
         child: Text(
           "Page",
           style: TextStyle(color: Colors.white, fontSize: 22.0),
         ),
       ),
      );
    }
  },
  itemCount: 10,
)

现在,我们更改从滑动来的页面到滑动到的页面。

currentPageValue.floor() 获取到左侧的页面,而 currentPageValue.floor() + 1 获取到右侧的页面。

在这个例子中,我们在 X 方向旋转页面,因为它通过 currentPageValue 减去 index 的弧度值进行滑动。我们可以通过乘于这个值放大这种效果。

我们可以调整此变换和变换的对齐方式,来获取多种类型的页面转产场效果。

Transition 2

和上面相似的代码结构,只是不同的转场效果。

代码语言:javascript
复制
PageView.builder(
  controller: controller,
  itemBuilder: (context, position) {
    if (position == currentPageValue.floor()) {
      return Transition(
        transform: Matrix4.identity()..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blue : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        ),
      );
    } else if (position == currentPageValue.floor() + 1) {
      return Transform(
        transform: Matrix4.identity()..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blue : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        ),
      );
    } else {
      return Container(
        color: position % 2 == 0 ? Colors.blue : Colors.pink,
        child: Center(
          child: Text(
            "Page",
            style: TextStyle(color: Colors.white, fontSize: 22.0),
          ),
        ),
      );
    }
  },
  itemCount: 10,
)

这里,我们 Y 轴和 Z 轴都进行旋转。

Transition 3

这个和上一个过渡类似,但是我们添加了 3-D 特效。

代码语言:javascript
复制
PageView.builder(
  controller: controller,
  itemBuilder: (context, position) {
    if(position == currentPageValue.floor()) {
      return Transform(
        transform: Matrix4.identity()..setEntry(3, 2, 0.004)..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blue : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        ),
      );
    } else if (postion == currentPageValue.floor() + 1) {
      return Transform(
        transform: Matrix4.identity()..setEntry(3, 2, 0.004)..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blue : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        ),
      ); 
    } else {
      return Container(
        color: position % 2 == 0 ? Colors.blue : Colors.pink,
        child: Center(
          child: Text(
            "Page",
            style: TextStyle(color: Colors.white, fontSize: 22.0),
          ),
        ),
      );
    }
  },
  itemCount: 10,
)

这行代码:

代码语言:javascript
复制
..setEntry(3, 2, 0.004)

给页面类似 3-D 的效果。

Transition 4

代码语言:javascript
复制
PageView.builder(
  controller: controller,
  itemBuilder: (context, position) {
    if(position == currentPageValue.floor()) {
      return Transform(
        alignment: Alignment.center,
        transform: Matrix4.identity()..setEntry(3, 2, 0.001)
          ..rotateX(currentPageValue - position)
          ..rotateY(currentPageValue - position)
          ..rotateZ(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blue : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        ),
      );
    } else if (postion == currentPageValue.floor() + 1) {
      return Transform(
        transform: Matrix4.identity()..setEntry(3, 2, 0.001)
          ..rotateX(currentPageValue - position)
          ..rotateY(currentPageValue - position)
          ..rotateZ(currentPageValue - position),
        child: Container(
          color: position % 2 == 0 ? Colors.blue : Colors.pink,
          child: Center(
            child: Text(
              "Page",
              style: TextStyle(color: Colors.white, fontSize: 22.0),
            ),
          ),
        ),
      ); 
    } else {
      return Container(
        color: position % 2 == 0 ? Colors.blue : Colors.pink,
        child: Center(
          child: Text(
            "Page",
            style: TextStyle(color: Colors.white, fontSize: 22.0),
          ),
        ),
      );
    }
  },
  itemCount: 10,
)

我们可以通过简单更改旋转的角度,坐标轴,对齐方式和平移来创建更多类型的过渡效果。

Demo App using PageView

Flutter 中,为了演示使用 PageView 来创建一个简单的应用,我创建了一个来学习 GRE 词汇的应用。这个应用使用了 SQLite 存储,为用户展示了单词并保存难懂的词汇。它也有单词发音的功能。

对应的仓库地址为 github.com/deven98/Flu…

官方位置👉 PageView class

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 探索 PageViews
    • PageView(默认构造器)
      • PageView.builder
      • Orientation
      • PageSnapping
      • ScrollPhysics
      • Controlling a PageView
      • 添加自定义过渡到 PageViews
      • Transform 1
        • 设置
        • Transition 2
        • Transition 3
        • Transition 4
        • Demo App using PageView
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档