flutter开发实战-类似微博帖子列表及下拉刷新上拉加载效果

本文主要是介绍flutter开发实战-类似微博帖子列表及下拉刷新上拉加载效果,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

flutter开发实战-类似微博帖子列表及下拉刷新上拉加载效果

在之前处理类似微博帖子列表及下拉刷新上拉加载效果,刷新使用的是EasyRefresh

一、引入EasyRefresh与likeButton

在工程的pubspec.yaml中引入插件

    # 下拉刷新、上拉更多easy_refresh: ^3.3.2+1pull_to_refresh: ^2.0.0

需要使用EasyRefreshController来控制处理刷新,初始化

@overridevoid initState() {super.initState();_controller = EasyRefreshController(controlFinishRefresh: true,controlFinishLoad: true,);}@overridevoid dispose() {_controller.dispose();super.dispose();}

需要在onRefresh与onLoad来处理下拉刷新上拉加载数据。

二、类似微博帖子列表及下拉刷新上拉加载效果

类似微博帖子列表,这里定义帖子item,每个帖子中可能包括多张图片。

NoteItem:

class NoteItem {String? feedId;String? coverImageUrl;String? title;String? textContent;String? username;String? avatarUrl;String? time;String? likeNum;bool? liked;String? categoryName;List<NoteImage>? images;
}class NoteImage {String? imageUrl;String? imageWidth;String? imageHeight;
}

定义每个帖子的Widget:NoteListItemWidget,NoteListItemWidget结构是用户头像,描述文本,帖子图片,点赞、评论、分享栏。

