网站服务器有哪几种咸阳市网站建设公司

张小明 2026/1/3 3:30:00
网站服务器有哪几种,咸阳市网站建设公司,精品网站建设电话,asp简单购物网站源码在 Flutter 开发中#xff0c;分页列表#xff08;下拉刷新、上拉加载#xff09;是数据展示的核心场景。原生 RefreshIndicator 仅支持下拉刷新#xff0c;上拉加载需手动实现#xff0c;且缺乏空状态、错误状态统一处理。本文封装的 RefreshListWidget 整合 “下拉刷新 …在 Flutter 开发中分页列表下拉刷新、上拉加载是数据展示的核心场景。原生RefreshIndicator仅支持下拉刷新上拉加载需手动实现且缺乏空状态、错误状态统一处理。本文封装的RefreshListWidget整合 “下拉刷新 上拉加载 空态展示 错误重试” 四大核心能力支持分页逻辑、样式自定义一行代码即可集成适配 90% 列表数据展示场景。一、核心优势精准解决开发痛点分页逻辑内置统一封装下拉刷新重置数据、上拉加载加载下一页逻辑无需外部手动管理页码与状态状态全覆盖支持加载中、空数据、加载失败、无更多数据四种状态自动切换无需额外判断样式全自定义刷新指示器、加载更多组件、空态组件、错误组件均可自定义贴合 APP 设计风格交互体验优化上拉加载时禁止重复请求滑动到底部自动加载下拉刷新支持自定义动画低侵入高复用仅需传入数据源回调与列表项构建方法无需关心状态管理统一项目列表样式二、核心配置速览关键参数一目了然配置分类核心参数核心作用必选配置fetchData: FutureListT、itemBuilder: Widget Function(T, int)数据请求回调返回下一页数据、列表项构建器分页配置pageSize、initialPage、hasMore每页条数、初始页码、是否有更多数据样式配置refreshIndicatorColor、loadMoreWidget、emptyWidget、errorWidget刷新指示器颜色、加载更多组件、空态组件、错误组件交互配置enablePullDown、enablePullUp、onRefresh、onLoadMore启用下拉刷新、启用上拉加载、刷新回调、加载回调适配配置adaptDarkMode、padding、physics深色模式适配、内边距、滚动物理效果三、生产级完整代码可直接复制开箱即用dartimport package:flutter/material.dart; /// 列表加载状态枚举 enum ListLoadStatus { idle, // 空闲状态 loading, // 加载中 empty, // 空数据 error, // 加载失败 noMore, // 无更多数据 } /// 通用下拉刷新上拉加载列表组件 class RefreshListWidgetT extends StatefulWidget { // 必选参数 final FutureListT Function(int page, int pageSize) fetchData; // 数据请求回调参数页码、每页条数 final Widget Function(T data, int index) itemBuilder; // 列表项构建器参数数据、索引 // 分页配置 final int pageSize; // 每页条数默认10 final int initialPage; // 初始页码默认1 final bool Function(ListT data)? hasMore; // 是否有更多数据默认返回数据长度 pageSize // 样式配置 final Color refreshIndicatorColor; // 下拉刷新指示器颜色默认蓝色 final Widget? loadMoreWidget; // 上拉加载组件 final Widget? emptyWidget; // 空数据组件 final Widget? errorWidget; // 加载失败组件 final double itemSpacing; // 列表项间距默认0 final EdgeInsetsGeometry padding; // 列表内边距默认无 // 交互配置 final bool enablePullDown; // 是否启用下拉刷新默认true final bool enablePullUp; // 是否启用上拉加载默认true final Duration refreshDuration; // 刷新动画时长默认300ms final ScrollPhysics? physics; // 滚动物理效果默认BouncingScrollPhysics // 适配配置 final bool adaptDarkMode; // 适配深色模式默认true final bool shrinkWrap; // 是否自适应高度默认false final Axis scrollDirection; // 滚动方向默认垂直 const RefreshListWidget({ super.key, required this.fetchData, required this.itemBuilder, // 分页配置 this.pageSize 10, this.initialPage 1, this.hasMore, // 样式配置 this.refreshIndicatorColor Colors.blue, this.loadMoreWidget, this.emptyWidget, this.errorWidget, this.itemSpacing 0.0, this.padding EdgeInsets.zero, // 交互配置 this.enablePullDown true, this.enablePullUp true, this.refreshDuration const Duration(milliseconds: 300), this.physics, // 适配配置 this.adaptDarkMode true, this.shrinkWrap false, this.scrollDirection Axis.vertical, }); override StateRefreshListWidgetT createState() _RefreshListWidgetStateT(); } class _RefreshListWidgetStateT extends StateRefreshListWidgetT { late ListT _dataList; late int _currentPage; late ListLoadStatus _loadStatus; bool _isLoading false; // 防止重复请求 override void initState() { super.initState(); _dataList []; _currentPage widget.initialPage; _loadStatus ListLoadStatus.idle; // 初始化加载第一页数据 _fetchData(_currentPage, isRefresh: false); } /// 数据请求逻辑 Futurevoid _fetchData(int page, {required bool isRefresh}) async { if (_isLoading) return; setState(() { _isLoading true; if (isRefresh) { _loadStatus ListLoadStatus.loading; } else if (_loadStatus ! ListLoadStatus.loading) { _loadStatus ListLoadStatus.loading; } }); try { final data await widget.fetchData(page, widget.pageSize); setState(() { if (isRefresh) { // 下拉刷新重置数据 _dataList data; _currentPage widget.initialPage; } else { // 上拉加载追加数据 _dataList.addAll(data); } // 判断是否有更多数据 final hasMore widget.hasMore?.call(data) ?? (data.length widget.pageSize); if (_dataList.isEmpty) { _loadStatus ListLoadStatus.empty; } else if (!hasMore) { _loadStatus ListLoadStatus.noMore; } else { _loadStatus ListLoadStatus.idle; _currentPage; } }); } catch (e) { setState(() { _loadStatus _dataList.isEmpty ? ListLoadStatus.error : _loadStatus; debugPrint(列表加载失败$e); }); } finally { setState(() _isLoading false); } } /// 下拉刷新回调 Futurevoid _onRefresh() async { await _fetchData(widget.initialPage, isRefresh: true); await Future.delayed(widget.refreshDuration); // 保证刷新动画完整 } /// 上拉加载回调滑动到底部触发 void _onScrollNotification(ScrollNotification notification) { if (!widget.enablePullUp || _loadStatus ! ListLoadStatus.idle || _isLoading) return; final metrics notification.metrics; if (metrics.pixels metrics.maxScrollExtent - 200) { // 提前200px触发加载 _fetchData(_currentPage, isRefresh: false); } } /// 构建加载更多组件 Widget _buildLoadMoreWidget() { if (_loadStatus ! ListLoadStatus.loading || !widget.enablePullUp) return const SizedBox.shrink(); return widget.loadMoreWidget ?? Container( height: 60, alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const CircularProgressIndicator(strokeWidth: 2), const SizedBox(width: 8), Text( 加载中..., style: TextStyle( fontSize: 14, color: _adaptDarkMode(const Color(0xFF999999), const Color(0xFF777777)), ), ), ], ), ); } /// 构建空数据组件 Widget _buildEmptyWidget() { if (_loadStatus ! ListLoadStatus.empty) return const SizedBox.shrink(); return widget.emptyWidget ?? Container( height: MediaQuery.of(context).size.height - 200, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.inbox_outlined, size: 64, color: _adaptDarkMode(const Color(0xFFE0E0E0), const Color(0xFF444444)), ), const SizedBox(height: 16), Text( 暂无相关数据, style: TextStyle( fontSize: 16, color: _adaptDarkMode(const Color(0xFF666666), const Color(0xFF999999)), ), ), const SizedBox(height: 8), TextButton( onPressed: () _fetchData(widget.initialPage, isRefresh: true), child: Text( 点击重试, style: TextStyle(color: _adaptDarkMode(Colors.blue, Colors.blueAccent)), ), ), ], ), ); } /// 构建加载失败组件 Widget _buildErrorWidget() { if (_loadStatus ! ListLoadStatus.error) return const SizedBox.shrink(); return widget.errorWidget ?? Container( height: MediaQuery.of(context).size.height - 200, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outlined, size: 64, color: _adaptDarkMode(Colors.redAccent, const Color(0xFFE57373)), ), const SizedBox(height: 16), Text( 加载失败请重试, style: TextStyle( fontSize: 16, color: _adaptDarkMode(const Color(0xFF666666), const Color(0xFF999999)), ), ), const SizedBox(height: 8), TextButton( onPressed: () _fetchData(widget.initialPage, isRefresh: true), child: Text( 重新加载, style: TextStyle(color: _adaptDarkMode(Colors.blue, Colors.blueAccent)), ), ), ], ), ); } /// 构建无更多数据组件 Widget _buildNoMoreWidget() { if (_loadStatus ! ListLoadStatus.noMore || !widget.enablePullUp) return const SizedBox.shrink(); return Container( height: 40, alignment: Alignment.center, child: Text( 没有更多数据了, style: TextStyle( fontSize: 14, color: _adaptDarkMode(const Color(0xFF999999), const Color(0xFF777777)), ), ), ); } /// 深色模式颜色适配 Color _adaptDarkMode(Color lightColor, Color darkColor) { if (!widget.adaptDarkMode) return lightColor; return MediaQuery.platformBrightnessOf(context) Brightness.dark ? darkColor : lightColor; } override Widget build(BuildContext context) { // 构建列表项 final listItems List.generate(_dataList.length, (index) { final item widget.itemBuilder(_dataList[index], index); return Padding( padding: EdgeInsets.only(bottom: index _dataList.length - 1 ? 0 : widget.itemSpacing), child: item, ); }); // 列表主体包含下拉刷新、上拉加载、状态组件 final listBody ListView( shrinkWrap: widget.shrinkWrap, physics: widget.physics ?? const BouncingScrollPhysics(), scrollDirection: widget.scrollDirection, padding: widget.padding, children: [ ...listItems, _buildLoadMoreWidget(), _buildNoMoreWidget(), ], ); // 包裹下拉刷新指示器 final refreshBody widget.enablePullDown ? RefreshIndicator( color: widget.refreshIndicatorColor, onRefresh: _onRefresh, child: listBody, ) : listBody; // 叠加状态组件空态/错误态 return Stack( children: [ refreshBody, _buildEmptyWidget(), _buildErrorWidget(), ], ); } }四、三大高频场景落地示例直接复制到项目可用场景 1普通分页列表商品列表 - 下拉刷新上拉加载适用场景商品列表、资讯列表、用户列表等普通分页场景dart// 商品列表页面 class GoodsListPage extends StatelessWidget { override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(商品列表)), body: RefreshListWidgetGoodsModel( pageSize: 15, fetchData: (page, pageSize) async { // 实际业务调用分页接口 final response await GoodsApi.getGoodsList(page: page, pageSize: pageSize); return response.data; // 返回ListGoodsModel }, itemBuilder: (goods, index) { // 构建商品列表项 return GoodsListItem( goods: goods, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) GoodsDetailPage(goods: goods)), ), ); }, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), itemSpacing: 12, refreshIndicatorColor: Colors.orangeAccent, adaptDarkMode: true, ), ); } } // 商品列表项组件示例 class GoodsListItem extends StatelessWidget { final GoodsModel goods; final VoidCallback onTap; const GoodsListItem({super.key, required this.goods, required this.onTap}); override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Card( elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(goods.name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)), const SizedBox(height: 4), Text(¥${goods.price}, style: const TextStyle(color: Colors.redAccent, fontSize: 15)), ], ), ), ), ); } }场景 2空态与错误处理资讯列表 - 无数据 / 加载失败适用场景需要特殊处理空数据、加载失败的列表场景dart// 资讯列表页面 RefreshListWidgetNewsModel( fetchData: (page, pageSize) async { // 模拟接口请求 await Future.delayed(const Duration(milliseconds: 800)); // 模拟空数据场景page1时返回空 if (page 1) return []; // 模拟正常数据 return List.generate(pageSize, (index) NewsModel(title: 资讯标题 ${page * pageSize index})); }, itemBuilder: (news, index) { return ListTile( title: Text(news.title), trailing: const Icon(Icons.arrow_forward_ios, size: 16), onTap: () Navigator.push(context, MaterialPageRoute(builder: (context) NewsDetailPage(news: news))), ); }, // 自定义空态组件 emptyWidget: Container( height: MediaQuery.of(context).size.height - 200, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset(assets/images/no_news.png, width: 120, height: 120), const SizedBox(height: 16), const Text(暂无资讯, style: TextStyle(fontSize: 16, color: Color(0xFF666666))), const SizedBox(height: 8), TextButton( onPressed: () debugPrint(刷新资讯), child: const Text(刷新一下, style: TextStyle(color: Colors.blue)), ), ], ), ), // 自定义错误组件 errorWidget: Container( height: MediaQuery.of(context).size.height - 200, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.wifi_off_outlined, size: 64, color: Colors.orangeAccent), const SizedBox(height: 16), const Text(网络异常加载失败, style: TextStyle(fontSize: 16, color: Color(0xFF666666))), const SizedBox(height: 8), TextButton( onPressed: () debugPrint(重新加载资讯), child: const Text(检查网络并重试, style: TextStyle(color: Colors.orangeAccent)), ), ], ), ), enablePullUp: false, // 关闭上拉加载 padding: const EdgeInsets.symmetric(horizontal: 16), );场景 3自定义加载更多组件聊天记录 - 倒序加载适用场景聊天记录、历史记录等需要自定义加载样式的场景dart// 聊天记录列表倒序加载最新消息在底部 RefreshListWidgetMessageModel( pageSize: 20, initialPage: 1, fetchData: (page, pageSize) async { // 实际业务调用聊天记录分页接口倒序分页 final response await MessageApi.getHistoryMessages(page: page, pageSize: pageSize, chatId: 123); return response.data; // 返回ListMessageModel }, itemBuilder: (message, index) { // 构建聊天消息项左侧他人消息右侧自己消息 return MessageItem( message: message, isSelf: message.senderId myId, ); }, enablePullDown: false, // 关闭下拉刷新倒序列表无需下拉 enablePullUp: true, // 上拉加载更早的消息 // 自定义加载更多组件 loadMoreWidget: Container( height: 40, alignment: Alignment.center, child: const Text(加载更早的消息..., style: TextStyle(fontSize: 13, color: Color(0xFF999999))), ), padding: const EdgeInsets.symmetric(vertical: 8), itemSpacing: 8, physics: const ClampingScrollPhysics(), // 禁止弹性滚动 adaptDarkMode: true, );五、核心封装技巧复用成熟设计思路分页逻辑统一内置页码管理、数据追加 / 重置逻辑外部仅需传入数据请求回调无需关心分页细节状态自动切换通过ListLoadStatus枚举管理四种核心状态根据数据请求结果自动切换无需外部手动判断防止重复请求通过_isLoading标志位禁止加载中时重复请求避免接口压力与数据混乱组件插槽化加载更多、空态、错误态组件支持自定义兼顾通用性与个性化适配不同设计需求交互细节优化上拉加载提前 200px 触发提升用户体验下拉刷新保证动画完整避免视觉卡顿六、避坑指南解决 90% 开发痛点数据请求规范fetchData回调需返回FutureListT分页逻辑需与后端一致页码从 1 开始或 0 开始hasMore 配置默认通过 “返回数据长度 pageSize” 判断是否有更多数据若后端返回数据不足一页但仍有更多数据需自定义hasMore回调列表项高度列表项高度建议固定或自适应避免动态高度导致的滚动抖动长文本建议限制行数空态与错误态空态组件、错误态组件需设置足够高度如屏幕高度 - 导航栏高度确保居中显示性能优化列表项建议使用const构造函数静态内容避免频繁重建数据量较大时建议使用ListView.builder而非List.generate倒序列表注意倒序列表如聊天记录需关闭下拉刷新、启用上拉加载且数据追加后需滚动到底部可通过ScrollController实现https://openharmonycrossplatform.csdn.net/content
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

