Flutter 可滚动组件:ScrollController

前言

可以用ScrollController来控制可滚动组件的滚动位置。

接口描述

ScrollController({
    // 初始滚动位置
    double initialScrollOffset = 0.0,
    // 是否保持滚动位置
    this.keepScrollOffset = true,
    this.debugLabel,
  })

代码示例

// ScrollController

// 可以用ScrollController来控制可滚动组件的滚动位置。

import 'package:flutter/material.dart';

class ScrollControllerTest extends StatefulWidget{
  @override
  ScrollControllerTestState createState() => ScrollControllerTestState();
}

class ScrollControllerTestState extends State<ScrollControllerTest>{
  ScrollController _controller = ScrollController();
  // 是否显示“返回到顶部”按钮
  bool showToTopBtn = false;

  @override
  void 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();
  }

  Widget build(BuildContext context){
    return Scaffold(
      appBar: AppBar(
        title: Text('滚动控制1'),
      ),
      body: Scrollbar(
        child: ListView.builder(
          itemCount: 100,
          itemExtent: 50.0,
          controller: _controller,
          itemBuilder: (context, index){
            return ListTile(title: Text("$index"),);
          },
        ),
      ),
      floatingActionButton: !showToTopBtn ? null : FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: (){
          // 返回到顶部时执行动画
          _controller.animateTo(
            .0,
            // 返回顶部的过程中执行一个滚动动画,动画时间是200毫秒,动画曲线是Curves.ease
            duration: Duration(milliseconds: 200),
            curve: Curves.ease,
          );
        },
      ),
    );
  }

}

// 滚动监听
class ScrollNotificationTest extends StatefulWidget{
  @override
  _ScrollNotificationTestState createState() => _ScrollNotificationTestState();
}

class _ScrollNotificationTestState extends State<ScrollNotificationTest>{
  //保持进度百分比
  String _progress = '0%';

  Widget build(BuildContext context){
    return Scaffold(
      appBar: AppBar(
        title: Text('滚动控制2'),
      ),
      // 进度条
      body: Scrollbar(
        // 监听滚动通知
        child: NotificationListener<ScrollNotification>(
          onNotification: (ScrollNotification notification){
            // pixels:当前滚动位置;maxScrollExtent:最大可滚动长度
            double progress = notification.metrics.pixels /
                notification.metrics.maxScrollExtent;
            // 重新构建
            setState(() {
              _progress = '${(progress * 100).toInt()}%';
            });
            // extentBefore:滑出ViewPort顶部的长度;此示例中相当于顶部滑出屏幕上方的列表长度。
            // extentInside:ViewPort内部长度;此示例中屏幕显示的列表部分的长度。
            // extentAfter:列表中未滑入ViewPort部分的长度;此示例中列表底部未显示到屏幕范围部分的长度。
            // atEdge:是否滑到了可滚动组件的边界(此示例中相当于列表顶或底部)。
            print('BottomEdge: ${notification.metrics.extentAfter == 0}');
            //
            return true;
          },
          child: Stack(
            alignment: Alignment.center,
            children: <Widget>[
              ListView.builder(
                itemCount: 100,
                itemExtent: 50.0,
                itemBuilder: (context, index){
                  return ListTile(title: Text('$index'),);
                },
              ),
              // 显示百分比
              CircleAvatar(
                radius: 30.0,
                child: Text(_progress),
                backgroundColor: Colors.black54,
              )
            ],
          ),
        ),
      ),
    );
  }
}

总结

滚动位置恢复

PageStorage是一个用于保存页面(路由)相关数据的组件,它并不会影响子树的UI外观,其实,PageStorage是一个功能型组件,它拥有一个存储桶(bucket),子树中的Widget可以通过指定不同的PageStorageKey来存储各自的数据或状态。
每次滚动结束,可滚动组件都会将滚动位置offset存储到PageStorage中,当可滚动组件重新创建时再恢复。如果ScrollController.keepScrollOffset为false,则滚动位置将不会被存储,可滚动组件重新创建时会使用ScrollController.initialScrollOffset;ScrollController.keepScrollOffset为true时,可滚动组件在第一次创建时,会滚动到initialScrollOffset处,因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置,而initialScrollOffset会被忽略。
当一个路由中包含多个可滚动组件时,如果你发现在进行一些跳转或切换操作后,滚动位置不能正确恢复,这时你可以通过显式指定PageStorageKey来分别跟踪不同的可滚动组件的位置。

ScrollPosition

ScrollPosition是用来保存可滚动组件的滚动位置的。一个ScrollController对象可以同时被多个可滚动组件使用,ScrollController会为每一个可滚动组件创建一个ScrollPosition对象,这些ScrollPosition保存在ScrollController的positions属性中(List)。ScrollPosition是真正保存滑动位置信息的对象,offset只是一个便捷属性。
一个ScrollController虽然可以对应多个可滚动组件,但是有一些操作,如读取滚动位置offset,则需要一对一!但是我们仍然可以在一对多的情况下,通过其它方法读取滚动位置。ScrollPosition有两个常用方法:animateTo() 和 jumpTo(),它们是真正来控制跳转滚动位置的方法,ScrollController的这两个同名方法,内部最终都会调用ScrollPosition的。

ScrollController控制原理

ScrollController的另外三个方法:

ScrollPosition createScrollPosition(
    ScrollPhysics physics,
    ScrollContext context,
    ScrollPosition oldPosition);
void attach(ScrollPosition position) ;
void detach(ScrollPosition position) ;

