Flutter下拉刷新上拉加载的简单实现方式一

news2024/11/5 13:06:47

方式一:RefreshIndicator+ListView实现

import 'package:flutter/material.dart';

class SimpleRefreshDemoPage extends StatefulWidget {
  const SimpleRefreshDemoPage({super.key});

  @override
  State<StatefulWidget> createState() {
    return _SimpleRefreshDemoPage();
  }
}

class _SimpleRefreshDemoPage extends State<SimpleRefreshDemoPage> {
  final int pageSize = 50;

  bool disposed = false;

  List<String> dataList = [];

  final ScrollController _scrollController = ScrollController();

  final GlobalKey<RefreshIndicatorState> refreshKey = GlobalKey();

  Future<void> onRefresh() async {
    await Future.delayed(const Duration(seconds: 4));
    dataList.clear();
    for (int i = 0; i < pageSize; i++) {
      dataList.add("refresh");
    }

    if (disposed) {
      return;
    }
    setState(() {});
  }

  Future<void> loadMore() async {
    await Future.delayed(const Duration(seconds: 4));
    for (int i = 0; i < pageSize; i++) {
      dataList.add("loadmore");
    }
    if (disposed) {
      return;
    }
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        loadMore();
      }
    });

    Future.delayed(const Duration(seconds: 0), () {
      refreshKey.currentState?.show();
    });
  }

  @override
  void dispose() {
    disposed = true;
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("SimpleRefreshDemoPage"),
      ),
      body: RefreshIndicator(
        key: refreshKey,
        onRefresh: onRefresh,
        child: ListView.builder(
          physics: const AlwaysScrollableScrollPhysics(),
          itemBuilder: (context, index) {
            if (index == dataList.length) {
              return Container(
                margin: const EdgeInsets.all(10),
                child: const Align(
                  child: CircularProgressIndicator(),
                ),
              );
            }
            return Card(
              child: Container(
                height: 60,
                alignment: Alignment.centerLeft,
                child: Text("Item ${dataList[index]} $index"),
              ),
            );
          },
          itemCount: (dataList.length >= pageSize)
              ? dataList.length + 1
              : dataList.length,
          controller: _scrollController,
        ),
      ),
    );
  }
}

 如何实现下拉刷新       

 RefreshIndicator

        RefreshIndicator 是 Flutter 中用于实现下拉刷新功能的一个组件。RefreshIndicator 包裹一个可滚动的子组件,如 `ListView` 或 `GridView`,当用户下拉到列表顶部时,会触发刷新操作。上面的例子中RefreshIndicator包裹在了ListView。

        `onRefresh` 是一个返回 `Future` 的异步函数,用于执行刷新操作。

        关键属性

  • onRefresh`: 必须实现的回调函数,定义刷新时的操作。
  • `child`: 需要包裹的可滚动子组件。
  • `color`: 刷新指示器的进度条颜色。
  • `backgroundColor`: 刷新指示器的背景色。
  • `displacement`: 指示器开始显示时与顶部的距离。

如何实现下拉加载

ScrollController

   _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        loadMore();
      }
    });

如何检测用户是否滚动到了 `ScrollView` 的底部?

_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent

_scrollController.position.pixels` :表示当前滚动的位置。

`_scrollController.position.maxScrollExtent` :表示可滚动区域的最大值。

上面两个值相等时,说明用户已经滚动到了底部。

`bool disposed` 字段作用

        在上面的代码中,`bool disposed` 字段用于指示 `State` 对象是否已经被销毁。当 `dispose` 方法被调用时,`disposed` 被设置为 `true`。这个字段主要用于在异步操作完成后,确保不会调用已经被销毁的 `State` 对象的 `setState` 方法。

作用分析

1.防止在销毁后调用 `setState`:
  • 异步操作(如 `Future.delayed`)在完成后,可能会尝试调用 `setState` 来更新 UI。然而,如果在异步操作执行期间,用户已经导航离开了当前页面,导致 `State` 对象被销毁,那么调用 `setState` 就会抛出异常,因为 `State` 已经不再是活动的。
  • 通过检查 `disposed` 字段,可以在调用 `setState` 前确认 `State` 是否仍然有效,从而避免在组件销毁后对其进行不必要的更新。
2.提高代码的稳健性:
  • 这种模式是一种防御性编程的方法,确保应用程序在面对不确定的异步执行时仍然是稳定的。
 @override
  void dispose() {
    disposed = true;
    super.dispose();
  }

  

  Future<void> onRefresh() async {
    await Future.delayed(const Duration(seconds: 4));
    dataList.clear();
    for (int i = 0; i < pageSize; i++) {
      dataList.add("refresh");
    }

    if (disposed) {
      return;
    }
    setState(() {});
  }

  Future<void> loadMore() async {
    await Future.delayed(const Duration(seconds: 4));
    for (int i = 0; i < pageSize; i++) {
      dataList.add("loadmore");
    }
    if (disposed) {
      return;
    }
    setState(() {});
  }

