首页 文章

Flutter,在异步调用后呈现小部件

提问于
浏览
1

我想渲染一个需要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()方法,但是小部件不会更新 .

1 回答

  • 5

    最好的方法是使用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',
          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(),
              ),
            ),
          );
        }
      }
    }
    

相关问题