首页 > 其他分享 >Flutter可滚动组件(5):PageView与页面缓存

Flutter可滚动组件(5):PageView与页面缓存

时间:2024-10-11 16:14:28浏览次数:1  
标签:PageView 缓存 flutter build cacheExtent Flutter 页面

一、PageView

如果要实现页面切换和 Tab 布局,我们可以使用 PageView 组件。需要注意,PageView 是一个非常重要的组件,因为在移动端开发中很常用,比如大多数 App 都包含 Tab 换页效果、图片轮动以及抖音上下滑页切换视频功能等等,这些都可以通过 PageView 轻松实现。

PageView({
  Key? key,
  this.scrollDirection = Axis.horizontal, // 滑动方向
  this.reverse = false,
  PageController? controller,
  this.physics,
  List<Widget> children = const <Widget>[],
  this.onPageChanged,
  
  // 每次滑动是否强制切换整个页面,如果为false,则会根据实际的滑动距离显示页面
  this.pageSnapping = true,
  // 主要是配合辅助功能用的,后面解释
  this.allowImplicitScrolling = false,
  // 后面解释
  this.padEnds = true,
})

我们看一个 Tab 切换的实例,为了突出重点,我们让每个 Tab 页都只显示一个数字。

// Tab 页面 
class Page extends StatefulWidget {
  const Page({
    Key? key,
    required this.text
  }) : super(key: key);

  final String text;

  @override
  _PageState createState() => _PageState();
}

class _PageState extends State<Page> {
  @override
  Widget build(BuildContext context) {
    print("build ${widget.text}");
    return Center(child: Text("${widget.text}", textScaleFactor: 5));
  }
}

我们创建一个 PageView:

@override
Widget build(BuildContext context) {
  var children = <Widget>[];
  // 生成 6 个 Tab 页
  for (int i = 0; i < 6; ++i) {
    children.add( Page( text: '$i'));
  }

  return PageView(
    // scrollDirection: Axis.vertical, // 滑动方向为垂直方向
    children: children,
  );
}

运行后就可以左右滑动来切换页面了,效果如下图所示:

图6-16


如果将 PageView 的滑动方向指定为垂直方向(上面代码中注释部分),则会变为上下滑动切换页面。


二、页面缓存

我们在运行上面示例时,读者可能已经发现:每当页面切换时都会触发新 Page 页的 build,比如我们从第一页滑到第二页,然后再滑回第一页时,控制台打印如下:

flutter: build 0
flutter: build 1
flutter: build 0

可见 PageView 默认并没有缓存功能,一旦页面滑出屏幕它就会被销毁,这和我们前面讲过的 ListView/GridView 不一样,在创建 ListView/GridView 时我们可以手动指定 ViewPort 之外多大范围内的组件需要预渲染和缓存(通过 cacheExtent 指定),只有当组件滑出屏幕后又滑出预渲染区域,组件才会被销毁,但是不幸的是 PageView 并没有 cacheExtent 参数!

但是在真实的业务场景中,对页面进行缓存是很常见的一个需求,比如一个新闻 App,下面有很多频道页,如果不支持页面缓存,则一旦滑到新的频道旧的频道页就会销毁,滑回去时又得重新请求数据和构建页面,这谁扛得住!

按道理 cacheExtent 是 Viewport 的一个配置属性,且 PageView 也是要构建 Viewport 的,那么为什么就不能透传一下这个参数呢?于是笔者带着这个疑问看了一下 PageView 的源码,发现在 PageView 创建Viewport 的代码中是这样的:

child: Scrollable(
  ...
  viewportBuilder: (BuildContext context, ViewportOffset position) {
    return Viewport(
      // TODO(dnfield): we should provide a way to set cacheExtent
      // independent of implicit scrolling:
      // https://github.com/flutter/flutter/issues/45632
      cacheExtent: widget.allowImplicitScrolling ? 1.0 : 0.0,
      cacheExtentStyle: CacheExtentStyle.viewport,
      ...
    );
  },
)

我们发现 虽然 PageView 没有透传 cacheExtent,但是却在allowImplicitScrolling 为 true 时设置了预渲染区域,注意,此时的缓存类型为 CacheExtentStyle.viewport,则 cacheExtent 则表示缓存的长度是几个 Viewport 的宽度,cacheExtent 为 1.0,则代表前后各缓存一个页面宽度,即前后各一页。既然如此,那我们将 PageView 的 allowImplicitScrolling 置为 true 则不就可以缓存前后两页了?我们修改代码,然后运行示例,发现在第一页时,控制台打印信息如下:

flutter: build 0
flutter: build 1 // 预渲染第二页

滑到第二页时:

flutter: build 0
flutter: build 1
flutter: build 2 // 预渲染第三页

当再滑回第一页时,控制台信息不变,这也就意味着第一页缓存成功,它没有被重新构建。但是如果我们从第二页滑到第三页,然后再滑回第一页时,控制台又会输出 ”build 0“,这也符合预期,因为我们之前分析的就是设置 allowImplicitScrolling 置为 true 时就只会缓存前后各一页,所以滑到第三页时,第一页就会销毁。

