Flutter异步数据构建插件listenable_future_builder的使用
Flutter异步数据构建插件listenable_future_builder的使用
引言
我们经常使用 ChangeNotifier
和 ValueNotifier
作为 StatelessWidget
的控制器。然而,存在两个问题:
- 一个普通的
StatelessWidget
不能持有控制器,因为它可能会在任何时间重建并失去状态。 - 我们有时需要在使用控制器之前完成一些异步工作。
ListenableFutureBuilder
通过结合 AnimatedBuilder
和 FutureBuilder
的功能解决了这些问题。
示例
您可以在此处查看实时示例: https://dartpad.dev/?id=8ea8e0e52e26be59d4cb8c056de53617
import 'package:flutter/material.dart';
import 'package:listenable_future_builder/listenable_future_builder.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(
useMaterial3: true,
primarySwatch: Colors.blue,
),
home: ListenableFutureBuilder<ValueNotifier<int>>(
listenable: getController,
builder: (context, child, snapshot) => Scaffold(
appBar: AppBar(),
body: Center(
child: switch (snapshot) {
AsyncSnapshot(hasData: true) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${snapshot.data!.value}',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
AsyncSnapshot(hasError: true) => const Text('Error'),
AsyncSnapshot() => const CircularProgressIndicator.adaptive()
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => snapshot.data?.value++,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
),
),
debugShowCheckedModeBanner: false,
),
);
}
Future<ValueNotifier<int>> getController() async =>
Future.delayed(const Duration(seconds: 2), () => ValueNotifier<int>(0));
与之相比,不使用 ListenableFutureBuilder
的版本更为冗长:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({
super.key,
});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ValueNotifier<int>? _controller;
[@override](/user/override)
void initState() {
super.initState();
_getController();
}
Future<void> _getController() async {
final controller = await getController();
setState(() {
_controller = controller;
});
}
[@override](/user/override)
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData(
useMaterial3: true,
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () => _controller!.value++,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
body: _controller != null
? AnimatedBuilder(
animation: _controller!,
builder: (context, child) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${_controller!.value}',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
)
: const CircularProgressIndicator.adaptive(),
),
debugShowCheckedModeBanner: false,
);
}
Future<ValueNotifier<int>> getController() async =>
Future.delayed(const Duration(seconds: 2), () => ValueNotifier<int>(0));
开始使用
在 pubspec.yaml
中添加 listenable_future_builder
依赖项:
dependencies:
flutter:
sdk: flutter
listenable_future_builder: ^[CURRENT-VERSION]
在 Dart 文件中导入包:
import 'package:listenable_future_builder/listenable_future_builder.dart';
使用方法
ListenableFutureBuilder
可以与任何 Listenable
(如 ChangeNotifier
或 ValueNotifier
)一起使用。要使用 ListenableFutureBuilder
,提供一个返回 Listenable
控制器的 Future
函数和一个根据 AsyncSnapshot
状态定义如何构建小部件的 builder
函数。
以下是如何使用 ListenableFutureBuilder
与 ValueNotifier
的示例。builder
函数应该检查 AsyncSnapshot
的状态,以确定数据是否准备好、是否发生错误或仍在加载。我们在等待时显示一个 CircularProgressIndicator
,如果发生错误则显示错误消息,并在数据可用时显示值。
点击浮动操作按钮将弹出输入对话框,如果您输入了一个值,它将在新列表中创建一个新的项目。ListView
将显示当前列表中的所有项目。
您可以在此处查看实时示例: https://dartpad.dev/?id=8bb84e817cf5f1245eb25d1c52c7c217
import 'package:flutter/material.dart';
import 'package:listenable_future_builder/listenable_future_builder.dart';
void main() => runApp(
MaterialApp(
theme: ThemeData(
useMaterial3: true,
primarySwatch: Colors.purple,
),
debugShowCheckedModeBanner: false,
home: ListenableFutureBuilder<ValueNotifier<List<String>>>(
listenable: () => Future<ValueNotifier<List<String>>>.delayed(
const Duration(seconds: 2),
() => ValueNotifier<List<String>>([])),
builder: (context, child, snapshot) => Scaffold(
appBar: AppBar(title: const Text('To-do List')),
body: Center(
child: switch (snapshot) {
AsyncSnapshot(hasData: true) => ListView.builder(
itemCount: snapshot.data!.value.length,
itemBuilder: (context, index) =>
ListTile(title: Text(snapshot.data!.value[index])),
),
AsyncSnapshot(hasError: true) => const Text('Error'),
AsyncSnapshot() => const CircularProgressIndicator.adaptive()
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Add a new to-do item'),
content: TextField(
onSubmitted: (value) {
Navigator.of(context).pop();
List<String> updatedList =
List<String>.from(snapshot.data!.value);
updatedList.add(value);
snapshot.data!.value = updatedList;
},
),
),
),
tooltip: 'Add a new item',
child: const Icon(Icons.add),
),
),
),
),
);
处理清理
在某些情况下,当 ListenableFutureBuilder
被销毁时,可能需要执行清理操作。这在监听器持有资源需要在小部件不再使用时释放时是必要的。要处理清理,提供一个 disposeListenable
函数。此函数将在 ListenableFutureBuilder
被销毁时调用监听器实例。
在这个例子中,我们创建了一个自定义的 ChangeNotifier
类来管理一个计时器,并使用 ListenableFutureBuilder
显示计时器的当前值。我们还提供了 disposeListenable
函数,在小部件不再使用时停止计时器并清理资源。当 ListenableFutureBuilder
被销毁时,disposeListenable
函数停止计时器并释放由 TimerNotifier
持有的资源。这有助于防止资源泄漏并确保 Listenable
的正确清理。这个例子是有状态的,因此我们可以使用浮动操作按钮切换 _showListenableFutureBuilder
。点击此按钮将移除 ListenableFutureBuilder
,从而触发 disposeListenable
函数。
您可以在此处查看实时示例: https://dartpad.dev/?id=bf318c5a7e87e0fa1ace796247d96620
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:listenable_future_builder/listenable_future_builder.dart';
class TimerNotifier extends ChangeNotifier {
Timer? _timer;
int _seconds = 0;
TimerNotifier() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
_seconds++;
notifyListeners();
});
}
int get seconds => _seconds;
void disposeTimer() {
_timer?.cancel();
_timer = null;
}
}
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
theme: ThemeData(
useMaterial3: true,
primarySwatch: Colors.orange,
),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _showListenableFutureBuilder = true;
[@override](/user/override)
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(),
body: Center(
child: _showListenableFutureBuilder
? ListenableFutureBuilder<TimerNotifier>(
listenable: getTimerNotifier,
builder: (context, child, snapshot) => switch (snapshot) {
AsyncSnapshot(hasData: true) =>
Text('Elapsed seconds: ${snapshot.data!.seconds}'),
AsyncSnapshot(hasError: true) => const Text('Error'),
AsyncSnapshot() =>
const CircularProgressIndicator.adaptive()
},
disposeListenable: (timerNotifier) async =>
timerNotifier.disposeTimer(),
)
: const Text('ListenableFutureBuilder removed.'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(
() => _showListenableFutureBuilder = !_showListenableFutureBuilder,
),
tooltip: 'Toggle ListenableFutureBuilder',
child: const Icon(Icons.toggle_on),
),
);
}
Future<TimerNotifier> getTimerNotifier() async {
await Future.delayed(const Duration(seconds: 2));
return TimerNotifier();
}
自定义监听器
您可能希望实现自己的 Listenable
类。这个例子展示了点击浮动操作按钮时显示随机颜色。我们创建了一个 ColorController
类,它扩展了 Listenable
。此控制器允许您通过调用 changeColor
方法更改 ColoredBox
小部件的颜色。ListenableFutureBuilder
用于使用 ColorController
构建小部件树,并提供一个 FloatingActionButton
随机更改颜色。当 ListenableFutureBuilder
从小部件树中移除时,将调用 disposeListenable
函数,并释放 ColorController
。
您可以在此处查看实时示例: https://dartpad.dev/?id=a98cd6da42144ea581b9ef45704dcbf2
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:listenable_future_builder/listenable_future_builder.dart';
class ColorController extends Listenable {
final List<VoidCallback> _listeners = [];
Color _color = Colors.red;
Color get color => _color;
void changeColor(Color newColor) {
_color = newColor;
notifyListeners();
}
[@override](/user/override)
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
[@override](/user/override)
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
void notifyListeners() {
for (final listener in _listeners) {
listener();
}
}
void dispose() {
_listeners.clear();
}
}
void main() => runApp(
MaterialApp(
theme: ThemeData(
useMaterial3: true,
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: ListenableFutureBuilder<ColorController>(
listenable: () => Future.delayed(
const Duration(seconds: 2), () => ColorController()),
builder: (context, child, snapshot) => Scaffold(
body: Center(
child: switch (snapshot) {
AsyncSnapshot(hasData: true) => ColoredBox(
color: snapshot.data!.color,
child: const SizedBox(width: 100, height: 100),
),
AsyncSnapshot(hasError: true) => const Text('Error'),
AsyncSnapshot() => const CircularProgressIndicator.adaptive()
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => snapshot.data?.changeColor(
Colors.primaries[Random().nextInt(Colors.primaries.length)]),
tooltip: 'Change color',
child: const Icon(Icons.color_lens),
),
),
disposeListenable: (colorController) async =>
colorController.dispose(),
),
),
);
更多关于Flutter异步数据构建插件listenable_future_builder的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter异步数据构建插件listenable_future_builder的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,listenable_future_builder
是一个 Flutter 插件,它允许你使用 ListenableFuture
来构建异步数据列表。这在需要监听数据变化并实时更新 UI 时非常有用。以下是一个使用 listenable_future_builder
的示例代码,展示如何构建一个简单的列表视图,该视图会根据 ListenableFuture
的数据变化进行更新。
首先,确保你已经在 pubspec.yaml
文件中添加了 listenable_future_builder
依赖:
dependencies:
flutter:
sdk: flutter
listenable_future_builder: ^x.y.z # 请替换为最新版本号
然后,你可以按照以下步骤在 Dart 文件中使用它:
1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:listenable_future_builder/listenable_future_builder.dart';
import 'dart:async';
2. 创建一个模拟的 ListenableFuture
数据源
这里我们创建一个简单的 ListenableFuture
,它会在一段时间后返回一个字符串列表。
ListenableFuture<List<String>> createListenableFuture() {
Completer<List<String>> completer = Completer<List<String>>();
// 模拟异步数据获取
Future.delayed(Duration(seconds: 2), () {
List<String> data = ["Item 1", "Item 2", "Item 3"];
completer.complete(data);
});
return completer.future;
}
3. 使用 ListenableFutureBuilder
构建 UI
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('ListenableFutureBuilder Demo'),
),
body: Center(
child: ListenableFutureBuilder<List<String>>(
future: createListenableFuture(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data?[index] ?? ''),
);
},
);
}
},
),
),
),
);
}
}
解释
- 数据源:
createListenableFuture()
函数返回一个ListenableFuture
,它会在 2 秒后完成并返回一个字符串列表。 - UI 构建:
ListenableFutureBuilder
接受一个future
和一个builder
函数。builder
函数根据snapshot
的状态(如等待、错误、数据)来构建相应的 UI。- 当
connectionState
为ConnectionState.waiting
时,显示一个进度指示器。 - 当
hasError
为true
时,显示错误信息。 - 否则,使用
ListView.builder
来显示数据列表。
- 当
这样,你就可以使用 listenable_future_builder
来构建异步数据列表,并实时更新 UI。这个示例展示了基本的使用方法,你可以根据需要进一步扩展和修改。