NoteListItemWidget:

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_app_demolab/display/note_item.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:like_button/like_button.dart';class NoteListItemWidget extends StatefulWidget {NoteListItemWidget({super.key,required this.noteItem,});final NoteItem noteItem;@override_NoteListItemWidgetState createState() => _NoteListItemWidgetState();
}class _NoteListItemWidgetState extends State<NoteListItemWidget> {@overrideWidget build(BuildContext context) {return Container(margin: EdgeInsets.symmetric(vertical: 5.0, horizontal: 0.0),padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 15.0),//边框设置decoration: new BoxDecoration(//背景color: Color(0xFFFFFFFF),//设置四周圆角 角度 这里的角度应该为 父Container height 的一半borderRadius: BorderRadius.only(topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)),//设置四周边框border: new Border.all(width: 1, color: Color(0xFFf1f1f1)),),child: Column(mainAxisAlignment: MainAxisAlignment.start,crossAxisAlignment: CrossAxisAlignment.start,children: [NoteListAuthorBar(username: widget.noteItem.username,avatarUrl: widget.noteItem.avatarUrl,showTime: widget.noteItem.time),Text(widget.noteItem.textContent??"",maxLines: 5,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 14,fontWeight: FontWeight.normal,color: Color(0xFF666666),),),NoteListImage(images: (widget.noteItem.images != null? widget.noteItem.images: [])),NoteListBottom(liked: widget.noteItem.liked,likeNum: widget.noteItem.likeNum,categoryName: widget.noteItem.categoryName),],),);}
}// 顶部header
class NoteListAuthorBar extends StatelessWidget {const NoteListAuthorBar({super.key,this.username,this.avatarUrl,this.showTime,});final String? username;final String? avatarUrl;final String? showTime;@overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.only(bottom: 10),height: 60,child: Row(mainAxisAlignment: MainAxisAlignment.start,crossAxisAlignment: CrossAxisAlignment.center,children: [CircleAvatar(backgroundImage: NetworkImage(avatarUrl ?? ""),),Padding(padding: EdgeInsets.only(left: 10),),Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.start,children: [Text(username ?? "",maxLines: 1,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold,color: Color(0xFF444444),),),Padding(padding: EdgeInsets.only(top: 5),),Text(showTime ?? "",maxLines: 1,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 12,fontWeight: FontWeight.normal,color: Color(0xFF444444),),),],)],),);}
}// 显示图片
class NoteListImage extends StatelessWidget {const NoteListImage({super.key,this.images,});final List<NoteImage>? images;Widget layoutOfImages(List images, BuildContext context) {double screenWidth = MediaQuery.of(context).size.width;double containW = screenWidth - 30;if (images.length == 1) {NoteImage noteImage = images[0];double imgWidth = 0;double imgHeight = 0;if (noteImage.imageWidth != null &&noteImage.imageWidth!.isNotEmpty &&noteImage.imageHeight != null &&noteImage.imageHeight!.isNotEmpty) {imgWidth = double.parse(noteImage.imageWidth!);imgHeight = double.parse(noteImage.imageHeight!);if (imgWidth > 0 && imgHeight > 0) {if (imgWidth < 1.0 && imgHeight < 1.0) {double maxLen = containW / 2.0;double showW = maxLen;double showH = maxLen;return Container(height: showH,width: showW,child: CachedNetworkImage(imageUrl: noteImage.imageUrl ?? "",placeholder: (context, url) =>Center(child: CupertinoActivityIndicator()),errorWidget: (context, url, error) => Icon(Icons.error),fit: BoxFit.cover,),);} else {double imageScale = imgWidth / imgHeight;double len1_3 = (containW - 20) / 3.0;double maxLen = len1_3 * 2 + 10;double showW = 0;double showH = 0;if (imageScale > 1.0) {// 横图showW = maxLen;showH = showW / imageScale;} else {// 竖图showH = maxLen;showW = imageScale * showH;}return Container(height: showH,width: showW,child: CachedNetworkImage(imageUrl: noteImage.imageUrl??"",placeholder: (context, url) =>Center(child: CupertinoActivityIndicator()),errorWidget: (context, url, error) => Icon(Icons.error),fit: BoxFit.cover,),);}}}} else if (images.length >= 2) {double showImageW = 0;if (images.length == 2 || images.length == 4) {showImageW = (containW - 10) / 2.0;} else {showImageW = (containW - 20) / 3.0;}List<Widget> imageWidgets = [];for (int index = 0; index < images.length; index++) {NoteImage noteImage = images[index];Widget widget = Container(height: showImageW,width: showImageW,child: CachedNetworkImage(imageUrl: noteImage.imageUrl??"",placeholder: (context, url) =>Center(child: CupertinoActivityIndicator()),errorWidget: (context, url, error) => Icon(Icons.error),fit: BoxFit.cover,));imageWidgets.add(widget);}return Container(alignment: Alignment.center,width: containW,child: Wrap(spacing: 8.0,// 主轴(水平)方向间距runSpacing: 8.0,// 纵轴(垂直)方向间距alignment: WrapAlignment.start,//沿主轴方向居中crossAxisAlignment: WrapCrossAlignment.center,children: imageWidgets,),);}return Container();}@overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.symmetric(vertical: 10, horizontal: 0),child: layoutOfImages(this.images??[], context),);}
}// 显示底部
class NoteListBottom extends StatelessWidget {const NoteListBottom({super.key,this.liked,this.likeNum,this.categoryName,});final bool? liked;final String? likeNum;final String? categoryName;@overrideWidget build(BuildContext context) {return Container(height: 60,child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,crossAxisAlignment: CrossAxisAlignment.center,children: [Container(padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),//边框设置decoration: new BoxDecoration(//背景color: Color(0xFFFFFFFF),//设置四周圆角 角度 这里的角度应该为 父Container height 的一半borderRadius: BorderRadius.only(topLeft: Radius.circular(10.0),topRight: Radius.circular(10.0)),//设置四周边框border: new Border.all(width: 1, color: Color(0xFFf1f1f1)),),child: Text(this.categoryName??"",maxLines: 1,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 12,fontWeight: FontWeight.normal,color: Color(0xFF444444),),),),Container(padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),child: Row(mainAxisAlignment: MainAxisAlignment.end,crossAxisAlignment: CrossAxisAlignment.center,children: [LikeButton(likeBuilder: (bool isLiked) {return Icon(Icons.thumb_up,color: (isLiked ? Colors.deepOrange : Color(0xFFA9A9A9)));},likeCount: int.parse(this.likeNum??"0"),isLiked: this.liked,),TextButton(child: Wrap(// 可以通过设置两个基础组件的间距alignment: WrapAlignment.center, //沿主轴方向居中crossAxisAlignment: WrapCrossAlignment.center,spacing: 5,children: [Icon(Icons.comment, color: Color(0xFFA9A9A9)),Text("评论",style:TextStyle(fontSize: 12, color: Color(0xFF888888)))],),onPressed: () {},),TextButton(child: Wrap(// 可以通过设置两个基础组件的间距alignment: WrapAlignment.center, //沿主轴方向居中crossAxisAlignment: WrapCrossAlignment.center,spacing: 5,children: [Icon(Icons.share, color: Color(0xFFA9A9A9)),Text("分享",style:TextStyle(fontSize: 12, color: Color(0xFF888888)))],),onPressed: () {},),],),)],),);}
}

最后,我们在page中处理类似帖子列表DisplayPage

DisplayPage:

import 'dart:math';import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_demolab/display/note_item.dart';
import 'package:flutter_app_demolab/display/note_item_widget.dart';class DisplayPage extends StatefulWidget {const DisplayPage({super.key});@overrideState<DisplayPage> createState() => _DisplayPageState();
}class _DisplayPageState extends State<DisplayPage> {int _count = 10;late EasyRefreshController _controller;bool isLoading = false;ScrollController scrollController = ScrollController();List<NoteItem> list = [];List<NoteImage> noteImages = [];String randomBit() {String scopeF = '0123456789'; //首位String result = '';result = scopeF[Random().nextInt(scopeF.length)];return result;}@overridevoid initState() {initItems();super.initState();_controller = EasyRefreshController(controlFinishRefresh: true,controlFinishLoad: true,);}@overridevoid dispose() {_controller.dispose();super.dispose();}void initItems() {for (int index = 0; index < 12; index++) {NoteImage model = NoteImage();noteImages.add(model);if (index == 0) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202010/11/20201011085349_R2CjU.thumb.1000_0.jpeg";model.imageWidth = "580";model.imageHeight = "880";} else if (index == 1) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202011/15/20201115080806_7ddba.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "580";} else if (index == 2) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202011/15/20201115100436_7ffc6.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "560";} else if (index == 3) {model.imageUrl ="https://c-ssl.duitang.com/uploads/item/202006/09/20200609232715_yvqkd.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "380";} else if (index == 4) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202012/03/20201203144923_425bc.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "430";} else if (index == 5) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202010/11/20201011085351_3d2cS.thumb.1000_0.jpeg";model.imageWidth = "580";model.imageHeight = "850";} else if (index == 6) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202009/28/20200928184639_sxrum.thumb.1000_0.jpeg";model.imageWidth = "580";model.imageHeight = "810";} else if (index == 7) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202012/03/20201203145208_55d8c.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "430";} else if (index == 8) {model.imageUrl ="https://c-ssl.duitang.com/uploads/item/202006/09/20200609232715_pjsin.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "860";} else if (index == 9) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202012/03/20201203144929_cd406.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "450";} else if (index == 10) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202012/03/20201203144705_40405.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "450";} else if (index == 11) {model.imageUrl ="https://c-ssl.duitang.com/uploads/blog/202012/03/20201203144708_d5287.thumb.1000_0.jpg";model.imageWidth = "580";model.imageHeight = "750";}}for (int index = 0; index < 20; index++) {NoteItem model = NoteItem();model.liked = false;list.add(model);if (index % 5 == 0) {model.username = "可可";model.avatarUrl ="https://c-ssl.duitang.com/uploads/item/202007/22/20200722212206_ifscm.thumb.1000_0.jpg";model.time = "08/01 13:51";model.textContent = "等轮到你讲话时再说,认真聆听对方的谈话,千万别打断对方讲话,耐心等着,轮到你时再讲。";model.likeNum = "861380";model.categoryName = "兽圈";} else if (index % 5 == 2) {model.username = "琪琪";model.avatarUrl ="https://c-ssl.duitang.com/uploads/item/202007/22/20200722212209_insvm.thumb.1000_0.png";model.time = "08/01 13:51";model.textContent = "记得有活动,在活动现场等你哦~";model.likeNum = "50999";model.categoryName = "娃圈";} else if (index % 5 == 4) {model.username = "悦悦";model.avatarUrl ="https://c-ssl.duitang.com/uploads/item/202007/19/20200719102754_jrcoe.thumb.1000_0.jpg";model.time = "08/01 13:51";model.textContent = "说一说你想要的形象模型,晒一晒你的喜欢";model.likeNum = "6150";model.categoryName = "Pia戏圈";} else {model.username = "爱丽多啦";model.avatarUrl ="https://c-ssl.duitang.com/uploads/item/202007/19/20200719102756_ihfku.thumb.1000_0.jpg";model.time = "08/01 13:51";model.textContent = "你永远不懂我喜欢的东西,555";model.likeNum = "1507";model.categoryName = "语c圈";}List<NoteImage> tmpImages = [];String randomf = randomBit();String randomt = randomBit();int f = int.parse(randomf);int t = int.parse(randomt);print("f:$f");print("t:$t");if (f < t) {tmpImages.addAll(noteImages.sublist(f, t));} else if (f > t) {tmpImages.addAll(noteImages.sublist(t, f));} else {tmpImages.addAll(noteImages.sublist(t, t+1));}model.images = tmpImages;}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('EasyRefresh'),),body: EasyRefresh(controller: _controller,header: const ClassicHeader(),footer: const ClassicFooter(),onRefresh: () async {await Future.delayed(const Duration(seconds: 4));if (!mounted) {return;}list = [];initItems();setState(() {_count = 10;});_controller.finishRefresh();_controller.resetFooter();},onLoad: () async {await Future.delayed(const Duration(seconds: 4));if (!mounted) {return;}initItems();setState(() {_count += 5;});_controller.finishLoad(_count >= 200 ? IndicatorResult.noMore : IndicatorResult.success);},child: ListView.builder(itemBuilder: (context, index) {return NoteListItemWidget(noteItem: this.list[index]);},itemCount: this.list.length,),),);}
}

效果图如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、小结

flutter开发实战-类似微博帖子列表及下拉刷新上拉加载效果

学习记录,每天不停进步。

这篇关于flutter开发实战-类似微博帖子列表及下拉刷新上拉加载效果的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1016504

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

防近视护眼台灯什么牌子好?五款防近视效果好的护眼台灯推荐

在家里,灯具是属于离不开的家具,每个大大小小的地方都需要的照亮,所以一盏好灯是必不可少的,每个发挥着作用。而护眼台灯就起了一个保护眼睛,预防近视的作用。可以保护我们在学习,阅读的时候提供一个合适的光线环境,保护我们的眼睛。防近视护眼台灯什么牌子好?那我们怎么选择一个优秀的护眼台灯也是很重要,才能起到最大的护眼效果。下面五款防近视效果好的护眼台灯推荐: 一:六个推荐防近视效果好的护眼台灯的

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta