常见的滚动widget

7 篇文章 0 订阅
4 篇文章 0 订阅

这个转自我自己的有道云 想看图片去那里

文档:Day3_11 常见的滚动的Widget.md
链接:http://note.youdao.com/noteshare?id=a72c965bf9e1194b2d4873c64479f408&sub=25B1E0477B0B4C3CAF8384F640C2EC2B

常见的滚动的Widget

一. 关于上节课的StackWidget使用

  • 如何将按钮点击变色
  1. 改变成StatefulWidget
  2. 设置一个变量
  3. 在IconButton改变其值
  4. setState(() {})
  5. 将color: _isFlage : Colors.red ? Colors.white;
class HYHomeContent extends StatefulWidget {
  @override
  _HYHomeContentState createState() => _HYHomeContentState();
}

class _HYHomeContentState extends State<HYHomeContent> {
//  但是我们不能用
  bool _isFavite = false;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Image.asset("assets/images/test.jpg", width: double.infinity, fit: BoxFit.cover),
        Positioned(
          left: 0,
          right: 0,
          bottom: 0,
          child: Container(
            padding: EdgeInsets.symmetric(horizontal: 8),
            width: double.infinity,
            color: Color.fromARGB(100, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text("test", style: TextStyle(fontSize: 20, color: Colors.white)),
                IconButton(
                  icon: Icon(Icons.favorite),
                  color: _isFavite ? Colors.red : Colors.white,
                  onPressed: () {
                    _isFavite = !_isFavite;
                    setState(() {});
                  },
                )

              ],
            ),
          ),
        )
      ],
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jcIVldq9-1584345888525)(D801F96FBB4D4F3095D90BFA960F6764)]

所以这个东西的最关键的是 设置一个变量去记录这个状态

  • 但是这里有另外一个问题 如果我们有个很多个独立的变量需要去 记录的话
  • 我们需要每个Statful里面的东西对应一个模型 每个模型里面做一个记录(?什么叫每个StatfulWidget里面对应一个模型)

那么我们可不可以用StatfulWidget来做所有的Widget这样我们就不用管是不是需要设置状态的这个问题了

  • 最好不要把所有的Widget设置成StatefulWidget因为所有的StatfulWidget在数据刷新的时候就会重建这个Widget这样消耗性能

二. 如何设置Android的template

创建文件的快捷键的设置 CTRL + alt + , 我们可以在keymap里面设置创建文件的快捷键

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCpjA3wH-1584345888531)(D69E10C0D2CE46F6B42E370C927FB5DA)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PZEIvD67-1584345888534)(C0B1621787C5431AB02CF6A4D9552932)]

如何设置Android Studio代码模板

我们在setting中找到对应的 LiveTemplate

  • 这个使用范围是 顶层使用的意思就是只能在最外层使用 在里面写代码不会出现这个结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qgpPiNUQ-1584345888537)(510E5187E4274D2E9E7D05D855D055E0)]

三. 今天的主要内容

  • 注意我们不会讲所有的flutter的内容
  • 最重要的是掌握学习方法 不会的时候去看源代码 或者去查文档 百度 google

滚动的Widget

  1. ListView 这个是和很多的语言里面都差不多 IOS:UITableView Web:List
  2. GridView 这个是九宫格
  3. sliver分片 上面两个本质上就是用的这个
  4. 滚动的监听

四. ListView

  • 列表就用的比较多了 通讯录了 商品列表了 都是用列表来做的

创建ListView的创建方式有很多

  • 默认的构造函数 ListView()
  • ListView.builder()
  • ListView.separated() 这个可以给我们增加一个分割线
  • ListView.custom 这个是一个sliver的代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3SErOAfw-1584345888538)(20D750A3C740461EB7C57571C808F971)]

我们来到官方文档, 我们用的最多的是ListView GridView Custom

然后我们找到这个对应的ListView 可以看到它的构造器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HORSHC4Y-1584345888539)(5CEE7CAAF03842F895ECDBCE2B65A0A8)]

4.1 ListView()
  • 想我们的ListView它既然是一个可以滚动的视图 那么到时候这个里面因该是可以放很多的视图
  • 所以它因该是和Column一样使用的children
class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView(
        children: <Widget>[
          
        ],
      ),
    );
  }
}

我们这里有一个小语法

很多的前端语言都会再超出之后会自动滚动

但是这个flutter和一些语言不会

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uXVLO7kB-1584345888541)(20E5C1477D324DCDB2F0E9FE992695E6)]

List.generate 创建ListIten

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView(
        children: List.generate(length, generator)
      ),
    );
  }
}

可以看到这个构造器需要什么东西 后面我们用List做展示的时候都会用这个List.generate

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BRNmQkus-1584345888542)(A01FF4CD65204B24BC213B5052A135EB)]

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView(
        children: List.generate(100, (index) {
          return Text("Hello World $index");
        })
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohb4VagA-1584345888544)(943749C799984AB4B395BC721A09374C)]

帮助我们生成一个列表

还有就是我们可以使用一个它已经搭建好的小东西

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView(
        children: List.generate(100, (index) {
          return ListTile(
//           头部
            leading: Icon(Icons.people),
//            尾部
            trailing: Icon(Icons.delete),
            title: Text("联系人 ${index + 1}"),
            subtitle: Text("联系人的电话号码: 18188480948"),
          );
        })
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZbrqxR7H-1584345888545)(8CB228AFC3A54AAA97A9F75E0513EFF9)]

