当前位置: 代码网 > it编程>App开发>苹果IOS > Flutter ScrollController滚动监听及控制示例详解

Flutter ScrollController滚动监听及控制示例详解

2024年05月18日 苹果IOS 我要评论
scrollcontrollerscrollcontroller构造函数如下:scrollcontroller({ double initialscrolloffset = 0.0, //初始滚动位

scrollcontroller

scrollcontroller构造函数如下:

scrollcontroller({
  double initialscrolloffset = 0.0, //初始滚动位置
  this.keepscrolloffset = true,//是否保存滚动位置
  ...
})

我们介绍一下scrollcontroller常用的属性和方法:

  • offset:可滚动组件当前的滚动位置。
  • jumpto(double offset)、animateto(double offset,...):这两个方法用于跳转到指定的位置,它们不同之处在于,后者在跳转时会执行一个动画,而前者不会。

scrollcontroller还有一些属性和方法,我们将在后面原理部分解释。

滚动监听

scrollcontroller间接继承自listenable,我们可以根据scrollcontroller来监听滚动事件,如:

controller.addlistener(()=>print(controller.offset))

滚动监听示例

我们创建一个listview,当滚动位置发生变化时,我们先打印出当前滚动位置,然后判断当前位置是否超过1000像素,如果超过则在屏幕右下角显示一个“返回顶部”的按钮,该按钮点击后可以使listview恢复到初始位置;如果没有超过1000像素,则隐藏“返回顶部”按钮。代码如下:

import 'package:demo202112/utils/common_appbar.dart';
import 'package:flutter/material.dart';
/// @author wywinstonwy
/// @date 2022/1/19 10:46 下午
/// @description:
class myscrollcontroller extends statefulwidget {
  const myscrollcontroller({key? key}) : super(key: key);
  @override
  _myscrollcontrollerstate createstate() => _myscrollcontrollerstate();
}
class _myscrollcontrollerstate extends state<myscrollcontroller> {
  final scrollcontroller _controller = scrollcontroller();
  bool showtotopbtn = false; //是否显示“返回到顶部”按钮
  @override
  void initstate() {
    // todo: implement initstate
    super.initstate();
    //监听滚动事件,打印滚动位置
    _controller.addlistener(() {
      //打印滚动位置
      print(_controller.offset);
      if (_controller.offset < 1000 && showtotopbtn) {
        setstate(() {
          showtotopbtn = false;
        });
      } else if (_controller.offset >= 1000 && showtotopbtn == false) {
        setstate(() {
          showtotopbtn = true;
        });
      }
    });
  }
  @override
  void dispose() {
    //为了避免内存泄露,需要调用_controller.dispose
    _controller.dispose();
    super.dispose();
  }
  @override
  widget build(buildcontext context) {
    return scaffold(
      appbar: getappbar('滚动监听以及控制'),
      body: _buildscollbar(),
      floatingactionbutton: showtotopbtn==false?null:floatingactionbutton(
        onpressed: (){
          //返回到顶部时候执行动画
          _controller.animateto(0, duration: const duration(milliseconds: 200), curve: curves.easein);
        },
        child: const icon(icons.arrow_upward),),
    );
  }
  _buildscollbar(){
    return scrollbar(
        child: listview.builder(
          controller: _controller,
            itemcount: 100,
            itemextent: 44,
            itembuilder: (context,index){
          return listtile(title: text('$index'),);
        })
    );
  }
}

运行效果:

由于列表项高度为 44 像素,当滑动到第 20+ 个列表项后,右下角 “返回顶部” 按钮会显示,点击该按钮,listview 会在返回顶部的过程中执行一个滚动动画,动画时间是 200 毫秒,动画曲线是 curves.ease

滚动位置恢复

pagestorage是一个用于保存页面(路由)相关数据的组件,它并不会影响子树的ui外观,其实,pagestorage是一个功能型组件,它拥有一个存储桶(bucket),子树中的widget可以通过指定不同的pagestoragekey来存储各自的数据或状态。

每次滚动结束,可滚动组件都会将滚动位置offset存储到pagestorage中,当可滚动组件重新创建时再恢复。如果scrollcontroller.keepscrolloffset为false,则滚动位置将不会被存储,可滚动组件重新创建时会使用scrollcontroller.initialscrolloffsetscrollcontroller.keepscrolloffset为true时,可滚动组件在第一次创建时,会滚动到initialscrolloffset处,因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置,而initialscrolloffset会被忽略。

当一个路由中包含多个可滚动组件时,如果你发现在进行一些跳转或切换操作后,滚动位置不能正确恢复,这时你可以通过显式指定pagestoragekey来分别跟踪不同的可滚动组件的位置,如:

listview(key: pagestoragekey(1), ... );
...
listview(key: pagestoragekey(2), ... );

不同的pagestoragekey,需要不同的值,这样才可以为不同可滚动组件保存其滚动位置。

注意:一个路由中包含多个可滚动组件时,如果要分别跟踪它们的滚动位置,并非一定就得给他们分别提供pagestoragekey。这是因为scrollable本身是一个statefulwidget,它的状态中也会保存当前滚动位置,所以,只要可滚动组件本身没有被从树上detach掉,那么其state就不会销毁(dispose),滚动位置就不会丢失。只有当widget发生结构变化,导致可滚动组件的state销毁或重新构建时才会丢失状态,这种情况就需要显式指定pagestoragekey,通过pagestorage来存储滚动位置,一个典型的场景是在使用tabbarview时,在tab发生切换时,tab页中的可滚动组件的state就会销毁,这时如果想恢复滚动位置就需要指定

scrollposition

scrollposition是用来保存可滚动组件的滚动位置的。一个scrollcontroller对象可以同时被多个可滚动组件使用,scrollcontroller会为每一个可滚动组件创建一个scrollposition对象,这些scrollposition保存在scrollcontrollerpositions属性中(list<scrollposition>)。scrollposition是真正保存滑动位置信息的对象,offset只是一个便捷属性:

double get offset => position.pixels;

一个scrollcontroller虽然可以对应多个可滚动组件,但是有一些操作,如读取滚动位置offset,则需要一对一!但是我们仍然可以在一对多的情况下,通过其它方法读取滚动位置,举个例子,假设一个scrollcontroller同时被两个可滚动组件使用,那么我们可以通过如下方式分别读取他们的滚动位置:

...
controller.positions.elementat(0).pixels
controller.positions.elementat(1).pixels
...    

我们可以通过controller.positions.length来确定controller被几个可滚动组件使用。

scrollposition的方法

scrollposition有两个常用方法:animateto()jumpto(),它们是真正来控制跳转滚动位置的方法,scrollcontroller的这两个同名方法,内部最终都会调用scrollposition的。

scrollcontroller控制原理

我们来介绍一下scrollcontroller的另外三个方法:

scrollposition createscrollposition(
    scrollphysics physics,
    scrollcontext context,
    scrollposition oldposition);
void attach(scrollposition position) ;
void detach(scrollposition position) ;

scrollcontroller和可滚动组件关联时,可滚动组件首先会调用scrollcontrollercreatescrollposition()方法来创建一个scrollposition来存储滚动位置信息,接着,可滚动组件会调用attach()方法,将创建的scrollposition添加到scrollcontrollerpositions属性中,这一步称为“注册位置”,只有注册后animateto()jumpto()才可以被调用。

当可滚动组件销毁时,会调用scrollcontrollerdetach()方法,将其scrollposition对象从scrollcontrollerpositions属性中移除,这一步称为“注销位置”,注销后animateto()jumpto() 将不能再被调用。

需要注意的是,scrollcontrolleranimateto()jumpto()内部会调用所有scrollpositionanimateto()jumpto(),以实现所有和该scrollcontroller关联的可滚动组件都滚动到指定的位置。

滚动监听

下面,我们监听listview的滚动通知,然后显示当前滚动进度百分比:

import 'package:demo202112/utils/common_appbar.dart';
import 'package:flutter/material.dart';
/// @author wywinstonwy
/// @date 2022/1/19 11:21 下午
/// @description: 
class myscrollcontroller2 extends statefulwidget {
  const myscrollcontroller2({key? key}) : super(key: key);
  @override
  _myscrollcontroller2state createstate() => _myscrollcontroller2state();
}
class _myscrollcontroller2state extends state<myscrollcontroller2> {
  string _progress ='0%';
  @override
  widget build(buildcontext context) {
    return scaffold(
      appbar: getappbar("滚动监听"),
      body: notificationlistener<scrollnotification>(
        onnotification: (scrollnotification notification){
          double progress = notification.metrics.pixels/notification.metrics.maxscrollextent;
          //重新构建
          setstate(() {
            _progress ='${(progress*100).toint()}%';
          });
          print("bottomedge: ${notification.metrics.extentafter == 0}");
          return false;          //return true; //放开此行注释后,进度条将失效
        },
        child: stack(
          alignment: alignment.center,
          children: [
            listview.builder(
              itemcount: 100,
                itemextent: 50,
                itembuilder: (context,index){
                return listtile(title: text('$index'),);
                }),
            circleavatar(
              radius: 30,
              child: text(_progress),
              backgroundcolor: colors.black54,
            )
          ],
        ),
      ),
    );
  }
}

运行结果:

在接收到滚动事件时,参数类型为scrollnotification,它包括一个metrics属性,它的类型是scrollmetrics,该属性包含当前viewport及滚动位置等信息:

  • pixels:当前滚动位置。
  • maxscrollextent:最大可滚动长度。
  • extentbefore:滑出viewport顶部的长度;此示例中相当于顶部滑出屏幕上方的列表长度。
  • extentinsideviewport内部长度;此示例中屏幕显示的列表部分的长度。
  • extentafter:列表中未滑入viewport部分的长度;此示例中列表底部未显示到屏幕范围部分的长度。
  • atedge:是否滑到了可滚动组件的边界(此示例中相当于列表顶或底部)。

scrollmetrics还有一些其它属性,可以自行查阅api文档。

详细的官方文档地址:api.flutter.dev/flutter/wid…

官方文档解释 控制可滚动小部件。

滚动控制器通常作为成员变量存储在state对象中,并在每个state.build中重用。单个滚动控制器可用于控制多个可滚动小部件,但有些操作(如读取滚动偏移量)要求控制器与单个可滚动小部件一起使用。

滚动控制器创建一个scrollposition来管理特定于单个可滚动小部件的状态。要使用自定义的scrollposition,子类化scrollcontroller并重写createscrollposition。

scrollcontroller是一个listenable。当附加的任何scrollposition通知它们的侦听器时(即当它们中的任何一个滚动时),它会通知它的侦听器。当附加的scrollposition列表发生变化时,它不会通知侦听器。

通常与listview, gridview, customscrollview一起使用。

参见: listview, gridview, customscrollview,它们可以由scrollcontroller控制。 scrollable,它是较低层的小部件,用于创建scrollposition对象和scrollcontroller对象并将它们关联起来。 pagecontroller,它是控制pageview的一个类似对象。 scrollposition,用于管理单个滚动小部件的滚动偏移量。 scrollnotification和notificationlistener,它们可用于监视滚动位置,而无需使用scrollcontroller。

以上就是flutter scrollcontroller滚动监听及控制示例详解的详细内容,更多关于flutter scrollcontroller滚动监听的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com