Flutter实现上拉刷新加载

迈不过友情╰ 2022-04-24 02:02 319阅读 0赞

这篇博客主要是实现以下功能

  1. flutter中 ListView的基本使用
  2. 上拉加载刷新
  3. 加载提示

准备

  1. //在配置文件中添加这个库我们会随机生成单词
  2. english_words: ^3.1.0

基础代码

下面这部分代码是死的,我就不介绍了,我们重点介绍_MyInfiniteListViewState这里面的内容

  1. import 'package:english_words/english_words.dart';
  2. import 'package:flutter/material.dart';
  3. void main() => runApp(new InfiniteListView());
  4. class InfiniteListView extends StatelessWidget{
  5. @override
  6. Widget build(BuildContext context) {
  7. // TODO: implement build
  8. return MaterialApp(
  9. title: "InfiniteListView",
  10. home: Scaffold(
  11. appBar: AppBar(
  12. title: Text("InfiniteLisView"),
  13. ),
  14. body: MyInfiniteListView(),
  15. ),
  16. );
  17. }
  18. }
  19. class MyInfiniteListView extends StatefulWidget{
  20. @override
  21. _MyInfiniteListViewState createState() => _MyInfiniteListViewState();
  22. }

主要内容

数据容器
  1. //定义一个结尾的标志和一个初始的数组,数组里面放一个元素防止后面的异常
  2. static const loadingTag = "****loading****";
  3. var _word = <String>[loadingTag];
初始化
  1. @override
  2. void initState() {
  3. //初始化的时候添加20个元素
  4. super.initState();
  5. _retireveData();
  6. }

接下来我们来看这个初始化数据的方法怎么写

  1. /** * 在新的数组中添加20个单词 */
  2. void _retireveData(){
  3. Future.delayed(new Duration(seconds: 2)).then((e)=>{
  4. //_word 是一个数组,因为默认有一个元素,所以这里减一没关系
  5. _word.insertAll(_word.length-1,generateWordPairs().take(20).map((e) => e.asPascalCase).toList()),
  6. //每次生成20个单词
  7. //这里用到了 english_words: ^3.1.0这个框架,调用生成单词的函数
  8. //生成完了20个单词之后就要更新界面了
  9. setState(() {
  10. //重新构建列表,这里不用做什么事,空的调用也会提示程序刷新builder
  11. })
  12. });
  13. }

build中的内容

  1. //我们使用这个构造函数,是因为这个构造函数中带有分割线
  2. ListView.separated()

我们来看一下伪代码

  1. ListView.separated(
  2. //这个是对每个item的构建
  3. itemBuilder: (context,index){
  4. 是否滑动到最后一条
  5. 是: 是否超过100
  6. 是: 显示没有更多数据
  7. 否:显示转圈圈,等待加载数据
  8. 否:构建下一条数据
  9. }
  10. )

基本上就是这个思路了,我们来看下如果超过100条的写法

  1. //超过100条,不在加载更多的数据了,提示用户
  2. return Container(
  3. alignment: Alignment.center,
  4. padding: EdgeInsets.all(16.0),
  5. child: Text("--------我也是有底线的------",
  6. style:TextStyle(color: Colors.grey)),
  7. );