在 `onRefresh` 和 `loadMore` 方法中,`disposed` 被用来检查 `State` 是否已经被销毁。如果 `disposed` 为 `true`,则不再调用 `setState`,从而避免可能的异常。


3.总结


使用 `disposed` 字段可以有效地防止在组件销毁后进行不必要或有害的 UI 更新。这种模式特别适用于涉及异步操作的 Flutter 应用开发,确保代码在处理 `State` 生命周期时更加健壮。

        在 Flutter 中,`dispose` 方法是一个非常重要的生命周期方法,用于在不再需要一个对象时释放资源。通常,`dispose` 方法用于清理那些可能导致内存泄漏的资源,如控制器、监听器和订阅等。

 `dispose` 方法的常规用途

`dispose` 方法在 `StatefulWidget` 的状态对象中被重写。它在以下情况下被调用:

  • Widget 被从树中永久移除时。
  • 需要释放资源以避免内存泄漏时。

`dispose` 方法的典型用法

1.释放控制器:
  • 常用于释放 `AnimationController`、`TextEditingController`、`PageController` 等控制器。
2.取消订阅:
  • 用于取消流(stream)的订阅,以防止内存泄漏。
3.移除监听器:
  • 移除添加到某些对象上的监听器,例如 `ScrollController` 的监听器。

重要注意事项

  • 调用 `super.dispose()`:在重写 `dispose` 方法时,确保在最后调用 `super.dispose()`。这会调用父类的 `dispose` 方法,完成必要的清理。
  • 确保资源释放:所有在 `initState` 或其他地方创建的需要手动管理的资源,都应在 `dispose` 中被正确释放。
  • 流的取消:对于任何流的订阅,一定要在 `dispose` 中调用 `cancel` 方法。

通过正确使用 `dispose` 方法,你可以确保你的应用程序以更高效的方式管理内存,避免可能的内存泄漏问题。

       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2233582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaEE初阶---servlet篇(二)(smartTomcat的使用相关错误类型)

文章目录 1.servlet创建项目问题说明2.SamrtTomcat插件3.乱码问题的说明4.其他的错误类型说明4.1常见错误之4044.2常见错误之4054.3常见错误之5004.4空白页面/无法访问 1.servlet创建项目问题说明 servlet进行这个项目创建的时候&#xff0c;我们的这个web.xml里面的这个内容就…

Centos 网络接口打vlan标签

Centos 网络接口打vlan标签 本次使用给bond打vlan标签&#xff0c;其实其他普通接口也一样 Centos创建bond前需要关闭NetworkManager [root192 network-scripts]# systemctl disable NetworkManager --now Removed symlink /etc/systemd/system/multi-user.target.wants/Netwo…

使用Docker-Compose安装redis,rabbitmq,nacos,mysql,nginx,tomcat,portainer组件教程

因为开发经常会用到一些组件&#xff0c;又不想在本地启动&#xff0c;所以买了个服务器&#xff0c;然后将这些组件都安装到服务器上以便开发使用。下面就记录下使用docker-compose安装组件的教程以及一些需要注意的地方。 关于docker和docker-compose的安装在另一篇博客中有…

WPF+MVVM案例实战(二十二)- 制作一个侧边弹窗栏(CD类)

文章目录 1、案例效果1、侧边栏分类2、CD类侧边弹窗实现1、样式代码实现2、功能代码实现3 运行效果4、源代码获取1、案例效果 1、侧边栏分类 A类 :左侧弹出侧边栏B类 :右侧弹出侧边栏C类 :顶部弹出侧边栏D类 :底部弹出侧边栏2、CD类侧边弹窗实现 1、样式代码实现 在原有的…

解决 “Error: listen EACCES: permission denied 0.0.0.0:80“ 错误

前言 在开发过程中&#xff0c;我们经常会遇到各种各样的错误。其中一个常见的错误是 Error: listen EACCES: permission denied 0.0.0.0:80。这个错误通常发生在尝试启动一个开发服务器时&#xff0c;服务器试图绑定到80端口&#xff0c;但由于权限不足而失败。本文将详细介绍…

国产之光-海豚调度器的入门知识篇

目录 概念和定义 核心特性 核心组件 工作流程 环境准备 系统软件方面 硬件方面 部署方式 单机模式 伪集群模式 集群模式 基本配置 工作流定义 调度管理 定时调度 手动调度 监控告警 任务类型扩展 资源管理 权限控制 概念和定义 Apache DolphinScheduler是一…

使用Netty实现一个简单的聊天服务器

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;Netty &#x1f96d;本文内容&a…

新世联科技:NG2-A-7在DAC空气捕集提取CO2的应用

