Flutter 局部刷新小组件汇总

前言:
在Flutter实际的开发工作中,局部刷新也就是更小的颗粒度刷新一直是开发者追求的一个目标,总结一下几个常用的局部刷新的小组件。
首先来说有很多状态管理的框架,可以轻松实现局部刷新。
  1. Provider :使用 Consumer 或者 Selector 来实现。

  2. Bloc :使用 BuildWhen 来实现。

  3. GetX :使用 Obx() 来实现。

  4. RiverPod通过 ref.watch(homeDetailScrollRiverProvider.select 或者 细化拆分river 来实现。

其实,Flutter本身自己也提供了局部刷新的小组件,随着业务的复杂,用这些小组件配合状态管理框架一起来使用,会使颗粒度更细,法力无边。
一. StatefulWidget

在写页面时,尽量的细化和封装组件,小组件继承 StatefulWidget 通过 setStates 来更新小组件自己,页面上其他组件不变。

二. StatefulBuilder

有些时候可能一个小组件或者页面相对复杂,而需要根据状态变化的 Widget 只有一个,要想做到更小颗粒度的刷新那肯定就不能调用 setStates ,因为小组件本身不需要全部重新 build 只是需要重新构建根据状态变化的 Widget 即可。

简单来说,StatefulBuilder 是一个 "便携式"、"嵌入式"的 StatefulWidget , 它的独特之处在于它的 builder 方法,这个方法提供了一个 setState 回调,你可以用这个回调来触发仅重建 StatefulBuilder 内部 UI 的局部刷新。

在实际的项目开发过程中有很多使用的场景,比如 表单选择 点赞 收藏 等 。

写了一个例子,点击 widget 改变自身文字显示 代码如下:

dart 复制代码
return StatefulBuilder(
  builder: (context, buildSetState) {
    return InkWell(
      onTap: () {
        _title = Until.getTitle();
        buildSetState.call(() {});
      },
      child: Container(
        width: double.infinity,
        color: Colors.white,
        alignment: Alignment.center,
        padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 18.w),
        child: Text(_title),
      ),
    );
  },
);
三. ValueNotifier + ValueListenableBuilder

ValueListenableBuilder 的使用场景和 StatefulBuilder 类似,二者在很多的场景中可以互相替代。

ValueListenableBuilder官方推荐 的用于中等复杂度场景的局部刷新方案。它将数据(ValueNotifier)UI(ValueListenableBuilder) 分离,实现完美解耦。

极致的局部刷新ValueListenableBuilderbuilder 方法就是需要刷新的最小范围。

完美解耦 :组件之间不需要互相知道对方的存在,它们只通过 ValueNotifier通信。

写了一个例子,点击 widget 改变自身文字显示 代码如下:

dart 复制代码
Widget _valueNotifierWidget() {
  return ValueListenableBuilder<String>(
    valueListenable: _valueNotifier,
    builder: (context, value, child) {
      return InkWell(
        onTap: () {
          _valueNotifier.value = Until.getTitle();
        },
        child: Container(
          width: double.infinity,
          color: Colors.white,
          alignment: Alignment.center,
          margin: EdgeInsets.symmetric(vertical: 12.h),
          padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 18.w),
          child: Text(value),
        ),
      );
    },
  );
}
三. StreamBuilder

对于异步数据流(比如来自网络、文件读取),用 StreamBuilder 来实现局部刷新也有助于解耦。

写了一个例子,网络请求数据,成功之后更新自身文字展示 代码如下:

dart 复制代码
Widget _streamBuilderWidget() {
  return StreamBuilder<CarModel?>(
    stream: _carStream,
    builder: (context, snapshot) {
      if (snapshot.hasError) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.error_outline, color: Colors.red, size: 60),
            const SizedBox(height: 16),
            Text(
              '出错啦: ${snapshot.error}',
              style: Theme.of(context).textTheme.bodyLarge,
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: _refreshData, // 出错后可以重试
              child: const Text('重试'),
            ),
          ],
        );
      } else if (snapshot.hasData) {
        return Container(
          color: Colors.blue,
          padding: const EdgeInsets.all(8.0),
          child: Text(snapshot.data?.description ?? ""),
        );
      } else {
        return const CircularProgressIndicator();
      }
    },
  );
}
四. FutureBuilder

FutureBuilder 是 Flutter 中用于处理单次异步操作 并据此构建 UI 的强大组件。它比 StreamBuilder 更简单,适用于绝大多数"请求-响应"模式的场景,如网络请求、数据库查询、本地文件读取等。

写了一个例子,网络请求数据,成功之后更新自身文字展示 代码如下:

dart 复制代码
Widget _futureBuilderWidget() {
  return FutureBuilder<CarModel?>(
    future: _futureCar,
    builder: (context, snapshot) {
      if (snapshot.hasError) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.error_outline, color: Colors.red, size: 60),
            const SizedBox(height: 16),
            Text(
              '出错啦: ${snapshot.error}',
              style: Theme.of(context).textTheme.bodyLarge,
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: _refreshStream, // 出错后可以重试
              child: const Text('重试'),
            ),
          ],
        );
      }
      if (snapshot.hasData) {
        final CarModel? carModel = snapshot.data;
        return Container(
          color: Colors.red,
          padding: const EdgeInsets.all(8.0),
          child: Text(carModel?.description ?? ''),
        );
      }
      return const CircularProgressIndicator();
    },
  );
}

Demo 地址

相关推荐
胡gh10 分钟前
浏览器:我要用缓存!服务器:你缓存过期了!怎么把数据挽留住,这是个问题。
前端·面试·node.js
你挚爱的强哥28 分钟前
SCSS上传图片占位区域样式
前端·css·scss
奶球不是球29 分钟前
css新特性
前端·css
Nicholas6830 分钟前
flutter滚动视图之Viewport、RenderViewport源码解析(六)
前端
无羡仙40 分钟前
React 状态更新:如何避免为嵌套数据写一长串 ...?
前端·react.js
TimelessHaze1 小时前
🔥 一文掌握 JavaScript 数组方法(2025 全面指南):分类解析 × 业务场景 × 易错点
前端·javascript·trae
jvxiao2 小时前
搭建个人博客系列--(4) 利用Github Actions自动构建博客
前端
袁煦丞2 小时前
SimpleMindMap私有部署团队脑力风暴:cpolar内网穿透实验室第401个成功挑战
前端·程序员·远程工作
li理2 小时前
鸿蒙 Next 布局开发实战:6 大核心布局组件全解析
前端
EndingCoder2 小时前
React 19 与 Next.js:利用最新 React 功能
前端·javascript·后端·react.js·前端框架·全栈·next.js