[英]Understanding Future, await in Flutter
我已经阅读了 Flutter 中关于期货的大部分文档。 但我仍然不明白如何从 function 返回一个无未来值。
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp( Kitchen());
class Kitchen extends StatelessWidget {
String caption = '-none-';
Kitchen({Key? key}) : super(key: key);
String retrieve() async {
const String link = 'http://worldclockapi.com/api/json/utc/now';
Uri url = Uri.parse(link);
http.Response response = await http.get(url);
String result = response.toString();
return result;
}
@override
Widget build(BuildContext context) {
caption = retrieve();
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Kitchen async demo') )
, body: Container(
color: Colors.cyan[50]
, child: Center( child: Text(caption) )
)
)
);
}
}
代码不会运行,因为retrieve 的返回类型必须是Future 所写的。 那么,如果我永远不能从这个 function 中返回任何东西,而只是一个 Future,为什么还要使用 await 关键字呢? 为什么 await 语句的返回类型不是 Future。 我不明白。
我喜欢将未来视为“即将成为的价值”。
期货可以(并且应该!)有一个类型参数来解释未来最终会是什么(如果未来永远不会是未来,你可以使用 void )
Future<String> someLongComputation() async { ... }
上面, someLongComputation
将立即返回一个未来,一段时间后,未来将用一个字符串解析。
await 关键字所做的是等到未来返回一个值然后返回该值,基本上将异步计算转换为同步计算,当然这首先否定了使其异步的全部意义,所以 await关键字只能在另一个异步 function 内部使用。
您不能从异步 function 返回字符串,字符串必须立即具有值,但异步 function 永远不会立即具有值,您应该做的是返回未来的字符串:
Future<String> retrieve() async {
const String link = 'http://worldclockapi.com/api/json/utc/now';
Uri url = Uri.parse(link);
http.Response response = await http.get(url);
String result = response.toString();
return result;
}
但这是一个问题,因为您正试图在小部件上显示字符串。 当 function 运行时,您希望看到什么? 如果您没有互联网并且 function 卡住了一分钟怎么办?
你有几个选择:
您可以使用 setState 和有状态的小部件:
class Kitchen extends StatefulWidget{
...
}
class _KitchenState extends State<Kitchen> {
String caption = '-none-';
Kitchen({Key? key}) : super(key: key);
Future<void> retrieve() async {
const String link = 'http://worldclockapi.com/api/json/utc/now';
Uri url = Uri.parse(link);
http.Response response = await http.get(url);
String result = response.toString();
setState(() => caption = result);
}
@override
initState() {
super.initState();
retrieve();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Kitchen async demo') )
, body: Container(
color: Colors.cyan[50]
, child: Center( child: Text(caption) )
)
)
);
}
}
如您所见,我删除了返回类型并将其更改为调用setState
,我还在 initstate 上调用 function 而不是build
。
您的第二个选择是使用FutureBuilder
来完成类似的事情:
class Kitchen extends StatelessWidget {
Kitchen({Key? key}) : super(key: key);
Future<String> retrieve() async {
const String link = 'http://worldclockapi.com/api/json/utc/now';
Uri url = Uri.parse(link);
http.Response response = await http.get(url);
String result = response.toString();
return result;
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: retrieve(),
builder: (context, snapshot) {
final caption = snapshot.data ?? '-none-';
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Kitchen async demo') ),
body: Container(
color: Colors.cyan[50],
child: Center( child: Text(caption) )
)
)
);
}
);
}
上面的代码可能看起来很难看,但你要明白的是, FutureBuilder
小部件需要两个 arguments: future
和builder
, future
只是你想要使用的未来,而builder
是一个 function,它接受两个参数并返回一个小部件。 FutureBuilder
将在未来完成之前和之后运行这个 function。 两个 arguments 是当前上下文和snapshot
,其中有一些有用的东西:
您可以使用snapshot.data
访问未来的结果,如果未来尚未完成,它将为空!
您还可以查看是否有带有snapshot.hasData
的数据。
您可以查看snapshot.hasError
和snapshot.error
是否有问题。
最后,您可以通过snapshot.connectionState
看到未来的 state ,它可能具有以下四个值之一:
例如,检查使用if (snapshot.connectionState == ConnectionState.done)
。
要更好地理解snapshot
和FutureBuilder
,您可以查看文档:
快照(实际称为异步快照): https://api.flutter.dev/flutter/widgets/AsyncSnapshot-class.html
未来建造者: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
这是一个在屏幕中间打印当前时间的工作示例。 创建初始小部件时,它将打印 -none- 然后一旦未来完成,它将生成当前日期和时间。 这一切都基于上面的答案。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp( Kitchen());
class Kitchen extends StatefulWidget{
Kitchen( {Key? key,}) : super(key: key);
@override
_KitchenState createState(){
return _KitchenState();
}
}
class _KitchenState extends State<Kitchen> {
String caption = '-none-';
Future<void> retrieve() async {
const String link = 'http://worldclockapi.com/api/json/utc/now';
Uri url = Uri.parse(link);
http.Response response = await http.get(url);
Map body = json.decode( response.body );
String result = body['currentDateTime'];
setState(() => caption = result);
}
@override
initState() {
super.initState();
retrieve();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Kitchen async demo') )
, body: Container(
color: Colors.cyan[50]
, child: Center( child: Text(caption) )
)
)
);
}
}
基于第一个答案的工作 futureBuilder 示例。 就像 initState 版本一样工作。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp( Kitchen());
class Kitchen extends StatefulWidget{
Kitchen( {Key? key,}) : super(key: key);
@override
_KitchenState createState(){
return _KitchenState();
}
}
class _KitchenState extends State<Kitchen> {
Future<String> retrieve() async {
const String link = 'http://worldclockapi.com/api/json/utc/now';
Uri url = Uri.parse(link);
http.Response response = await http.get(url);
Map body = json.decode( response.body );
String result = body['currentDateTime'];
return result;
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: retrieve(),
builder: (context, snapshot) {
final caption = snapshot.data ?? '-none-';
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Kitchen async demo') ),
body: Container(
color: Colors.cyan[50],
child: Center( child: Text( caption.toString() ) )
)
)
);
}
);
}
}
带有进度指示器的工作示例。 我猜一种做装载机的方法。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp( Kitchen());
class Kitchen extends StatefulWidget{
Kitchen( {Key? key,}) : super(key: key);
//===================================================
// createState =
//===================================================
@override
_KitchenState createState(){
return _KitchenState();
}
}
class _KitchenState extends State<Kitchen> {
Future<bool> wasteTime() {
return Future.delayed(const Duration(seconds: 2)).then((onValue) => true);
}
Future<String> retrieve() async {
const String link = 'http://worldclockapi.com/api/json/utc/now';
Uri url = Uri.parse(link);
http.Response response = await http.get(url);
Map body = json.decode( response.body );
String result = body['currentDateTime'];
// here only to show the CircularProgressIndicator to the user.
// The network calls are fairly quick.
await wasteTime();
return result;
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: retrieve(),
builder: (context, snapshot) {
if( snapshot.connectionState == ConnectionState.done
&& snapshot.hasError )
{
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Kitchen async demo') ),
body: Container(
color: Colors.cyan[50],
child: const Center( child: Text( 'Error message' ) )
)
)
);
}
else if( snapshot.connectionState == ConnectionState.done
&& snapshot.hasData )
{
final caption = snapshot.data ?? '-none-';
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Kitchen async demo')
),
body: Container(
color: Colors.cyan[50],
child: Center(
child: Text( caption.toString() )
)
)
)
);
}
else {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Kitchen async demo') ),
body: Container(
color: Colors.cyan[50],
child: const Center(
child: CircularProgressIndicator(
value: null, color: Colors.blue
)
)
)
)
);
}
}
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.