我想渲染一个需要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()方法用新数据更新小部件,但这对我不起作用.
解决方法
最好的方法是使用
FutureBuilder.
从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',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..."),body: new Center( child: new ListView( children: data .map((data) => new ListTile( title: new Text("one element"),subtitle: new Text(data),)) .toList(),); } } }