seo网站优化详解导航网站容易做吗

随着智能化生产理念的深入,主打复杂结构成型、高精度高效率的金属3D打印已经深度渗透进鞋业、医疗、汽车工业、航空航天等多个制造领域。与此同时,随着应用领域的不断拓展,金属3d打印质量的重要性已经远远超过“标准合格”,成为企…

张小明 2025/12/27 16:42:07 网站建设

做网站要sql 数据库名字账号密码京创影视app

Kotaemon本地化部署指南:保障数据安全的最佳实践 在金融、医疗、法律等行业,一个共同的挑战正日益凸显:如何在享受大语言模型(LLM)带来的智能对话能力的同时,确保敏感信息不被泄露?当员工询问“…

张小明 2026/1/2 13:05:36 网站建设

湖南养老院中企动力网站建设网络建设文章网站

理工学院纳米沉浸式实验室沉浸式实验室是理工学院的多学科空间,旨在可视化复杂数据并开发沉浸式技术原型。它为科学、工程和艺术领域的用户提供增强现实和虚拟现实研究、动作捕捉以及数字物理交互方面的支持。外科手术训练挑战现代神经外科技术对精准度要求极高&…

张小明 2025/12/27 16:42:03 网站建设

凉山州住房和城乡建设厅网站电脑培训网上课程

JavaScript 作为现代 Web 开发的核心语言,几乎无处不在——从简单的前端交互到复杂的 Node.js 后端应用。然而,正是这种广泛的应用使 JavaScript 成为攻击者的主要目标。本文旨在为开发者提供 10 个关键的安全编码实践,帮助构建更安全的 Java…

张小明 2025/12/28 9:16:05 网站建设

wordpress 如何回到初始化seo技巧优化

Unity LipSync口型同步技术深度解析与实战应用 【免费下载链接】LipSync LipSync for Unity3D 根据语音生成口型动画 支持fmod 项目地址: https://gitcode.com/gh_mirrors/lip/LipSync 在游戏开发领域,角色对话时的口型同步一直是提升沉浸感的关键技术难点。…

张小明 2025/12/28 9:15:53 网站建设

网站备案和域名备案一样吗广州市建筑工程有限公司

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 创建一个MySQL视图性能对比工具,自动生成相同功能的视图实现和原生SQL实现,并执行EXPLAIN分析。要求包含:简单查询、多表JOIN、子查询、聚合查询…

张小明 2025/12/28 9:15:48 网站建设