Flutter 嵌套滚动 CustomScrollView 示例

CustomScrollView

来源: CustomScrollView 是可以使用Sliver来自定义滚动模型(效果)的组件。举个例子,假设有一个页面,顶部需要一个GridView,底部需要一个ListView,而要求整个页面的滑动效果是统一的,即它们看起来是一个整体。如果使用GridView+ListView来实现的话,就不能保证一致的滑动效果,而CustomScrollView 就可以实现这个效果。

在这里插入图片描述
实现源码请查看原文:CustomScrollView

简易版代码如下:

class CustomScrollViewTestRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Material(
      child: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
             //AppBar,包含一个导航栏
          ),
          SliverPadding(
            padding: const EdgeInsets.all(8.0),
            sliver: new SliverGrid(
            	 //Grid 内容
            ),
          ),
          new SliverFixedExtentList(
          		 // ListView 内容
          ),
        ],
      ),
    );
  }
}
添加头部

可以使用 SliverPersistentHeader 来实现,也可以直接在ListView顶部添加一个 Head来实现。

效果如下:
在这里插入图片描述
源码实现也很简单,这里直接在CustomScrollView里面嵌套两个SliverFixedExtentList,一个显示Head 头部,一个显示List 列表
在这里插入图片描述

参考:Flutter:Slivers大家族,让滑动视图的组合变得很简单!
使用 SliverPersistentHeader 来实现需要自定义继承SliverPersistentHeaderDelegate

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate{
  _SliverAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => math.max(maxHeight, minHeight);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

使用的时候直接添加到sliver里面即可

SliverPersistentHeader(
    pinned: false,
    floating: false,
    delegate: _SliverAppBarDelegate(
      minHeight: 60.0,
      maxHeight: 250.0,
      child: Container(
        color: Colors.blue,
        child: Center(
          child: Text('header',style: TextStyle(color: Colors.white),),
        )
      ),
    ),
  ),
视差滑动

视差滚动是指让多层背景以不同的速度移动,在形成立体滚动效果的同时,还能保证良好的视觉体验。

效果图如下:
在这里插入图片描述
要实现该效果主要用到了SliverAppBar组件。

做过Android 开发的都知道CollapsingToolbarLayout控件,它可以实现页面头部展开、合并的视差效果。在Flutter中是通过SliverAppBar组件实现类似的效果。

直接查看SliverAppBar组件支持的字段吧:

  const SliverAppBar({
    Key key,
    this.leading,// 左侧的widget
    this.automaticallyImplyLeading = true,
    this.title,//标题
    this.actions,//标题右侧的操作
    this.flexibleSpace,// 背景widget,如 FlexibleSpaceBar 可设置标题,背景图片,标题边距等
    this.bottom, // 底部区
    this.elevation,//阴影
    this.forceElevated = false,//是否显示阴影
    this.backgroundColor,//背景颜色
    this.brightness,//状态栏主题
    this.iconTheme,// 图标主题
    this.actionsIconTheme,//action图标主题
    this.textTheme,//文字主题
    this.primary = true,//是否显示在状态栏的下面,false就会占领状态栏的高度
    this.centerTitle,//标题居中显示
    this.titleSpacing = NavigationToolbar.kMiddleSpacing,//标题横向间距
    this.expandedHeight,//合并的高度,默认是状态栏的高度加AppBar的高度
    this.floating = false,//滑动时是否悬浮
    this.pinned = false,// 滑动时标题栏是否固定
    this.snap = false,// 滑动时标题栏跟随移动并固定在顶部, pinned 和 floating 效果的组合
    this.stretch = false,// 标题跟随滑动时拉伸,收缩
    this.stretchTriggerOffset = 100.0,// 标题跟随滑动时拉伸,收缩的偏移量
    this.onStretchTrigger,// 跟随滑动时拉伸,收缩的回调
    this.shape,// 阴影形状,elevation 大于0 才会显示
  })

在字段的后面都写明了相应的介绍,只需要在使用的时候设置相关的参数即可实现效果。

监听滑动
ScrollController

使用列表提供的controller字段,并调用监听方法监听滑动距离

_controller.addListener((){
      print('_controller offset : ${_controller.offset}');
    });
NotificationListener
  • 使用ScrollNotification控件去监听滚动列表。
 @override
  Widget build(BuildContext context) {
    return Material(
      child: NotificationListener<ScrollNotification>(
          onNotification: (scrollNotification) {
            //注册通知回调
            if (scrollNotification is ScrollStartNotification) {
              //滚动开始
              print('Scroll Start: ${scrollNotification.metrics.pixels}');
            }else if (scrollNotification is ScrollUpdateNotification) {
              //滚动位置更新
              print('Scroll Update: ${scrollNotification.metrics.pixels}');
            } else if (scrollNotification is ScrollEndNotification) {
              //滚动结束
              print('Scroll End: ${scrollNotification.metrics.pixels}');
            }
            return false;
          },
        child: CustomScrollView(
          controller: _controller,
          // 滑动列表 widget
        ),
      )
    );
  }
回到顶部功能

在新闻列表,或者列表数据很多的时候,我们往后翻好几页之后,突然想回到列表的顶部,这时候该如何实现呢?

在这里插入图片描述

  • 首先,在initState方法里,初始化 ScrollController
  • 随后,在视图构建方法 build 中,我们将 ScrollController对象与 滚动列表 controll关联,并在SliverAppBar添加一个按钮用于点击后调用_controller.animateTo 方法返回列表顶部。
  • 最后,在 dispose方法中,我们对 ScrollController 进行了资源释放。

实现源码如下:

class _CustomScrollViewPageState extends State<CustomScrollViewPage> {
  //滑动控制器
  ScrollController _controller;

  @override
  void initState() {
    //初始化控制器
    _controller = ScrollController();
    super.initState();
  }
  @override
  void dispose() {
    //销毁控制器
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Material(
      child: CustomScrollView(
        controller: _controller,
        slivers: <Widget>[
          //AppBar,包含一个导航栏
          SliverAppBar(
            pinned: true,
            expandedHeight: 250.0,
            actions: <Widget>[
              RaisedButton(
                child: Text('返回顶部'),
                onPressed: (){
                  _controller.animateTo(.0, duration: Duration(milliseconds: 200), curve: Curves.ease);
                },
              )
            ],
            flexibleSpace:  FlexibleSpaceBar(
              title: const Text('CustomScrollView'),
              background: Image.network(
                "https://ssyerv1.oss-cn-hangzhou.aliyuncs.com/picture/389e31d03d36465d8acd9939784df6f0.jpg!sswm", fit: BoxFit.cover,),
            ),
          ),
            //List
            new SliverFixedExtentList(
              itemExtent: 50.0,
              delegate: new SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                    //创建列表项
                    return new Container(
                      alignment: Alignment.center,
                      color: Colors.lightBlue[100 * (index % 9)],
                      child: new Text('list item $index'),
                    );
                  },
                  childCount: 50 //50个列表项
              ),
            ),
          ],
        ),
    );
  }
}