一、DAC空气捕集提取CO2的介绍 直接空气碳捕获&#xff08;Direct Air Capture&#xff0c;简称DAC&#xff09;是一种直接从大气中提取二氧化碳的技术。 二、DAC空气捕集提取CO2的前景 从大气中提取的这种二氧化碳可以作为循环经济的一部分以各种不同方式使用。未来&#xf…

ISUP协议视频平台EasyCVR视频融合平台接入各类摄像机的方法

安防视频监控ISUP协议视频平台EasyCVR兼容性强、支持灵活拓展&#xff0c;平台可提供视频远程监控、录像、存储与回放、视频转码、视频快照、告警、云台控制、语音对讲、平台级联等视频能力。 想要将摄像机顺利接入EasyCVR平台&#xff0c;实现视频监控的集中管理和分发&#x…

(五)Spark大数据开发实战:灵活运用PySpark常用DataFrame API

目录 一、PySpark 二、数据介绍 三、PySpark大数据开发实战 1、数据文件上传HDFS 2、导入模块及数据 3、数据统计与分析 ①、计算演员参演电影数 ②、依次罗列电影番位前十的演员 ③、按照番位计算演员参演电影数 ④、求每位演员所有参演电影中的最早、最晚上映时间及…

达梦数据库宕机问题分析及处理

官方宕机原因排查 官方故障诊断排除 相关概念 达梦数据库宕机往往会产生core文件&#xff0c;解读core文件是分析宕机原因的主要手段&#xff0c;类似oracle的diag.trc或system dump转储文件&#xff0c;记录数据库线程状态、sql语句等。 首选的排查方向可以从内存溢出、磁盘…

spring ai 入门 之 结构化输出 - 把大模型llm返回的内容转换成java bean

目录 ​编辑 将AI非结构化文本转换为特定格式数据的应用场景说明 Spring AI 介绍 &#xff1a;为Java开发者打造的AI应用开发框架 Qwen 介绍 &#xff1a; 一个国内领先的开源大模型 Spring AI Alibaba框架介绍 &#xff1a; 一个国内最好的spring ai实现 使用spring ai …

HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac

寻找模拟器 背景&#xff1a; 运行的是h5&#xff0c;模拟器是网易MuMu。 首先检查一下是否配置dab环境&#xff0c;adb version 配置一下hbuilderX的adb&#xff1a; 将命令输出的路径配置到hbuilderx里面去&#xff0c;然后重启下HbuilderX。 开始安装基座…一直安装不…

使用Docker Compose构建多容器应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Docker Compose构建多容器应用 引言 Docker Compose 简介 安装 Docker Compose 创建基本配置 运行多容器应用 查看服务状态 …

react-router与react-router-dom的区别

写法上的区别&#xff1a; 写法1: import {Swtich, Route, Router, HashHistory, Link} from react-router-dom;写法2: import {Switch, Route, Router} from react-router; import {HashHistory, Link} from react-router-dom;react-router实现了路由的核心功能 react-router-…

Python 字符串类型中 ``split(“\n“)`` 与 ``splitlines()`` 方法的一些区别

最近在以 self.__print("#" * 20 "\n") 调用自己写的 __print 接口时发现打印的时候 "\n" 没有打出来&#xff0c;进而发现了 split("\n") 与 splitlines() 方法的一些区别。 一个是参数上&#xff0c;split 需要传递一个字符串作为…

Java Iterator 实现杨辉三角

一、问题描述 杨辉三角定义如下&#xff1a; 1/ \1 1/ \ / \1 2 1/ \ / \ / \1 3 3 1/ \ / \ / \ / \1 4 6 4 1/ \ / \ / \ / \ / \ 1 5 10 10 5 1 把每一行看做一个list&#xff0c;试写一个 Iterator&#xff0c;不断输出下一行的 list&#xf…

Spark 的介绍与搭建:从理论到实践

目录 一、分布式的思想 &#xff08;一&#xff09;存储 &#xff08;二&#xff09;计算 二、Spark 简介 &#xff08;一&#xff09;发展历程 &#xff08;二&#xff09;Spark 能做什么&#xff1f; &#xff08;三&#xff09;spark 的组成部分 &#xff08;四&…

Linux操作系统 ------(3.文本编译器Vim)

目录 1.前言 2.本章学习目标 3.vim的三种工作模式 3.1一般模式‌ 3.2编辑模式‌ 3.3命令行模式‌ 4.运行vim 5.vim 不同工作模式下的常见命令 6.一般模式下的功能键 6.1移动光标类 6.2删除、复制和粘贴类 6.3查找替换类 7.从一般模式进入编辑模式 8.命令行模式下的…

RocketMQ的消息类型

RocketMQ的消息类型 文章目录 RocketMQ的消息类型一、顺序消息二、广播消息应用场景&#xff1a;示例代码&#xff1a;实现思路&#xff1a;注意点&#xff1a; 三、延时消息应用场景&#xff1a;核心方法&#xff1a; 四、批量消息应用场景&#xff1a;示例代码&#xff1a;注…