Flutter异步构建插件async_builder的使用
Flutter异步构建插件async_builder的使用
该插件提供了AsyncBuilder
,一个类似于StreamBuilder
/FutureBuilder
的小部件,旨在减少样板代码并改进错误处理。它还提供了InitBuilder
,使得安全地开始异步任务变得更加容易。
如何使用
1. 添加到依赖项
dependencies:
async_builder: ^1.2.0
2. 导入
import 'package:async_builder/async_builder.dart';
import 'package:async_builder/init_builder.dart';
AsyncBuilder示例
Future
AsyncBuilder<String>(
future: myFuture,
waiting: (context) => Text('Loading...'),
builder: (context, value) => Text('$value'),
error: (context, error, stackTrace) => Text('Error! $error'),
)
Stream
AsyncBuilder<String>(
stream: myStream,
waiting: (context) => Text('Loading...'),
builder: (context, value) => Text('$value'),
error: (context, error, stackTrace) => Text('Error! $error'),
closed: (context, value) => Text('$value (closed)'),
)
注意:不能同时提供流和未来。
AsyncBuilder功能
分离的构建器
AsyncBuilder
允许你根据异步操作的状态指定不同的构建器:
waiting(context)
- 在没有任何事件发生时调用。builder(context, value)
- 必需。在有值可用时调用。error(context, error, stackTrace)
- 如果出现错误则调用。closed(context, value)
- 如果流关闭则调用。
如果没有提供这些,则默认调用builder
(可能带有空的value
)。
错误处理
AsyncBuilder
默认不会静默忽略错误。如果异常发生且提供了error
,小部件将重建并调用该构建器。否则,如果未提供error
且silent
不为真,则异常和堆栈跟踪将打印到控制台。
初始值
如果提供了initial
,则在其可用之前会使用该值。
RxDart ValueStream
如果流是一个持有现有值的ValueStream
(BehaviorSubject
),则在第一次构建时该值将立即可用。
流暂停
该小部件的StreamSubscription
可以通过pause
参数暂停,这在你需要通知上游StreamController
你不再需要更新时很有用。
InitBuilder
InitBuilder
是一个仅在配置更改时初始化值的小部件,这非常有用,因为它允许你安全地开始异步任务而无需创建一个新的StatefulWidget
。
基本用法
static Future<int> getNumber() async => ...;
build(context) => InitBuilder<int>(
getter: getNumber,
builder: (context, future) => AsyncBuilder<int>(
future: future,
builder: (context, value) => Text('$value'),
),
);
在这种情况下,getNumber
只会在第一次构建时被调用。
传递参数
你还可以向getter
传递参数,例如查询共享偏好设置:
final String prefsKey;
build(context) => InitBuilder.arg<String, String>(
getter: sharedPrefs.getString,
arg: prefsKey,
builder: (context, future) => AsyncBuilder<String>(
future: future,
builder: (context, value) => Text('$value'),
),
);
构造函数InitBuilder.arg
到InitBuilder.arg7
可用于向getter
传递参数,这些参数将在getter
或参数发生变化时重新初始化值。
完整示例
以下是一个完整的示例,展示了如何使用AsyncBuilder
和InitBuilder
。
import 'dart:async';
import 'dart:math';
import 'package:async_builder/async_builder.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const MyHomePage(title: 'AsyncBuilder Example'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
[@override](/user/override)
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: [
AsyncTestChild1(),
AsyncTestChild2(),
AsyncTestChild3(),
AsyncTestChild4(),
],
),
backgroundColor: Colors.blueGrey.shade600,
);
}
}
class AsyncTestChild1 extends StatefulWidget {
[@override](/user/override)
_AsyncTestChild1State createState() => _AsyncTestChild1State();
}
class _AsyncTestChild1State extends State<AsyncTestChild1> {
Future<int>? randomNumber;
[@override](/user/override)
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
return TestCard(
title: '随机数 - Future',
desc: '此示例在2秒后完成一个返回随机数的Future。',
child: Row(children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
textStyle: const TextStyle(color: Colors.white),
),
child: const Text('生成'),
onPressed: () {
setState(() {
randomNumber = Future.delayed(
const Duration(seconds: 2), () => Random().nextInt(100));
});
},
),
const Padding(padding: EdgeInsets.only(right: 16)),
if (randomNumber != null)
AsyncBuilder<int>(
waiting: (context) => const CircularProgressIndicator(),
builder: (context, i) => Text('$i', style: textTheme.titleLarge),
future: randomNumber,
),
]),
);
}
}
class AsyncTestChild2 extends StatefulWidget {
[@override](/user/override)
_AsyncTestChild2State createState() => _AsyncTestChild2State();
}
class _AsyncTestChild2State extends State<AsyncTestChild2> {
StreamController<int>? randomNumber;
void initController() {
setState(() {
randomNumber?.close();
randomNumber = StreamController<int>();
});
}
[@override](/user/override)
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
return TestCard(
title: '随机数 - Stream',
desc: '此示例在1秒后向流添加一个随机数。',
child: Row(children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
textStyle: const TextStyle(color: Colors.white),
),
child: const Text('重置'),
onPressed: randomNumber == null
? null
: () {
setState(() {
randomNumber = null;
});
},
),
const Padding(padding: EdgeInsets.only(right: 8)),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
textStyle: const TextStyle(color: Colors.white),
),
child: const Text('添加'),
onPressed: () async {
if (randomNumber == null) initController();
final ctrl = randomNumber;
await Future<void>.delayed(const Duration(seconds: 1));
ctrl!.add(Random().nextInt(100));
},
),
const Padding(padding: EdgeInsets.only(right: 16)),
if (randomNumber != null)
AsyncBuilder<int>(
waiting: (context) => const CircularProgressIndicator(),
builder: (context, i) => Text('$i', style: textTheme.titleLarge),
stream: randomNumber!.stream,
),
]),
);
}
}
class AsyncTestChild3 extends StatefulWidget {
[@override](/user/override)
_AsyncTestChild3State createState() => _AsyncTestChild3State();
}
class _AsyncTestChild3State extends State<AsyncTestChild3> {
StreamController<int>? randomNumber;
void initController() {
setState(() {
randomNumber?.close();
final ctrl = StreamController<int>();
randomNumber = ctrl;
final timer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
ctrl.add(Random().nextInt(100));
});
ctrl.onCancel = timer.cancel;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
return TestCard(
title: '随机数 - 关闭',
desc: '此示例持续向流添加数字直到关闭。',
child: Row(children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
textStyle: const TextStyle(color: Colors.white),
),
child: Text(randomNumber == null ? '开始' : '重启'),
onPressed: initController,
),
const Padding(padding: EdgeInsets.only(right: 8)),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
textStyle: const TextStyle(color: Colors.white),
),
child: const Text('关闭'),
onPressed: randomNumber == null || randomNumber!.isClosed
? null
: () {
setState(() {
randomNumber!.close();
});
},
),
const Padding(padding: EdgeInsets.only(right: 16)),
if (randomNumber != null)
AsyncBuilder<int>(
waiting: (context) => const CircularProgressIndicator(),
builder: (context, i) => Text('$i', style: textTheme.titleLarge),
closed: (context, i) =>
Text('$i (关闭)', style: textTheme.titleLarge),
stream: randomNumber!.stream,
),
]),
);
}
}
class AsyncTestChild4 extends StatefulWidget {
[@override](/user/override)
_AsyncTestChild4State createState() => _AsyncTestChild4State();
}
class _AsyncTestChild4State extends State<AsyncTestChild4> {
StreamController<int>? randomNumber;
var pause = false;
void initController() {
setState(() {
randomNumber?.close();
final ctrl = StreamController<int>();
randomNumber = ctrl;
late Timer timer;
void start() {
timer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
ctrl.add(Random().nextInt(100));
});
}
ctrl.onPause = () => timer.cancel();
ctrl.onCancel = () => timer.cancel();
ctrl.onResume = start;
start();
});
}
[@override](/user/override)
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
return TestCard(
title: '随机数 - 暂停',
desc: '此示例持续向流添加数字但允许订阅暂停。',
child: Row(children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
textStyle: const TextStyle(color: Colors.white),
),
child: Text(randomNumber == null ? '开始' : '重启'),
onPressed: initController,
),
const Padding(padding: EdgeInsets.only(right: 16)),
Text('暂停', style: textTheme.titleSmall),
Switch(
value: pause,
onChanged: (b) {
setState(() {
pause = b;
});
},
activeColor: Colors.blue),
const Padding(padding: EdgeInsets.only(right: 16)),
if (randomNumber != null)
AsyncBuilder<int>(
waiting: (context) => const CircularProgressIndicator(),
builder: (context, i) => Text('$i', style: textTheme.titleLarge),
stream: randomNumber!.stream,
pause: pause,
),
]),
);
}
}
class TestCard extends StatelessWidget {
final String title;
final String desc;
final Widget child;
const TestCard({
required this.title,
required this.desc,
required this.child,
});
[@override](/user/override)
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
return Card(
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
child: Text(title, style: textTheme.titleLarge),
padding: const EdgeInsets.symmetric(vertical: 8),
),
const Divider(),
Text(desc, style: textTheme.titleMedium),
const Padding(padding: EdgeInsets.only(bottom: 16)),
child,
],
)),
);
}
}
更多关于Flutter异步构建插件async_builder的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter异步构建插件async_builder的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用async_builder
插件的详细代码案例。async_builder
是一个用于处理异步构建过程的Flutter插件,它允许你在数据加载过程中显示加载指示器,并在数据加载完成后显示实际内容。
首先,确保你已经在pubspec.yaml
文件中添加了async_builder
依赖:
dependencies:
flutter:
sdk: flutter
async_builder: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,我们来看一个完整的代码示例,展示如何使用AsyncBuilder
来异步加载数据并在UI中显示:
import 'package:flutter/material.dart';
import 'package:async_builder/async_builder.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Async Builder Example'),
),
body: Center(
child: AsyncBuilder<String>(
future: _fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
// 显示加载指示器
return CircularProgressIndicator();
} else if (snapshot.hasError) {
// 处理错误
return Text('Error: ${snapshot.error}');
} else {
// 显示数据
return Text('Data: ${snapshot.data}');
}
},
),
),
),
);
}
// 模拟异步数据加载函数
Future<String> _fetchData() async {
await Future.delayed(Duration(seconds: 2)); // 模拟网络延迟
return 'Hello, Flutter!';
}
}
代码解释:
-
依赖导入:首先,我们导入了必要的Flutter和
async_builder
包。 -
主应用:
MyApp
是一个StatelessWidget
,它构建了一个简单的Material应用,其中包含一个Scaffold
,AppBar
,和一个居中的AsyncBuilder
。 -
AsyncBuilder:
future
属性:指定一个返回Future
的函数,这里我们使用_fetchData()
函数来模拟异步数据加载。builder
属性:一个函数,它接收当前上下文和AsyncSnapshot
对象。根据AsyncSnapshot
的connectionState
,我们可以判断当前是处于加载中、加载完成还是出错状态。- 如果
connectionState
是ConnectionState.waiting
,则显示一个CircularProgressIndicator
作为加载指示器。 - 如果发生错误(
snapshot.hasError
),则显示错误信息。 - 如果加载完成(其他状态),则显示加载的数据。
- 如果
-
模拟异步数据加载:
_fetchData()
函数使用Future.delayed
来模拟一个耗时操作(例如网络请求),然后返回一个字符串数据。
这样,你就可以在Flutter应用中利用async_builder
插件优雅地处理异步数据加载和UI更新。希望这个示例对你有帮助!