扑动,异步调用后呈现小部件

我想呈现一个小部件,它需要一个 HTTP 调用来收集一些数据。

得到以下代码(简化)

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';


void main() {
runApp(new MyApp());
}


class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'async demo'),
);
}
}


class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);


final String title;


@override
_MyHomePageState createState() => new _MyHomePageState();
}


class _MyHomePageState extends State<MyHomePage> {


var asyncWidget;


@override
initState() {
super.initState();


loadData().then((result) {
print(result);
setState(() {
asyncWidget = result;
});
});
}


loadData() async {
var widget = new AsyncWidget();
return widget.build();
}


@override
Widget build(BuildContext context) {


if(asyncWidget == null) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Loading..."),
),
);
} else {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: this.asyncWidget,
),
);
}
}
}


class MyRenderer {


MyRenderer();


Widget render (List data) {


List<Widget> renderedWidgets = new List<Widget>();


data.forEach((element) {
renderedWidgets.add(new ListTile(
title: new Text("one element"),
subtitle: new Text(element.toString()),
));
});
var lv = new ListView(
children: renderedWidgets,
);
return lv;
}
}


class MyCollector {


Future gather() async {


var response = await // do the http request here;


return response.body;
}
}


class AsyncWidget {


MyCollector collector;
MyRenderer renderer;


AsyncWidget() {
this.collector = new MyCollector();
this.renderer = new MyRenderer();
}


Widget build() {


var data = this.collector.gather();
data.then((response) {
var responseObject = JSON.decode(response);
print(response);
return this.renderer.render(responseObject);
});
data.catchError((error) {
return new Text("Oups");
});
}
}

我的代码是这样工作的: 使用异步数据的小部件接受一个收集器(进行 http 调用)和一个呈现器(用 http 数据呈现小部件)。 我在 initState ()上创建这个小部件的一个实例,然后进行异步调用。

我发现一些文档说我们应该使用 setState ()方法用新数据更新小部件,但是这对我来说不起作用。

但是,当我放置一些日志时,我看到 HTTP 调用已经完成,并且调用了 setState ()方法,但是小部件没有更新。

87085 次浏览

最好的方法是使用 未来建设者

来自 FutureBuilder 文档:

new FutureBuilder<String>(
future: _calculation, // a Future<String> or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: return new Text('Press button to start');
case ConnectionState.waiting: return new Text('Awaiting result...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return new Text('Result: ${snapshot.data}');
}
},
)

另一件事是在 State.build 方法之外构建小部件并保存小部件本身,这是一种反模式。在构建方法中,每次都应该实际构建小部件。

您可以在不使用 FutureBuilder 的情况下让它工作,但是您应该保存 http 调用的结果(适当处理) ,然后在构建函数中使用数据。

看看这个,但是请注意,使用 FutureBuilder 是一个更好的方法,我只是提供这个给您学习。

import 'dart:async';
import 'dart:convert';


import 'package:flutter/material.dart';


void main() {
runApp(new MyApp());
}


class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'async demo'),
);
}
}


class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);


final String title;


@override
_MyHomePageState createState() => new _MyHomePageState();
}


class _MyHomePageState extends State<MyHomePage> {
List data;


@override
initState() {
super.initState();


new Future<String>.delayed(new Duration(seconds: 5), () => '["123", "456", "789"]').then((String value) {
setState(() {
data = json.decode(value);
});
});
}


@override
Widget build(BuildContext context) {
if (data == null) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Loading..."),
),
);
} else {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new ListView(
children: data
.map((data) => new ListTile(
title: new Text("one element"),
subtitle: new Text(data),
))
.toList(),
),
),
);
}
}
}

完整例子

对于异步调用后的 Rander widget,最好的方法是使用 < strong > FutureBuilder()

class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: downloadData(), // function where you call your api
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {  // AsyncSnapshot<Your object type>
if( snapshot.connectionState == ConnectionState.waiting){
return  Center(child: Text('Please wait its loading...'));
}else{
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
return Center(child: new Text('${snapshot.data}'));  // snapshot.data  :- get your object which is pass from your downloadData() function
}
},
);
}
Future<String> downloadData()async{
//   var response =  await http.get('https://getProjectList');
return Future.value("Data download successfully"); // return your response
}
}

在将来的构建器中,它调用将来函数来等待结果,一旦产生结果,它就调用构建器函数,在这里我们构建小部件。


AsyncSnapshot 有3个状态: < br > < br > None ——在这个状态下,future 为 null
等待——[ future ]不为空,但尚未完成
3.Done ——[ future ]不为空,并且已完成。如果未来成功完成,[ AsyncSnapshot.data ]将被设置为未来完成的值。如果它以错误完成,[ AsyncSnapshot.hasError ]将为 true