我想呈现一个需要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()方法被调用,但是小部件不会更新。
最好的方法是使用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方法之外构建窗口小部件并保存窗口小部件本身,这是一种反模式。您实际上应该每次都在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(), ), ), ); } } }