这个就是用这个ListTile来做的一个简单的东西

  • 注意啊 很多的前端编程语言里面有很多的都是如果你的东西超出屏幕的范围 它就会自动滚动
  • 但是很多的也不会这样 比如flutter IOS 它都是不会再内容超出屏幕的时候它 就会报错
  • 所以 如果你的内容超过了这个视口 的话就需要给它添加滚动的Widget

4.2 failed assertion: line XXX pos 12: “hasSize” 报错

  • 这里有scrollDirection

设置滚动方向 让我们试试

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView(
        scrollDirection: Axis.horizontal,
        children: List.generate(100, (index) {
          return ListTile(
//           头部
            leading: Icon(Icons.people),
//            尾部
            trailing: Icon(Icons.delete),
            title: Text("联系人 ${index + 1}"),
            subtitle: Text("联系人的电话号码: 18188480948"),
          );
        })
      ),
    );
  }
}

发现报错了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1lbMkyP-1584345888546)(4450BD8D1471416093279ABEA3F819ED)]

这个报错是说有一个断言没有通过

我们这样改一下就是没有设置这个高度, 如果你不确定高度它就不好给你排布

  • 因为flutter布局是声明式的
  • 就是你告诉它你想布局什么东西 然后它给你布局
  • 所以如果这里有写宽度和高度没有确定的时候flutter就不能给你很好的调整 就会报刚刚才的这个错误
class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView(
        itemExtent: 100,
        scrollDirection: Axis.horizontal,
        children: List.generate(100, (index) {
          return ListTile(
//           头部
            leading: Icon(Icons.people),
//            尾部
            trailing: Icon(Icons.delete),
            title: Text("联系人 ${index + 1}"),
            subtitle: Text("联系人的电话号码: 18188480948"),
          );
        })
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2S7lFqWc-1584345888547)(BBDA5BC47EA2482EBFEF0F153467142D)]

但是如果我们继续用的话这个东西就会变的比较丑

reverse

这个就用的更少了

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView(
        itemExtent: 100,
//        scrollDirection: Axis.horizontal,
        reverse: true,
        children: List.generate(100, (index) {
          return ListTile(
//           头部
            leading: Icon(Icons.people),
//            尾部
            trailing: Icon(Icons.delete),
            title: Text("联系人 ${index + 1}"),
            subtitle: Text("联系人的电话号码: 18188480948"),
          );
        })
      ),
    );
  }
}

也就是反着排

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwgkHSxA-1584345888548)(E6C0ED2967094E719390253FD1A15E46)]

primary

这个不是很清楚我们就到 propites 来看属性的作用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4nt005Op-1584345888550)(C13D499FA1944554A0EC2EF45BD99000)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FiHr7W4V-1584345888551)(45F92B1E73F24165A11AE09EB01EEBB5)]

这个是和PrimaryScrollController有关

如果你需要在关联这个Controller 我们就需要将他设置为true

我们来到ListView的继承StatelessWidget的父类 看到它的build方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OQgNrGMo-1584345888553)(7B13A5FCF4EA4683BAC929742DDEFFE5)]

如果你的Primary为true的时候同时这个scrollController不为空的时候做对应的操作

  • 当然我们的属性比较多 我们最常用的属性就是这个itemExtent 设置一个固定的高度
  • 如果你们有设置它就会根据你内容的高度来设置高度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z9IVFTzs-1584345888554)(7E00241834BF41549F39285AF20C866D)]

其他的东西就用的比较少了

  • 注意如果我们使用的默认的构造方法 它就会创建尽可能多的内部组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yM83G0Zv-1584345888555)(FBC6688B4C3B47D0B696CC8679AFF322)]

  • 比如这里我们创建了100个那它就会直接全部加载出来 以方便我们后面进行展示
  • 所以如果孩子比较少的时候就可以使用这个 比较多的时候就不要使用了
  • 如果有一些确定的Item需要展示我们就用这个ListView 但是如果不确定有多少的时候我们就用其他的

4.2 ListView.builder()

之前我们用这个ListView的时候它的里面有一个children 那我们就可以在里面创建很多的Widget

但是ListView.builder不一样 它这里常见的参数有两个

  • itemCount: 准备展示多少个
  • itemBuilder: 发现它是一个typedef
  • itemExtent: 高度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-96hDCeAu-1584345888556)(9B5BA0FE993F476E9B2517756F0B7DB0)]

这个定义的参数是一个context和index然后返回一个widget

这个context后面会讲

  • 所以这里是要求传入一个回调函数 它就是要你传入它的每个项的创建方法
  • 这个使用builder的ListView的构造函数是在需要展示的时候再绘制出来的 所以展示不知到有多少的时候 会更加的节省性能
class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView.builder(
        itemCount: 100,
        itemBuilder: (BuildContext ctx, int index) {
          return Text("hello world ${index + 1}" ,style: TextStyle(fontSize: 30));
        }
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4FPJcydJ-1584345888557)(1D721B222AFB4373BBDEF9F77DDE6331)]

itemExtent

一样的设置高度

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView.builder(
        itemCount: 100,
        itemExtent: 60,
        itemBuilder: (BuildContext ctx, int index) {
          return Text("hello world ${index + 1}" ,style: TextStyle(fontSize: 30));
        }
      ),
    );
  }
}

4.3 ListView.separated

这个因该就是有分割线的ListView构造函数