没有超过100条但是滑到底了的写法

  1. //不足100条,继续获取数据
  2. if(_word.length-1<100){
  3. //获取数据
  4. _retireveData();
  5. //加载时显示loading
  6. return Container(
  7. //设置边距
  8. padding: const EdgeInsets.all(16.0),
  9. //居中显示
  10. alignment: Alignment.center,
  11. child: SizedBox(
  12. width: 24.0,
  13. height: 24.0,
  14. //这是一个转圈的progressBar的widget
  15. child: CircularProgressIndicator(strokeWidth: 2.0,),
  16. ),
  17. );

好了 附上完整代码

  1. import 'package:english_words/english_words.dart';
  2. import 'package:flutter/material.dart';
  3. void main() => runApp(new InfiniteListView());
  4. class InfiniteListView extends StatelessWidget{
  5. @override
  6. Widget build(BuildContext context) {
  7. // TODO: implement build
  8. return MaterialApp(
  9. title: "InfiniteListView",
  10. home: Scaffold(
  11. appBar: AppBar(
  12. title: Text("InfiniteLisView"),
  13. ),
  14. body: MyInfiniteListView(),
  15. ),
  16. );
  17. }
  18. }
  19. class MyInfiniteListView extends StatefulWidget{
  20. @override
  21. _MyInfiniteListViewState createState() => _MyInfiniteListViewState();
  22. }
  23. class _MyInfiniteListViewState extends State<MyInfiniteListView>{
  24. static const loadingTag = "****loading****";
  25. var _word = <String>[loadingTag];
  26. @override
  27. void initState() {
  28. //初始化的时候添加20个元素
  29. super.initState();
  30. _retireveData();
  31. }
  32. @override
  33. Widget build(BuildContext context) {
  34. return ListView.separated(
  35. itemBuilder: (context,index){
  36. //如果到了末尾,就需要获取数据了,这里判断的依据就是是否和 ***loading***相等
  37. if(_word[index] == loadingTag){
  38. //不足100条,继续获取数据
  39. if(_word.length-1<100){
  40. //获取数据
  41. _retireveData();
  42. //加载时显示loading
  43. return Container(
  44. padding: const EdgeInsets.all(16.0),
  45. alignment: Alignment.center,
  46. child: SizedBox(
  47. width: 24.0,
  48. height: 24.0,
  49. child: CircularProgressIndicator(strokeWidth: 2.0,),
  50. ),
  51. );
  52. }else{
  53. //超过100条,不在加载更多的数据了,提示用户
  54. return Container(
  55. alignment: Alignment.center,
  56. padding: EdgeInsets.all(16.0),
  57. child: Text("--------我也是有底线的------",
  58. style:TextStyle(color: Colors.grey)),
  59. );
  60. }
  61. }
  62. return ListTile(title: Text("${_word[index]} $index"),);
  63. },
  64. //这里表示的是默认的分割线
  65. separatorBuilder: (context, index)=>Divider(),
  66. itemCount: _word.length);
  67. }
  68. /** * 在新的数组中添加20个单词 */
  69. void _retireveData(){
  70. Future.delayed(new Duration(seconds: 2)).then((e)=>{
  71. //_word 是一个数组,因为默认有一个元素,所以这里减一没关系
  72. _word.insertAll(_word.length-1,generateWordPairs().take(20).map((e) => e.asPascalCase).toList()),
  73. //每次生成20个单词
  74. //这里用到了 english_words: ^3.1.0这个框架,调用生成单词的函数
  75. //生成完了20个单词之后就要更新界面了
  76. setState(() {
  77. //重新构建列表,这里不用做什么事,空的调用也会提示程序刷新builder
  78. })
  79. });
  80. }
  81. }
  82. class MyListView3 extends StatelessWidget{
  83. @override
  84. Widget build(BuildContext context) {
  85. Widget divider1 = new Divider(color: Colors.red);
  86. Widget divider2 = new Divider(color: Colors.blue);
  87. // TODO: implement build
  88. return MaterialApp(
  89. title: "listView",
  90. home: Scaffold(
  91. appBar: AppBar(
  92. title: Text("LsitView"),
  93. ),
  94. body: ListView.separated(
  95. itemBuilder: (BuildContext context, int index){
  96. return ListTile(
  97. title: Text("idnex$index"),
  98. );
  99. },
  100. separatorBuilder:(BuildContext context, int index){
  101. return index%2==0?divider1:divider2;
  102. },
  103. itemCount: 100),
  104. ),
  105. );
  106. }
  107. }
  108. class MyListView extends StatelessWidget{
  109. @override
  110. Widget build(BuildContext context) {
  111. // TODO: implement build
  112. return MaterialApp(
  113. title: "ListViewText",
  114. home: Scaffold(
  115. appBar: AppBar(
  116. title: Text("ListViewText"),
  117. ),
  118. body: ListView.builder(
  119. itemCount: 100,
  120. itemExtent: 50,
  121. itemBuilder: (BuildContext contxt,int index){
  122. return ListTile(
  123. title: Text("index$index"),
  124. );
  125. }
  126. ),
  127. ),
  128. );
  129. }
  130. }
  131. class SingleChildScrollViewTestRoute extends StatelessWidget{
  132. @override
  133. Widget build(BuildContext context) {
  134. String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  135. // TODO: implement build
  136. return MaterialApp(
  137. debugShowCheckedModeBanner: false,
  138. title: "测试",
  139. home: Scaffold(
  140. appBar: AppBar(
  141. title: Text("SingleChildScrollViewTestRoute"),
  142. ),
  143. body: SingleChildScrollView(
  144. padding: EdgeInsets.all(16.0),
  145. child: Center(
  146. child: Column(
  147. //动态的创建一个List<Widget>
  148. children: str.split("").map((e)=>Text(e,textScaleFactor: 2.0,)).toList(),
  149. ),
  150. ),
  151. ),
  152. ),
  153. );
  154. }
  155. }
  156. class ScaffoldRoute extends StatefulWidget {
  157. @override
  158. _ScaffoldRoute createState() {
  159. // TODO: implement createState
  160. return _ScaffoldRoute();
  161. }
  162. }
  163. class _ScaffoldRoute extends State<ScaffoldRoute> with SingleTickerProviderStateMixin{
  164. List tabs = ["新闻", "历史", "图片"];
  165. TabController _tabController;
  166. int _selectedIndex = 1;
  167. @override
  168. void initState() {
  169. // TODO: implement initState
  170. // 创建Controller
  171. _tabController = TabController(length: tabs.length, vsync: this);
  172. super.initState();
  173. }
  174. @override
  175. Widget build(BuildContext context) {
  176. // TODO: implement build
  177. return MaterialApp(
  178. title: "pro",
  179. home: Scaffold(
  180. appBar: AppBar(
  181. //导航栏
  182. title: Text("App Name"),
  183. actions: <Widget>[
  184. IconButton(
  185. icon: Icon(Icons.share),
  186. onPressed: () {
  187. print("点击了");
  188. },
  189. ),
  190. ],
  191. bottom: TabBar(
  192. controller: _tabController,
  193. tabs: tabs.map((e)=>Tab(text: e,)).toList(),
  194. ),
  195. ),
  196. drawer: MyDrawer(),
  197. body:TabBarView(
  198. controller: _tabController,
  199. children:tabs.map((e){
  200. return Container(
  201. alignment: Alignment.center,
  202. child: Text(e, textScaleFactor: 5),
  203. );
  204. }).toList()
  205. ),
  206. bottomNavigationBar: BottomNavigationBar(
  207. items: <BottomNavigationBarItem>[
  208. BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
  209. BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),
  210. BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),
  211. ],
  212. currentIndex: _selectedIndex,
  213. //这个属性表示的是点击后的颜色变化
  214. fixedColor: Colors.blue,
  215. onTap: _onItemTapped,
  216. ),
  217. ),
  218. );
  219. }
  220. void _onItemTapped(int index) {
  221. setState(() {
  222. _selectedIndex = index;
  223. });
  224. }
  225. }
  226. class MyDrawer extends StatelessWidget {
  227. const MyDrawer({
  228. Key key,
  229. }) : super(key: key);
  230. @override
  231. Widget build(BuildContext context) {
  232. // TODO: implement build
  233. return Drawer(
  234. child: MediaQuery.removePadding(context: context,
  235. child:Column(
  236. crossAxisAlignment: CrossAxisAlignment.start,
  237. children: <Widget>[
  238. Padding(
  239. padding: const EdgeInsets.only(top: 28.0),
  240. child: Row(
  241. children: <Widget>[
  242. Padding(
  243. padding: const EdgeInsets.symmetric(horizontal: 16.0),
  244. child: ClipOval(
  245. child: Image.network("https://gss0.bdstatic.com/-4o3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=5ce3474036adcbef15397654cdc645b8/b7fd5266d01609242759dc9fd30735fae6cd3431.jpg",
  246. width: 80,
  247. ),
  248. ),
  249. ),
  250. Text("Wendux",
  251. style: TextStyle(fontWeight: FontWeight.bold),
  252. )
  253. ],
  254. ),
  255. ),
  256. Expanded(
  257. child: ListView(
  258. children: <Widget>[
  259. ListTile(
  260. leading: const Icon(Icons.add),
  261. title: const Text("add account"),
  262. ),
  263. ListTile(
  264. leading: const Icon(Icons.settings),
  265. title: const Text("Manage account"),
  266. ),
  267. ],
  268. ),
  269. ),
  270. ],
  271. )
  272. )
  273. );
  274. }
  275. }
  276. class FlexLayoutTestRoute extends StatelessWidget {
  277. @override
  278. Widget build(BuildContext context) {
  279. return MaterialApp(
  280. title: "test",
  281. home: Scaffold(
  282. appBar: AppBar(
  283. title: Text("test"),
  284. ),
  285. body: MyLayoutTest(),
  286. ),
  287. );
  288. }
  289. }
  290. class MyLayoutTest extends StatelessWidget {
  291. Widget redBox = DecoratedBox(
  292. decoration: BoxDecoration(color: Colors.red),
  293. );
  294. @override
  295. Widget build(BuildContext context) {
  296. // TODO: implement build
  297. return MaterialApp(
  298. title: "Test",
  299. home: Scaffold(
  300. appBar: AppBar(
  301. title: Text("布局测试"),
  302. ),
  303. body: Container(
  304. //容器外补白
  305. margin: EdgeInsets.only(top: 50.0, left: 120.0),
  306. //容器的大小
  307. constraints: BoxConstraints.tight(Size(500, 500)),
  308. decoration: BoxDecoration(
  309. //背景装饰
  310. gradient: RadialGradient(
  311. colors: [Colors.red, Colors.orange],
  312. center: Alignment.topLeft,
  313. radius: 0.98),
  314. boxShadow: [
  315. BoxShadow(
  316. color: Colors.black54,
  317. offset: Offset(2.0, 2.0),
  318. blurRadius: 4.0),
  319. ],
  320. ),
  321. //卡片倾斜式变换
  322. transform: Matrix4.rotationZ(0.2),
  323. //卡片内的文字居中
  324. alignment: Alignment.center,
  325. child: Text(
  326. "薇薇一笑很倾城",
  327. style: TextStyle(color: Colors.white, fontSize: 40.0),
  328. ),
  329. ),
  330. ),
  331. );
  332. }
  333. }
  334. class PaddingTestRoute extends StatelessWidget {
  335. @override
  336. Widget build(BuildContext context) {
  337. return Padding(
  338. //上下左右各添加16像素补白
  339. padding: EdgeInsets.all(16.0),
  340. child: Column(
  341. //显式指定对齐方式为左对齐,排除对齐干扰
  342. crossAxisAlignment: CrossAxisAlignment.start,
  343. children: <Widget>[
  344. Padding(
  345. //左边添加8像素补白
  346. padding: const EdgeInsets.only(left: 28.0),
  347. child: Text("Hello world"),
  348. ),
  349. Padding(
  350. //上下各添加8像素补白
  351. padding: const EdgeInsets.symmetric(vertical: 8.0),
  352. child: Text("I am Jack"),
  353. ),
  354. Padding(
  355. // 分别指定四个方向的补白
  356. padding: const EdgeInsets.fromLTRB(20.0, .0, 20.0, 20.0),
  357. child: Text("Your friend"),
  358. )
  359. ],
  360. ),
  361. );
  362. }
  363. }

发表评论

表情:
评论列表 (有 0 条评论,319人围观)

还没有评论,来说两句吧...

相关阅读