前言:
在Flutter实际的开发工作中,局部刷新也就是更小的颗粒度刷新一直是开发者追求的一个目标,总结一下几个常用的局部刷新的小组件。
首先来说有很多状态管理的框架,可以轻松实现局部刷新。
-
Provider :使用 Consumer 或者 Selector 来实现。
-
Bloc :使用 BuildWhen 来实现。
-
GetX :使用 Obx() 来实现。
-
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) 分离,实现完美解耦。
极致的局部刷新 :ValueListenableBuilder
的 builder
方法就是需要刷新的最小范围。
完美解耦 :组件之间不需要互相知道对方的存在,它们只通过 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();
},
);
}