常见的滚动widget
这个转自我自己的有道云 想看图片去那里
文档:Day3_11 常见的滚动的Widget.md
链接:http://note.youdao.com/noteshare?id=a72c965bf9e1194b2d4873c64479f408&sub=25B1E0477B0B4C3CAF8384F640C2EC2B
常见的滚动的Widget
一. 关于上节课的StackWidget使用
- 如何将按钮点击变色
- 改变成StatefulWidget
- 设置一个变量
- 在IconButton改变其值
- setState(() {})
- 将color: _isFlage : Colors.red ? Colors.white;
class HYHomeContent extends StatefulWidget {@override_HYHomeContentState createState() => _HYHomeContentState();
}class _HYHomeContentState extends State<HYHomeContent> {
// 但是我们不能用bool _isFavite = false;@overrideWidget 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
- ListView 这个是和很多的语言里面都差不多 IOS:UITableView Web:List
- GridView 这个是九宫格
- sliver分片 上面两个本质上就是用的这个
- 滚动的监听
四. 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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)]
@overrideWidget 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
@overrideWidget 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)]
- 我们看到这个里面有一个数组 它这个里面就只有这个一个元素
@overrideList<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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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
- 可以设置默认值
- 可以监听滚动, 也可以监听滚动的位置
- NotificationListener
1.
如果想要做导这个效果的话可以使用这个SliverAppBar
这里就可以展示不用这个AppBar了
这个导航是属于slivers 的是可以随内容一起滚动的
class HYHomePage extends StatelessWidget {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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);@overrideWidget 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里面是一个没有返回值也没有参数的函数
@overridevoid initState() {// TODO: implement initStatesuper.initState();controller.addListener(listener)}
class _HYHomePageState extends State<HYHomePage> {ScrollController controller = ScrollController(initialScrollOffset: 300);@overridevoid initState() {// TODO: implement initStatesuper.initState();controller.addListener(() {print("监听到滚动${controller.offset}");});}@overrideWidget 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;@overridevoid initState() {// TODO: implement initStatesuper.initState();_controller.addListener(() {print("监听到滚动${_controller.offset}");setState(() {_isShowFloatingButton = _controller.offset >= 1000;});});}@overrideWidget 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;@overridevoid initState() {// TODO: implement initStatesuper.initState();_controller.addListener(() {print("监听到滚动${_controller.offset}");setState(() {_isShowFloatingButton = _controller.offset >= 1000;});});}@overrideWidget 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;@overridevoid initState() {// TODO: implement initStatesuper.initState();_controller.addListener(() {
// print("监听到滚动${_controller.offset}");setState(() {_isShowFloatingButton = _controller.offset >= 1000;});});}@overrideWidget 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;@overridevoid initState() {// TODO: implement initStatesuper.initState();_controller.addListener(() {
// print("监听到滚动${_controller.offset}");setState(() {_isShowFloatingButton = _controller.offset >= 1000;});});}@overrideWidget 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;@overridevoid initState() {// TODO: implement initStatesuper.initState();_controller.addListener(() {
// print("监听到滚动${_controller.offset}");setState(() {_isShowFloatingButton = _controller.offset >= 1000;});});}@overrideWidget 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,);}@overridevoid dispose() {// TODO: implement disposesuper.dispose();_controller.dispose();}
}
分割的监听器写法
import "package:flutter/material.dart";main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget 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;@overridevoid initState() {// TODO: implement initStatesuper.initState();_controller.addListener(() {
// print("监听到滚动${_controller.offset}");setState(() {_isShowFloatingButton = _controller.offset >= 1000;});});}void changeShow() {_controller.addListener(() {
// print("监听到滚动${_controller.offset}");setState(() {_isShowFloatingButton = _controller.offset >= 1000;});});}@overrideWidget 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,);}@overridevoid dispose() {// TODO: implement disposesuper.dispose();_controller.dispose();}
}class NewWidget extends StatefulWidget {final changeShow;NewWidget(this.changeShow);@override_NewWidgetState createState() => _NewWidgetState();
}class _NewWidgetState extends State<NewWidget> {@overridevoid initState() {// TODO: implement initStatesuper.initState();widget.changeShow();}@overrideWidget 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)]
常见的滚动widget相关推荐
- 08-可滚动Widget
可滚动Widget ViewPort视口 在Flutter中,术语ViewPort(视口),如无特别说明,则是指一个Widget的实际显示区域.例如,一个ListView的显示区域高度是800像素,虽 ...
- 【Flutter从入门到实战】 ⑨、滚动的Widget-ListView、GridView、SliverWidget、滚动的Widget的滚动监听的方式
Flutter从入门到实战 一共分为23个系列 ①(Flutter.Dart环境搭建篇) 共3个内容 已更新 ②(Dart语法1 篇) 共4个内容 已更新 ③(Dart语法2 篇) 共2个内容 已更新 ...
- 针尖上带着脚镣跳舞的widget
自从iOS 10苹果给widget做了一次大改版后,很多人都开发了自己的widget.网上也有很多教程,来告诉你怎么快速开发一个widget.我的这篇文章也不会重复那些简单的创建extension添加 ...
- web widget(微件)
本文章来源于:http://baike.baidu.com/view/704016.htm 简介: Web Widget,中文译名被称作是微件,是一小块可以在任意一个基于HTML的Web页面上执行的 ...
- 手机号3-4-4 滚动函数 滚盘抽奖
手机号3-4-4 ** – 需引入一个jq** or zepto 下列flow 就是344相关的东西, **$('#inpTel')**对应的就是输入input输入框DOM节点. var flow = ...
- Flutter学习之认知基础组件
一.前言 前一天,学习了Dart语法,对Dart的语法和特性有了更深一步的了解.今天,来学习Flutter的基础控件,身为Android开发者都知道,一开始入坑Android就要熟悉学习其控件,如:T ...
- 重磅发布 | 承载亿级流量的开发框架,闲鱼Flutter技术解析与实战大公开
简介: 闲鱼是国内最早接触使用 Flutter 的团队,经过多次研讨验证并大规模上线,在App性能.稳定性.开发效率上收益甚多.现在,闲鱼将这个过程中的一手实践知识和技术沉淀,整理成册 --<F ...
- Flutter第一部分(UI)第二篇:在Flutter中构建布局
前言:Flutter系列的文章我应该会持续更新至少一个月左右,从User Interface(UI)到数据相关(文件.数据库.网络)再到Flutter进阶(平台特定代码编写.测试.插件开发等),欢迎感 ...
- 从原生到黑科技:闲鱼 Flutter 图片优化经历了什么?
简介:阿里妹导读:图片加载是 APP 最常见也最基本的功能,也是影响用户体验的因素之一.在看似简单的图片加载背后却隐藏着很多技术难题.本文介绍闲鱼技术团队在 Flutter 图片优化上所做的尝试,分享 ...
最新文章
- python程序员需要掌握哪些技术-python运维要掌握哪些内容
- 使用github pages创建博客
- windows下编译firefox
- [渝粤教育] 盐城工学院 无机及分析化学C 参考 资料
- 前端学习(2158):webpack配置文件的分离
- 正则表达式30分钟入门教程(转)
- Leetcode 374.猜数字大小
- java 复杂表达式计算_我的计算器. 用java实现的. 可以支持复杂表达式
- Python 【第十章】 Django路由
- QUIC 协议在蚂蚁落地综述
- 禁止恶意域名访问服务器方法
- 关于教程被人盗版出售的一些感想
- 计算机创造奇迹的英语作文,大学英语作文:创造奇迹-Creating-Miracle.docx
- max导出fbx动画模型导入unity后播放会出现局部模型扭曲解决办法总结
- saber软件安装后怎么打开_Saber2016安装包和安装详细安装步骤
- [Windows]获取md5值最简单的工具
- 【react面试题】不可错过的react 面试题 「务必收藏」
- 最全可白嫖之高光谱图像数据处理(格式转换,数据增强,通道剪切,大小裁剪,光谱显示,折线图表示)
- SEO之怎么能写出高质量原创文章和伪原创文章
- oracle数据库要如何登陆,登陆oracle数据库的方式
热门文章
- 【牛腩】过程或函数 ‘news_selectByCaId‘ 需要参数 ‘@caid‘,但未提供该参数
- SERC 2013 E Skyscrapers
- jQuery CSS3中国象棋网页代码
- Virus total爬虫分享
- iOS中CAShapeLayer用法
- 高考状元学习方法分享:学习三十六计
- 因为是程序员,被亲戚鄙视了!
- CF1520D Same Differences
- java page 分页_java Page分页显示
- 【CF 70D】Professor's task