当ScrollController和可滚动组件关联时,可滚动组件首先会调用ScrollController的createScrollPosition()方法来创建一个ScrollPosition来存储滚动位置信息,接着,可滚动组件会调用attach()方法,将创建的ScrollPosition添加到ScrollController的positions属性中,这一步称为“注册位置”,只有注册后animateTo() 和 jumpTo()才可以被调用。
当可滚动组件销毁时,会调用ScrollController的detach()方法,将其ScrollPosition对象从ScrollController的positions属性中移除,这一步称为“注销位置”,注销后animateTo() 和 jumpTo() 将不能再被调用。
需要注意的是,ScrollController的animateTo() 和 jumpTo()内部会调用所有ScrollPosition的animateTo() 和 jumpTo(),以实现所有和该ScrollController关联的可滚动组件都滚动到指定的位置。

滚动监听

Flutter Widget树中子Widget可以通过发送通知(Notification)与父(包括祖先)Widget通信。父级组件可以通过NotificationListener组件来监听自己关注的通知,这种通信方式类似于Web开发中浏览器的事件冒泡。
可滚动组件在滚动时会发送ScrollNotification类型的通知,ScrollBar正是通过监听滚动通知来实现的。通过NotificationListener监听滚动事件和通过ScrollController有两个主要的不同:

  1. 通过NotificationListener可以在从可滚动组件到widget树根之间任意位置都能监听。而ScrollController只能和具体的可滚动组件关联后才可以。
  2. 收到滚动事件后获得的信息不同;NotificationListener在收到滚动事件时,通知中会携带当前滚动位置和ViewPort的一些信息,而ScrollController只能获取当前滚动位置。

原文地址:https://www.cnblogs.com/parzulpan/p/12195948.html

时间: 2024-11-01 22:36:33

Flutter 可滚动组件:ScrollController的相关文章

Flutter——TabBar组件(顶部Tab切换组件)

TabBar组件的常用属性: 属性 描述 tabs 显示的标签内容,一般使用 Tab 对象,也可以是其他的Widget  controller TabController 对象 isScrollable 是否可滚动 indicatorColor 指示器颜色 indicatorWeight 指示器高度 indicatorPadding 底部指示器的 Padding indicator 指示器 decoration,例如边框等 indicatorSize 指示器大小计算方式,TabBarIndica

flutter 输入框组件封装

一.组件分析 ui如下 根据UI分析我们需要提取哪些是动态的,可以通过传递参数得到不同的结果? 1.左侧icon 2.输入的文本 3.是否是密码框 4.输入框的控制器:如何控制输入框的回调函数 二.快速创建自定义组件 vscode中使用快捷键stl快速生成一个无状态组件, class CreateMyInput extends StatelessWidget { @override Widget build(BuildContext context) { return Container( );

flutter 图片组件

加入图片的几种方式 Image.asset:加载资源图片,就是加载项目资源目录中的图片,加入图片后会增大打包的包体体积,用的是相对路径. Image.network:网络资源图片,意思就是你需要加入一段http://xxxx.xxx的这样的网络路径地址. Image.file:加载本地图片,就是加载本地文件中的图片,这个是一个绝对路径,跟包体无关. Image.memory: 加载Uint8List资源图片,不怎么使用 Image 组件的常用属性: fit属性的设置 fit属性可以控制图片的拉伸

flutter常用组件

TextWidget  文本组件 TextWidget  的常用属性 TextAlign: 下面的五种对齐方式 左对齐, 右对齐, 居中对齐, 从开始位置对齐 MaxLines: 显示几行 overflow: 超出范围截取后的样式 clip 直接阶段, ellipsis 后面省略号... fade 超出后渐变, decoration : underline 下划线 linethrough 中间穿过 none 不显示 overline 上划线 decorationStye: 下划线样式 dashe

Flutter——Expanded组件(&quot;可伸缩&quot;组件)

Expanded组件可以结合Row和Column布局组件使用. Expanded组件的常用属性 属性 说明 flex 元素占整个父Row/Column的比例 child 子元素 import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( title: "ExpandedWidget", home: MyApp(), )); } class MyApp extends StatelessWidget

Flutter——AspectRatio组件

AspectRatio 的作用是根据设置调整子元素 child 的宽高比. AspectRatio 首先会在布局限制条件允许的范围内尽可能的扩展,widget 的高度是由宽度和比率决定的,类似于 BoxFit 中的 contain,按照固定比率去尽量占满区域. 如果在满足所有限制条件过后无法找到一个可行的尺寸,AspectRatio 最终将会去优先适应布局限制条件,而忽略所设置的比率. AspectRatio组件常用的属性: 属性 说明 aspectRatio 宽高比,最终可能不会根据这个值去布

Flutter——AppBar组件(顶部导航组件)

AppBar组件的常用属性如下: 属性 描述 leading 在标题前面显示的一个控件,在首页通常显示应用的 logo:在其他界面通常显示为返回按钮 title 标题,通常显示为当前界面的标题文字,可以放组件 actions 通常使用 IconButton 来表示,可以放按钮组 bottom 通常放 tabBar,标题下面显示一个 Tab 导航栏 backgroundColor 导航背景颜色 iconTheme 图标样式 textTheme 文字样式 centerTitle 标题是否居中显示 i

Flutter 基础组件:Widget简介

概念 在Flutter中几乎所有的对象都是一个Widget.与原生开发中"控件"不同的是,Flutter中的Widget的概念更广泛,它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector widget.用于APP主题数据传递的Theme等等,而原生开发中的控件通常只是指UI元素. Widget与Element 在Flutter中,Widget的功能是"描述一个UI元素的配置数据",它就是说,Widget其实并不是表示最

Flutter 基础组件:文本、字体样式

// 文本.字体样式 import 'package:flutter/material.dart'; class TextFontStyle extends StatelessWidget { // 声明文本样式 TextStyle textStyle = const TextStyle(fontFamily: 'MyFont', fontSize: 30, ); @override Widget build(BuildContext context) { return Scaffold( ap