可以看到它的这个和builder的非常像因该就是做了一个代理

  • itemBuilder&itemCount: 就和ListView的builder方法一样
  • separatorBuilder: 分割线的构造函数 它的类型和itemBuilder一样
class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView.separated(
          itemBuilder: (BuildContext ctx, int index) {
            return Text("hello world ${index + 1}" ,style: TextStyle(fontSize: 30));
          },
          separatorBuilder: (BuildContext ctx, int index) {
            return Divider(color: Colors.red);
          },
          itemCount: 100
      ),
    );
  }
}

这个Divider也是一个Widget, 这些简单我们不会专门去讲

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ljbcTJ9G-1584345888559)(6113BC4B20374B86A24EB4F9721139C4)]

这样我们就完成了一个带有分割线的ListView

当然这里也可以不仅可以使用Divider也可以使用诸如 Icon之类的东西

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView.separated(
          itemBuilder: (BuildContext ctx, int index) {
            return Text("hello world ${index + 1}" ,style: TextStyle(fontSize: 30));
          },
          separatorBuilder: (BuildContext ctx, int index) {
            return Divider(color: Colors.red, height: 100, endIndent: 50, indent: 50, thickness: 10,);
          },
          itemCount: 100
      ),
    );
  }
}

Divider:

  • endIndent 据后端的距离

  • Indent 据前端的距离

  • thickness 的厚度

  • 这个方法没有设置高度的东西 他是自适应高度的情况

  • 这个地方它也是需要的时候加载

  • 一般这种需要回调函数的时候都是临时创建的

  • 如果想要在局部的区域进行滚动展示 我们就可以给它包一个Container

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Container(
        height: 300,
        child: ListView.separated(
            itemBuilder: (BuildContext ctx, int index) {
              return Text("hello world ${index + 1}" ,style: TextStyle(fontSize: 30));
            },
            separatorBuilder: (BuildContext ctx, int index) {
              return Divider(color: Colors.red, height: 100, endIndent: 50, indent: 50, thickness: 10,);
            },
            itemCount: 100
        ),
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UNFxi3ca-1584345888560)(FAD5722F9B3B4214B5E5E055F785D095)]

五. GridView

这个东西就是一个九宫格一样的东西, 我们就来做这个

GridView创建方法

  • GridView
  • GridView.count()
  • GridView.extend()
  • GridVIew.builder()

看究竟是谁实现的话可以用

如果不是widget就看父类 如果是widget就看build fulwidget就看Stat的build

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ClBrE6sm-1584345888562)(5434771A39094EB398464787F6497AFA)]

5.1 GridView()

他需要传一个 gridDelegate参数

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: GridView(
        gridDelegate: null,
        children: List.generate(100, (index) {
          return Container(
            color: Colors.red,
            width: 10,
            height: 10
          );
        })
      ),
    );
  }
}
  • 我们肯定需要把delegate传了才能运行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQRdrze0-1584345888562)(5DA4E2D06A204601B1D7FF38D26CC879)]

必选传了delegate才能运行

点到里面去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3FYKHJ6N-1584345888563)(FFFE93CA05644A689630199A7FE23C72)]

但是它是抽象的所以我们找子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g6bN1lvy-1584345888564)(596849C4FA2349EF846633B466FC4FD5)]

可以看到里面有三个 但是_ 私有的不用看

那么剩下两个前面都是一样的 看后面就行了

  • FixedCrossAxisCount 固定交叉轴的个数
    • 这个东西和Column一样竖着排的所以它的交叉轴因该是水平的
    • 固定个数也就是说如果你想排3个 那它就把整个宽度除以3等于每个的宽度来排
  • FixedCrossAxisExtent 固定每个项的长度
    • 同理这个东西是给定每个项的宽度然后 尽量在每行排 多的就放下一行 具体放下几个就不给定了

所以这个东西flutter就整的很合理

5.1.1 SliverCrossAxisCount

我们为了看清有几个可以这样也可以使用多种颜色来区分

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: GridView(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
           crossAxisCount: 3
        ),
        children: List.generate(100, (index) {
          return Container(
            margin: EdgeInsets.all(3),
            color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
            width: 10,
            height: 10
          );
        })
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4bwpLyaB-1584345888565)(F55EF8AEE5C74851B9D64F37C7F0ACC5)]

那这个东西高度怎么确定呢

这个东西也在Delegate里面设置

Delegat 设置 childAspectRatio 来设置元素高度
crossAxisSpacing: 8 mainAxisSpacing: 8

主轴交叉轴距离 这里尝试的时候我就把margin给去掉了

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: GridView(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
           crossAxisCount: 3,
           childAspectRatio: .5,
           crossAxisSpacing: 8,
           mainAxisSpacing: 8
        ),
        children: List.generate(100, (index) {
          return Container(
            color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
            width: 10,
            height: 10
          );
        })
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gVaRqhIS-1584345888566)(197F369747E347AFA1971E600AE2F238)]

但是你会发现这个左右两侧有间距 所以我们可以用在最外层包裹一个padding的方法来产生间距

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8),
        child: GridView(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
             crossAxisCount: 3,
             childAspectRatio: .5,
             crossAxisSpacing: 8,
             mainAxisSpacing: 8
          ),
          children: List.generate(100, (index) {
            return Container(
              color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
              width: 10,
              height: 10
            );
          })
        ),
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25WVXoDH-1584345888567)(86AB84BC45854D368C2B607829D16357)]