完~

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Flutter嵌套滑动通常是指在一个界面中有两个或以上的滑动手势区域可以同时进行滚动操作。这通常发生在 List 或 Grid 等布局中,比如长列表下方有一个侧边栏导航。在 Flutter 中,我们可以利用 `SliverList` 和 `SliverGrid` 结合 `ScrollableWidget`(如 `NestedScrollView`、`CustomScrollView`)来实现这样的效果。 `NestedScrollView` 提供了一个易于管理的容器,它允许内部的 `Slivers`(如 `SliverChildListDelegate` 或 `SliverGridDelegate`) 和外部的滚动视图相互作用。你可以设置一个 `NestedScrollView` 的 primary 直接滚动,而 secondary 则用于侧面滚动。 下面是一个简单的示例: ```dart NestedScrollView( axis: Axis.vertical, // 主轴滚动 headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return <Widget>[ SliverOverlapAbsorber( handle: _handle, child: SliverAppBar( title: Text('标题'), actions: [], ), ), ]; }, body: Column( children: [ SliverToBoxAdapter( child: Container(height: 500), // 长列表或内容区域 ), SliverPadding( padding: EdgeInsets.all(8), sliver: SliverList( delegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, // 水平网格列数 mainAxisSpacing: 16, crossAxisSpacing: 16, ), children: List.generate(10, (index) => Card()), // 导航卡片 ), ), ], ), ); ``` 在这个例子中,`_handle` 是 `NestedScrollView` 的内部滚动范围,当用户滚动主列表时,它会自动调整侧边栏的可见区域。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_龙衣

赏杯快乐水喝喝

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值