代码之家  ›  专栏  ›  技术社区  ›  Alexi Coard

颤振,异步调用后呈现小部件

  •  67
  • Alexi Coard  · 技术社区  · 7 年前

    我想呈现一个小部件,它需要一个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()方法被调用,但小部件没有更新。

    2 回复  |  直到 7 年前
        1
  •  96
  •   rmtmckenzie    7 年前

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

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

    您可以在没有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(),
              ),
            ),
          );
        }
      }
    }
    
        2
  •  30
  •   Sanjayrajsinh    4 年前

    完整示例

    最佳方式 异步调用使用后的rander小部件 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
      }
    }
    

    在future builder中,它调用future函数来等待结果,一旦生成结果,它就会调用构建小部件的builder函数。


    AsyncSnapshot有3种状态:

    1、连接状态。无--在此状态下,future为null
    2、连接状态。等待--[未来]不为空,但尚未完成
    3、连接状态。完成--[未来]不为null,并且已完成。如果future成功完成,[AsyncSnapshot.data]将设置为future完成的值。如果完成时出现错误,[AsyncSnapshot.hasError]将为true