同时我们也给这个垂直方向设置一个间距 这样就出现一个问题

  • 你着顶上的距离去不掉
class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
        child: GridView(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
             crossAxisCount: 3,
             childAspectRatio: .5,
             crossAxisSpacing: 8,
             mainAxisSpacing: 8
          ),
          children: List.generate(100, (index) {
            return Container(
              color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
              width: 10,
              height: 10
            );
          })
        ),
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1opfgx4i-1584345888568)(7E485419014B4A72BDA6E6AF481CB3AF)]

但是这个东西现在是没有办法解决的 但是我们后面有一个Sliver是专门来解决这个问题的

5.1.2 SliverGridDelegateWithMaxCrossAxisExtent

用这个的话就需要传宽度

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: GridView(
        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(

        ),
        children: List.generate(100, (index) {
          return Container(
              color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
              width: 10,
              height: 10
          );
        }),
      ),
    );
  }
}

我们知道这个屏幕大小是 414左右 所以如果传100那么因该是可以放下四个

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: GridView(
        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
          maxCrossAxisExtent: 100
        ),
        children: List.generate(100, (index) {
          return Container(
              color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
              width: 10,
              height: 10
          );
        }),
      ),
    );
  }
}

但是这里放下了5个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6n3j7zui-1584345888570)(C7A6FC72B88B445991C08D9827D5869D)]

  • 我们传的参数是max所以它是可以小于这个值的 所以它是可以多于这个值的
  • 这个是它自己去调整的

同样可以给它设置这些delegate选项

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: GridView(
        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
          maxCrossAxisExtent: 100,
          crossAxisSpacing: 8,
          mainAxisSpacing: 8,
          childAspectRatio: 1.8
        ),
        children: List.generate(100, (index) {
          return Container(
              color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
              width: 10,
              height: 10
          );
        }),
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SUIhpObF-1584345888571)(A1EABE5FEE564D15949338BD9FC385E7)]

所以我们在设置这个GridView的排版的时候我们就可以用这两个东西来排布

5.2 GridView.count

这个和那个 设置成 SliverGridDelegateWithFixedCrossAxisCount 的用法是一样的

5.3 GridView.builder

这个和ListView.builder的作用是一样的 只有当需要呈现的时候才会将他刷新

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          mainAxisSpacing: 8,
          crossAxisSpacing: 8
        ),
        itemBuilder: (BuildContext ctx, index) {
          return Container(
              color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
              width: 10,
              height: 10
          );
        }
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vQ31EUwG-1584345888572)(FDA55A531C2141A781FBE88F4556B7EE)]

六. Sliver的基本使用

ListView GridView 他们的本质是什么

我们点进去

看源代码的学习方式

  • 看源代码的时候 如果父类不是Widget我们就 继续找它的父类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UicjHptj-1584345888573)(F51004AE79D144A0B051E62F3B1B0CF8)]

不是Widget就继续找父类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RPUfEUDX-1584345888574)(6D5FE37D39C54A37AB73552123F07A9C)]

  • 如果是Widget我们就看build方法

这个就像我们之前看text实际上是去看它的RichText一样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9nye1N6w-1584345888575)(57C8674323124F3D9B9C69A4820CD5F8)]

  @override
  Widget build(BuildContext context) {
    final List<Widget> slivers = buildSlivers(context);
    final AxisDirection axisDirection = getDirection(context);

    final ScrollController scrollController = primary
      ? PrimaryScrollController.of(context)
      : controller;
    final Scrollable scrollable = Scrollable(
      dragStartBehavior: dragStartBehavior,
      axisDirection: axisDirection,
      controller: scrollController,
      physics: physics,
      semanticChildCount: semanticChildCount,
      viewportBuilder: (BuildContext context, ViewportOffset offset) {
        return buildViewport(context, offset, axisDirection, slivers);
      },
    );
    return primary && scrollController != null
      ? PrimaryScrollController.none(child: scrollable)
      : scrollable;
  }

它这里最主要的要返回的东西是

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aWq6IVhw-1584345888576)(1D36B86ACC9A415CB0871C0B23DF3A0A)]

  • 所以我们点到实际渲染的Widget里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjJCj3pn-1584345888576)(F9F1EE1CB9CC416DABCC98055D07FB2F)]

  • 发现它是一个StatfulWidget 所以我们要去到它的build方法

看到它本质上又创建了一个_ScrollableScope

  @override
  Widget build(BuildContext context) {
    assert(position != null);
    ...
    Widget result = _ScrollableScope(
     ...
  }
  • 所以我们继续取到_ScrollableScope

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3maP8KlV-1584345888577)(F3216DCB796544D1BCF4887CC8AFC95E)]

这个是InheritedWidget 这个就是一个记录着状态的Widget

他记录着当前的滚动的位置那些东西 但是这里就有点深了

这个是结构 但是我们不是要找这个

buildSliver

  • ListView -> BoxScrollView -> ScrollView(正真的Widget) 这个里面有一个非常重要的方法buildSliver
  • ListView, GridView 实际上都要做一个buildSliver 它们本质都需要这个buildSliver 这个才是正真可以滚动的东西

它这里获取到是一个List

这个地方点过去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q7UKHuj9-1584345888577)(08258C65B1E346D78B01F37B05BFBABB)]

会发现他啊是一个抽象方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iAFVs9Yh-1584345888578)(D1EF28CDA0DC442997E89B2978548AE5)]