OK,能缓存前后各一页也貌似比不能缓存好一点,但还是不能彻底解决不了我们的问题。为什么明明就是顺手的事, flutter 就不让开发者指定缓存策略呢?然后我们翻译一下源码中的注释:

Todo:我们应该提供一种独立于隐式滚动(implicit scrolling)的设置 cacheExtent 的机制。

放开 cacheExtent 透传是很简单的事情,为什么还要以后再做?是有什么难题么?要理解这个我们就需要看看 allowImplicitScrolling 到底是什么了,根据文档以及注释中 issue 的链接,发现PageView 中设置 cacheExtent 会和 iOS 中 辅助功能有冲突(读者可以先不用关注),所以暂时还没有什么好的办法。看到这可能国内的很多开发者要说我们的 App 不用考虑辅助功能,既然如此,那问题很好解决,将 PageView 的源码拷贝一份,然后透传 cacheExtent 即可。

拷源码的方式虽然很简单,但毕竟不是正统做法,那有没有更通用的方法吗?有!还记得我们前面说过“可滚动组件提供了一种通用的缓存子项的解决方案” 吗,我们将在下一节重点介绍。


标签:PageView,缓存,flutter,build,cacheExtent,Flutter,页面
From: https://www.cnblogs.com/linuxAndMcu/p/18458647

相关文章

  • Flutter可滚动组件(4):GridView
    网格布局是一种常见的布局类型,GridView组件正是实现了网格布局的组件,下面重点介绍一下它的用法。一、默认构造函数GridView可以构建一个二维网格列表,其默认构造函数定义如下:GridView({Key?key,AxisscrollDirection=Axis.vertical,boolreverse=false,......
  • Flutter可滚动组件(3):滚动监听及控制
    前一篇博客介绍了Flutter中常用的可滚动组件,也说过可以用ScrollController来控制可滚动组件的滚动位置,本节先介绍一下ScrollController,然后以ListView为例,展示一下ScrollController的具体用法。最后,再介绍一下路由切换时如何来保存滚动位置。一、ScrollControllerScrollContro......
  • Flutter可滚动组件(8):CustomScrollView 和 Slivers
    CustomScrollView前面介绍的ListView、GridView、PageView都是一个完整的可滚动组件,所谓完整是指它们都包括Scrollable、Viewport和Sliver。假如我们想要在一个页面中,同时包含多个可滚动组件,且使它们的滑动效果能统一起来,比如:我们想将已有的两个沿垂直方向滚动的ListView......
  • Flutter可滚动组件(7):TabBarView
    TabBarView是Material组件库中提供了Tab布局组件,通常和TabBar配合使用。一、TabBarViewTabBarView封装了PageView,它的构造方法很简单TabBarView({Key?key,requiredthis.children,//tab页this.controller,//TabControllerthis.physics,this.dra......
  • Flutter可滚动组件(6):可滚动组件子项缓存
    本节将介绍可滚动组件中缓存指定子项的通用方案。首先回想一下,在介绍ListView时,有一个addAutomaticKeepAlives属性我们并没有介绍,如果addAutomaticKeepAlives为true,则ListView会为每一个列表项添加一个AutomaticKeepAlive父组件。虽然PageView的默认构造函数和PageVi......
  • Flutter功能性组件(1):对话框
    Material库提供了三种基本对话框组件AlertDialog通常用于提示型对话框SimpleDialog通常用于列表型对话框Dialog通常用于自定义布局元素的对话框弹出对话框时,调用showDialog函数,将对话框控件传入,由于对话框本身是路由,所以关闭对话框时,需使用Navigator.of(context).pop......
  • Flutter可滚动组件(9):嵌套可滚动组件 NestedScrollView
    一、NestedScrollView上一节中,我们知道CustomScrollView只能组合Sliver,如果有孩子也是一个可滚动组件(通过SliverToBoxAdapter嵌入)且它们的滑动方向一致时便不能正常工作。为了解决这个问题,Flutter中提供了一个NestedScrollView组件,它的功能是组合(协调)两个可滚动组件,下面我......
  • Flutter功能性组件(2):弹出框
    一、showModalBottomSheet(模态底部弹出框)showModalBottomSheet用于显示一个模态底部弹出框。属性解析:Future<T?>showModalBottomSheet<T>({requiredBuildContextcontext,//表示底部弹出框所处的上下文,通常来自当前widget。requiredWidgetBuilderbuilder,//用......
  • Flutter基础组件(7):进度条
    在Flutter应用开发中,无论是处理网络请求,执行耗时任务,或是等待用户响应,我们总是需要在界面上显示进度条或者等待指示器。在这篇博客中,我们将介绍Flutter中两种常用的进度指示器:LinearProgressIndicator和CircularProgressIndicator。我们将比较它们的异同点,以及如何使用和自......
  • Flutter基础组件(6):单选按钮、复选框、单选开关
    在移动应用开发中,单选和复选是常见的用户交互模式,用于选择一个或多个选项。Flutter提供了一些内置的组件和机制,方便我们实现单选和复选功能。本文将介绍Flutter中的单选按钮(RadioButton)和复选框(Checkbox)的使用方法和示例。一、单选按钮(RadioButton)单选按钮是一种用户界面组件......