可见这个ScrollView都没有实现 这个关键的方法

我们看到它的子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQB4stCo-1584345888578)(90020573E869426DB338B58D4EB1FDDA)]

所以关系是这样的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pago55We-1584345888579)(953DFE794EF64AEF99EFDAEDC0B8C3A5)]

  • BoxScrollView里面确实实现了这个buildSliver

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G9k1vbKt-1584345888579)(F625CAE3DA14459CB3806D78568A5E10)]

  • 我们看到它是在这个LayoutSliver拿到这个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25d3pkGJ-1584345888580)(E15DCB7D6E5740A88DC6D38EF7E5D2E1)]

  • 我们看到这个里面有一个数组 它这个里面就只有这个一个元素
  @override
  List<Widget> buildSlivers(BuildContext context) {
    Widget sliver = buildChildLayout(context);
    ...

    if (effectivePadding != null)
      sliver = SliverPadding(padding: effectivePadding, sliver: sliver);
    return <Widget>[ sliver ];
  }
  • 我们发现这个sliver来自buildChildLayout 那我们再点进去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GevsDChB-1584345888581)(9AF16D3A1F6F4ECFABB42D18EB9BCBB1)]

  • 这个是个抽象方法 这个是给子类实现的 ListView和GridView实现的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wpF1Hmm4-1584345888581)(2281EDCB650340CEB3094B7FF5941A7A)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fedZmKpu-1584345888582)(1F6F690FE5E14756BE9EAA61EBE81D74)]

我们看到对应的都返回对应的Layout

  • ListView里面创建的是SliverList 或者 SliverFixedExtentList
  • GridView里面创建的是SliverGrid

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GTGQkxpI-1584345888582)(FB4E8431364E478595D29D6FE810A359)]

这些抽象方法取做的事情就是

去创建Sliver这个东西

ListView就是去创建SliverList 或者 SliverFixedExtentList

GridView就是去创建SliverGrid

这个也就是Slivers当中可用的Sliver 所以其实ListView和GridView的底层就使用的Sliver的东西

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLVejlKe-1584345888582)(5E904ED9AAF54850ABF4170EAED18330)]

但是我们希望在开发里面既有ListView又有GridView

但是ListView和GridViewp[sliver] 只有一个

但是如果我们使用CustomScrollView

就可以在里插入各种 ListView GridView

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nhXQdMro-1584345888583)(D2F52C61AE0C48A9A774E0B4A77AC381)]

其实拼起来也可以但是最标准是放在slivers

七. CustomScrollView

它这里要传一个slivers 虽然泛型是Widget但是实际上这里只能传sliverWidget

传一个GirdView试试

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverGrid(

          )
        ],
      ),
    );
  }
}

它这个构造器是和很多的构造器是差不多的

这个gridDelegate和一般的是差不多的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pGnh4djM-1584345888583)(A8CA4E4993F94C929FEF412ECF5F240B)]

注意我们的默认的构造器就是要你传一个方法所以它的效率也是比较高的

不用专门去找builder了

但是这个SliverChildDelegate 它还有有点不一样

SliverChildDelegate它有两个子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jLqCEKtJ-1584345888584)(F026E07AE20942A4BC12BDD7ADBCD9F0)]

SliverChildListDelegate

这个东西的用法和List.generate 一样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HEixsXgE-1584345888586)(4591E438AFF04EE69C9BADA6AD2CBA5F)]

其实我们可以看源码 ListView这些东西就是最后创建了一个 SliverChildListDelegate

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOwYW2Kq-1584345888586)(206C1C0DE7B640FFAB4EF20DA080573D)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NzhE2uzT-1584345888587)(05BBCF1860AA4F2CA824C2000812C378)]

  • 而这个东西就是让传入一个自定义的delegate
class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: 8,
              mainAxisSpacing: 8,
              childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                }
            ),
          )
        ],
      ),
    );
  }
}

这样我们就可以正常展示了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ISRYCSx-1584345888588)(E2ED72A8FD9449638FF344378541F271)]

但是这样创建完成以后它是无穷的 你可以一直滚

我们可以给他再传入一个参数

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: 8,
              mainAxisSpacing: 8,
              childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                },
                childCount: 100
            ),
          )
        ],
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b2rqVgnB-1584345888589)(5C51FB950E2F405A872F0A3593EA7CFF)]

如果我们的脚手架是没有AppBar的

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: 8,
              mainAxisSpacing: 8,
              childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                },
                childCount: 100
            ),
          )
        ],
      ),
    );
  }
}

这个时候我们有内容就会被刘海给盖上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNgi39kB-1584345888589)(5720F7B9D5C247EEA2B07FFA5FCE900C)]

safeArea

这个也就是说我们的这些东西因该是在我们的安全区域里面显示的

也可能会被下面的东西给盖上

所以我们给这个CustomScrollView包上一个SafeArea

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: CustomScrollView(
          slivers: <Widget>[
            SliverGrid(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
              ),
              delegate: SliverChildBuilderDelegate(
                  (BuildContext ctx,int index) {
                    return Container(
                        color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                        width: 10,
                        height: 10
                    );
                  },
                  childCount: 100
              ),
            )
          ],
        ),
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3avevcLA-1584345888590)(24FD1E0FADDB4BC4930C6B2082A87302)]

这样它就不会跑到上面去了

但是这个还有一个不好的地方 我们滚上去的时候内容是不能滚到最顶上的

我们想在滚上去的时候是能在安全区域里面显示我们的内容的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FE5SVuLM-1584345888591)(DA3A50236DA94BFF944C7212501C0999)]

这个时候我们就不能用这个SafeArea这个东西了

我们可以用这个SliverSafeArea

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverSafeArea(
            sliver: SliverGrid(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
              ),
              delegate: SliverChildBuilderDelegate(
                  (BuildContext ctx,int index) {
                    return Container(
                        color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                        width: 10,
                        height: 10
                    );
                  },
                  childCount: 100
              ),
            ),
          )
        ],
      ),
    );
  }
}

这个时候我们就可以滚上去了

如果你不想这个东西可以滚到最上面你就用这个SafeArea就可以了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4nQ801qG-1584345888591)(BD10BFC3C09544CDA70127CFAE6D8D72)]

现在我们想给它一个内边距

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CHjZGWqy-1584345888592)(4A3A6ABB2D0A422D85846A536D18D107)]

按我们目前学的我们大概率会加一个padding

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: Text("Slivers Demo")
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: CustomScrollView(
          slivers: <Widget>[
            SliverSafeArea(
              sliver: SliverGrid(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  crossAxisSpacing: 8,
                  mainAxisSpacing: 8,
                  childAspectRatio: 1.5
                ),
                delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index) {
                      return Container(
                          color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                          width: 10,
                          height: 10
                      );
                    },
                    childCount: 100
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

但是这个就又一个问题了 又滚不上去了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lttg0Qg1-1584345888592)(91E62F0BC01F4FB48ECE5452E231E28B)]

所以这个时候就不用这个padding了我们用SliverPadding

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: Text("Slivers Demo")
      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverSafeArea(
            sliver: SliverPadding(
              padding: EdgeInsets.all(8),
              sliver: SliverGrid(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  crossAxisSpacing: 8,
                  mainAxisSpacing: 8,
                  childAspectRatio: 1.5
                ),
                delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index) {
                      return Container(
                          color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                          width: 10,
                          height: 10
                      );
                    },
                    childCount: 100
                ),
              ),
            ),
          )
        ],
      ),
    );
  }
}

这样我们就可以滚上去了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HGWBst3O-1584345888593)(617C3FEE07CE449C932AE8FF90283930)]

这个就是关于我们的Sliver最基础的使用

使用CustomScrollView存放多个Sliver

刚刚其实我们还是只存放了一个Sliver

只不过多个Sliver之前存在一定的嵌套关系

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: Text("Slivers Demo")
      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                },
                childCount: 10
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx, int index) {
                  return ListTile(
                      leading: Icon(Icons.people),
                      title: Text("联系人"),
                  );
                }
            ),
          ),

        ],
      ),
    );
  }
}

这样你就会发现这个 有个GridView还有一个ListView了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UeHRmcae-1584345888594)(5B34C664B94A40C0B01676999C6D087F)]

但是如果我们想做这个东西因该怎么做呢

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aqy7SQ74-1584345888594)(D2F52C61AE0C48A9A774E0B4A77AC381)]

SliverAppBar

  • Controller
    1. 可以设置默认值
    2. 可以监听滚动, 也可以监听滚动的位置
  • NotificationListener
    1.

如果想要做导这个效果的话可以使用这个SliverAppBar

这里就可以展示不用这个AppBar了

这个导航是属于slivers 的是可以随内容一起滚动的

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
//      appBar: AppBar(
//          title: Text("Slivers Demo")
//      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
          ),
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                },
                childCount: 10
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx, int index) {
                  return ListTile(
                      leading: Icon(Icons.people),
                      title: Text("联系人"),
                  );
                }
            ),
          ),

        ],
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tUorkw6I-1584345888595)(45AF53DB180A4716AC681CD9105B25C8)]

如果你不想让他滚动的话可以设置一个属性 这样它就不会滚动了

          SliverAppBar(
            pinned: true,
          ),

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-La8zwBAn-1584345888595)(09FC5818AB1F4F5F9A286C34C5B4F887)]

这个SliverAppBar也是有很多的属性的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APzwzin5-1584345888596)(628E20B204FA43F4B4E61A3CA96C8075)]

我们来简单的用一下这个东西

  • expendHeight 扩展的高度
  • flexibleSpace 里面放的东西 Tpically 富有代表性的东西

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7Pqnzec-1584345888596)(6EF319CAF2664D81A5BCC55767500E03)]

可以放这个FlexibleSpaceBar 具有代表性嘛

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
//      appBar: AppBar(
//          title: Text("Slivers Demo")
//      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
//            pinned: true,
//          阔展高度
            expandedHeight: 300,
            flexibleSpace: FlexibleSpaceBar(
              title: Text("hello world"),
            ),
          ),
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                },
                childCount: 10
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx, int index) {
                  return ListTile(
                      leading: Icon(Icons.people),
                      title: Text("联系人"),
                  );
                }
            ),
          ),

        ],
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lx2KR9kM-1584345888597)(5A258B4083824FD78DFCAC15CF6FDA78)]

同时我们还可以传入图片

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
//      appBar: AppBar(
//          title: Text("Slivers Demo")
//      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
//            pinned: true,
//          阔展高度
            expandedHeight: 300,
            flexibleSpace: FlexibleSpaceBar(
              title: Text("hello world"),
              background: Image.asset("assets/images/test.jpg", fit: BoxFit.cover),
            ),
          ),
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                },
                childCount: 10
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx, int index) {
                  return ListTile(
                      leading: Icon(Icons.people),
                      title: Text("联系人"),
                  );
                }
            ),
          ),

        ],
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0sYVwsHI-1584345888597)(CB69DD9987F04C96B4C179F0AEEE275D)]

也可以让导航停在这里

class HYHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
//      appBar: AppBar(
//          title: Text("Slivers Demo")
//      ),
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
//            pinned: true,
//          阔展高度
            expandedHeight: 300,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              title: Text("hello world"),
              background: Image.asset("assets/images/test.jpg", fit: BoxFit.cover),
            ),
          ),
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
            ),
            delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index) {
                  return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      width: 10,
                      height: 10
                  );
                },
                childCount: 10
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx, int index) {
                  return ListTile(
                      leading: Icon(Icons.people),
                      title: Text("联系人"),
                  );
                }
            ),
          ),

        ],
      ),
    );
  }
}

八. 滚动的监听

有两种方式可以监听

  • controller
    • 可以设置默认高度
    • 可以监听滚动
  • NotificationListener
    • 可以监听开始滚动和结束滚动

ListView不管你用那个方法 甚至不管用GridView还是CustomView都有这个方法

8.1 controller监听滚动

这个东西叫做controller

  • 先把我们的这个lessWidget转换成fulWidget
  • 然后我们就定义变量了ScrollController

这个initialScrollOffset 就是初始的默认高度

class _HYHomePageState extends State<HYHomePage> {
  ScrollController controller = ScrollController(initialScrollOffset: 300);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView.builder(
        controller: controller,
        itemBuilder: (BuildContext ctx, int index) {
          return ListTile(
            leading: Icon(Icons.people),
            title: Text("联系人 ${index + 1}"),
          )
        }
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BaeCnezc-1584345888597)(08412CF40243426595D2EB90BFBE465C)]

如果我们想监听滚动到哪里了 我们可以在initState() 做监听的操作 这个里面也经常的做写监听的操作

这个addListener里面是一个没有返回值也没有参数的函数

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    
    controller.addListener(listener)
  }
class _HYHomePageState extends State<HYHomePage> {
  ScrollController controller = ScrollController(initialScrollOffset: 300);

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    
    controller.addListener(() {
      print("监听到滚动${controller.offset}");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView.builder(
        controller: controller,
        itemBuilder: (BuildContext ctx, int index) {
          return ListTile(
            leading: Icon(Icons.people),
            title: Text("联系人 ${index + 1}"),
          );
        },
        itemCount: 100,
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8HJXD94k-1584345888598)(0002E13F1A3A4D2990CA24A0CD55C031)]

现在我们就想做一个需求了 当我们滚动到对应的位置

我们设置一个变量来保存这个状态

class _HYHomePageState extends State<HYHomePage> {
  ScrollController _controller = ScrollController(initialScrollOffset: 300);
  bool _isShowFloatingButton = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _controller.addListener(() {
      print("监听到滚动${_controller.offset}");
      setState(() {
        _isShowFloatingButton = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: ListView.builder(
        controller: _controller,
        itemBuilder: (BuildContext ctx, int index) {
          return ListTile(
            leading: Icon(Icons.people),
            title: Text("联系人 ${index + 1}"),
          );
        },
        itemCount: 100,
      ),
      floatingActionButton: _isShowFloatingButton? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: () {
//          要滚动到一个位置可以用jumpTo也可以用animateTo
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hYC2JTh6-1584345888598)(3329969306FB47719402C84518991175)]

但是这个有一个问题

它没有办法监听开始滚动和结束滚动

8.2 NotificationListener监听滚动

class _HYHomePageState extends State<HYHomePage> {
  ScrollController _controller = ScrollController(initialScrollOffset: 300);
  bool _isShowFloatingButton = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _controller.addListener(() {
      print("监听到滚动${_controller.offset}");
      setState(() {
        _isShowFloatingButton = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: NotificationListener(
        onNotification: ,
        child: ListView.builder(
          controller: _controller,
          itemBuilder: (BuildContext ctx, int index) {
            return ListTile(
              leading: Icon(Icons.people),
              title: Text("联系人 ${index + 1}"),
            );
          },
          itemCount: 100,
        ),
      ),
      floatingActionButton: _isShowFloatingButton? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: () {
//          要滚动到一个位置可以用jumpTo也可以用animateTo
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }
}

onNotification这个属性是传入一个函数

它是一个泛型的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p0j0wZm6-1584345888599)(B104BB0BB6584FBE92F8C20C8FCF133C)]

是你要监听谁就传入谁就可以了

返回值是是否冒泡的作用 false就继续冒泡 true就是不冒泡

class _HYHomePageState extends State<HYHomePage> {
  ScrollController _controller = ScrollController(initialScrollOffset: 300);
  bool _isShowFloatingButton = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _controller.addListener(() {
//      print("监听到滚动${_controller.offset}");
      setState(() {
        _isShowFloatingButton = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: NotificationListener(
        onNotification: (ScrollNotification scrollNotification) {
          print("监听到滚动...");
          return true;
        },
        child: ListView.builder(
          controller: _controller,
          itemBuilder: (BuildContext ctx, int index) {
            return ListTile(
              leading: Icon(Icons.people),
              title: Text("联系人 ${index + 1}"),
            );
          },
          itemCount: 100,
        ),
      ),
      floatingActionButton: _isShowFloatingButton? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: () {
//          要滚动到一个位置可以用jumpTo也可以用animateTo
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }
}

同样是监听到了滚动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktVRrC4F-1584345888599)(27D9D84C3C9344308668CACDFC22CB0F)]

那这个ScrollNotification 的参数是干什么的啊

它这个参数有很多个子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GY95dTlb-1584345888600)(B50E172564164E3990C6A65A7871A2E8)]

这个东西主要就是用类判断它是

是什么类型 还有就是一遍滚动一刷新消耗性能 可以放在 在结束滚动的时候再刷新

class _HYHomePageState extends State<HYHomePage> {
  ScrollController _controller = ScrollController(initialScrollOffset: 300);
  bool _isShowFloatingButton = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _controller.addListener(() {
//      print("监听到滚动${_controller.offset}");
      setState(() {
        _isShowFloatingButton = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: NotificationListener(
        onNotification: (ScrollNotification scrollNotification) {
          if(scrollNotification is ScrollStartNotification) {
            print("开始滚动");
          } else if(scrollNotification is ScrollUpdateNotification) {
//            你是可以这样计算出一个百分比
            print("正在滚动。。总滚动的距离${scrollNotification.metrics.maxScrollExtent}当前位置 ${scrollNotification.metrics.pixels}");
          } else if(scrollNotification is ScrollEndNotification) {
            print("结束滚动");
          }
          return true;
        },
        child: ListView.builder(
          controller: _controller,
          itemBuilder: (BuildContext ctx, int index) {
            return ListTile(
              leading: Icon(Icons.people),
              title: Text("联系人 ${index + 1}"),
            );
          },
          itemCount: 100,
        ),
      ),
      floatingActionButton: _isShowFloatingButton? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: () {
//          要滚动到一个位置可以用jumpTo也可以用animateTo
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4W6jqpLR-1584345888600)(87C13FF8DD874CDAB4FEEA211F173B96)]

还有就是注意要销毁一下这个controller

避免有引用的时候 浪费性能

class _HYHomePageState extends State<HYHomePage> {
  ScrollController _controller = ScrollController(initialScrollOffset: 300);
  bool _isShowFloatingButton = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _controller.addListener(() {
//      print("监听到滚动${_controller.offset}");
      setState(() {
        _isShowFloatingButton = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: NotificationListener(
        onNotification: (ScrollNotification scrollNotification) {
          if(scrollNotification is ScrollStartNotification) {
            print("开始滚动");
          } else if(scrollNotification is ScrollUpdateNotification) {
//            你是可以这样计算出一个百分比
            print("正在滚动。。总滚动的距离${scrollNotification.metrics.maxScrollExtent}当前位置 ${scrollNotification.metrics.pixels}");
          } else if(scrollNotification is ScrollEndNotification) {
            print("结束滚动");
          }
          return true;
        },
        child: ListView.builder(
          controller: _controller,
          itemBuilder: (BuildContext ctx, int index) {
            return ListTile(
              leading: Icon(Icons.people),
              title: Text("联系人 ${index + 1}"),
            );
          },
          itemCount: 100,
        ),
      ),
      floatingActionButton: _isShowFloatingButton? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: () {
//          要滚动到一个位置可以用jumpTo也可以用animateTo
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _controller.dispose();
  }
}

分割的监听器写法

import "package:flutter/material.dart";

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(
        primarySwatch: Colors.blue, splashColor: Colors.transparent,
      ),
      home: HYHomePage(),
    );
  }
}

class HYHomePage extends StatefulWidget {
  @override
  _HYHomePageState createState() => _HYHomePageState();
}

ScrollController _controller = ScrollController(initialScrollOffset: 300);

class _HYHomePageState extends State<HYHomePage> {

  bool _isShowFloatingButton = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _controller.addListener(() {
//      print("监听到滚动${_controller.offset}");
      setState(() {
        _isShowFloatingButton = _controller.offset >= 1000;
      });
    });
  }

  void changeShow() {
    _controller.addListener(() {
//      print("监听到滚动${_controller.offset}");
      setState(() {
        _isShowFloatingButton = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: NewWidget(changeShow),
      floatingActionButton: _isShowFloatingButton? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: () {
//          要滚动到一个位置可以用jumpTo也可以用animateTo
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _controller.dispose();
  }
}

class NewWidget extends StatefulWidget {
  final changeShow;
  NewWidget(this.changeShow);

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

class _NewWidgetState extends State<NewWidget> {


  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    widget.changeShow();
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener(
      onNotification: (ScrollNotification scrollNotification) {
        if(scrollNotification is ScrollStartNotification) {
          print("开始滚动");
        } else if(scrollNotification is ScrollUpdateNotification) {
//            你是可以这样计算出一个百分比
          print("正在滚动。。总滚动的距离${scrollNotification.metrics.maxScrollExtent}当前位置 ${scrollNotification.metrics.pixels}");
        } else if(scrollNotification is ScrollEndNotification) {
          print("结束滚动");
        }
        return true;
      },
      child: ListView.builder(
        controller: _controller,
        itemBuilder: (BuildContext ctx, int index) {
          return ListTile(
            leading: Icon(Icons.people),
            title: Text("联系人 ${index + 1}"),
          );
        },
        itemCount: 100,
      ),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5CpeHHC-1584345888602)(2432D9B9860A43C297C4279BAEE6AB82)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值