【问题标题】:Understanding Future, await in Flutter了解未来,在 Flutter 中等待
【发布时间】:2022-01-23 14:49:32
【问题描述】:

我已经阅读了 Flutter 中有关 Futures 的大部分文档。但我仍然不明白如何从函数中返回非未来值。

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() => runApp( Kitchen());

class Kitchen extends StatelessWidget {
    String caption = '-none-';

    Kitchen({Key? key}) : super(key: key);

    String retrieve() async {
        const String link = 'http://worldclockapi.com/api/json/utc/now';
        Uri url = Uri.parse(link);

        http.Response response = await http.get(url);
        String result = response.toString();

        return result;
    }

    @override
    Widget build(BuildContext context) {
        caption = retrieve();

        return MaterialApp(
            title: 'Flutter Demo',
            home: Scaffold(
                appBar: AppBar(title: const Text('Kitchen async demo') )
                , body: Container(
                      color: Colors.cyan[50]
                    , child: Center( child: Text(caption) )
                )
            )
        );
    }
}

代码不会运行,因为retrieve 的返回类型必须是Future 所写的。如果我永远不能从这个函数返回任何东西,除了 Future,为什么还要使用 await 关键字呢?为什么 await 语句的返回类型不是 Future。没看懂。

【问题讨论】:

  • 您不能从async 函数返回非Future 值(尽管您可以返回void)。您实际上并不希望程序停止并等待 http.get 调用完成,因为如果您的互联网连接不佳,那么可能需要 10 秒,因此您的应用程序将在 10 秒内无响应。通过返回Future,您可以立即从函数返回,而无需等待/延迟应用程序,然后您可以在稍后到达时处理该值(也许通过使用FutureBuilder)。跨度>
  • 这能回答你的问题吗? How and when to use ‘async’ and ‘await’

标签: flutter dart asynchronous async-await


【解决方案1】:

我喜欢将未来视为“即将成为的价值”。

Future 可以(并且应该!)有一个类型参数来解释未来最终会是什么(如果未来永远不会是未来,你可以使用 void)

Future<String> someLongComputation() async { ... }

如上,someLongComputation 会立即返回一个future,一段时间后,该future 将解析为一个字符串。

await 关键字的作用是等到 future 返回一个值,然后返回该值,基本上将异步计算转换为同步计算,当然这首先否定了使其异步的全部意义,所以 await 关键字只能在另一个异步函数内部使用。

你不能从异步函数中返回一个字符串,一个字符串必须立即有一个值,但异步函数永远不会立即有一个值,你应该做的是返回一个未来的字符串:

Future<String> retrieve() async {
        const String link = 'http://worldclockapi.com/api/json/utc/now';
        Uri url = Uri.parse(link);

        http.Response response = await http.get(url);
        String result = response.toString();

        return result;
}

但这是一个问题,因为您正试图在小部件上显示字符串。您希望在函数运行时看到什么?如果您没有互联网并且该功能被卡住了一分钟怎么办?

你有几个选择:

您可以使用 setState 和有状态的小部件:

class Kitchen extends StatefulWidget{
  ...
}

class _KitchenState extends State<Kitchen> {
    String caption = '-none-';

    Kitchen({Key? key}) : super(key: key);

    Future<void> retrieve() async {
        const String link = 'http://worldclockapi.com/api/json/utc/now';
        Uri url = Uri.parse(link);

        http.Response response = await http.get(url);
        String result = response.toString();

        setState(() => caption = result);
    }

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

    @override
    Widget build(BuildContext context) {

        return MaterialApp(
            title: 'Flutter Demo',
            home: Scaffold(
                appBar: AppBar(title: const Text('Kitchen async demo') )
                , body: Container(
                      color: Colors.cyan[50]
                    , child: Center( child: Text(caption) )
                )
            )
        );
    }
}

如您所见,我删除了返回类型并将其更改为调用 setState,我还在 initstate 上调用函数,而不是 build

您的第二个选择是使用FutureBuilder 来完成类似的事情:

class Kitchen extends StatelessWidget {
    Kitchen({Key? key}) : super(key: key);

    Future<String> retrieve() async {
        const String link = 'http://worldclockapi.com/api/json/utc/now';
        Uri url = Uri.parse(link);

        http.Response response = await http.get(url);
        String result = response.toString();

        return result;
    }

    @override
    Widget build(BuildContext context) {

       return FutureBuilder(
         future: retrieve(),
         builder: (context, snapshot) {
           final caption = snapshot.data ?? '-none-'; 
           return MaterialApp(
            title: 'Flutter Demo',
            home: Scaffold(
                appBar: AppBar(title: const Text('Kitchen async demo') ),
                  body: Container(
                    color: Colors.cyan[50], 
                    child: Center( child: Text(caption) )
                )
             )
           ); 
         }
       );
    }

上面的代码可能看起来很难看,但您只需要了解FutureBuilder 小部件有两个参数:futurebuilderfuture 只是您想要使用的未来,而@987654335 @ 是一个接受两个参数并返回一个小部件的函数。 FutureBuilder 将在未来完成之前和之后运行此功能。这两个参数是当前上下文和一个snapshot,它有一些有用的东西:

你可以通过snapshot.data访问future的结果,如果future还没有完成,那就是null!

snapshot.hasData也可以查看是否有数据。

您可以查看snapshot.hasErrorsnapshot.error 是否有问题。

最后,您可以通过snapshot.connectionState 看到未来的状态,它可能具有以下四个值之一:

  • 没有,可能有一些初始数据。
  • 等待,表示异步操作已经开始,通常数据为空。
  • 活动,数据不为空,并且可能随时间变化。
  • 完成,数据不为空。

if (snapshot.connectionState == ConnectionState.done) 为例进行检查。

要更好地理解snapshotFutureBuilder,可以查看文档:

快照(其实叫异步快照): https://api.flutter.dev/flutter/widgets/AsyncSnapshot-class.html

未来建设者: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

【讨论】:

    【解决方案2】:

    这是一个在屏幕中间打印当前时间的工作示例。创建初始小部件时,它将打印 -none- 然后一旦未来完成,它将生成当前日期和时间。这都是基于上面的答案。

    import 'dart:convert';
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    
    void main() => runApp( Kitchen());
    
    class Kitchen extends StatefulWidget{
        Kitchen( {Key? key,}) : super(key: key);
    
        @override
        _KitchenState createState(){
            return _KitchenState();
        }
    }
    
    class _KitchenState extends State<Kitchen> {
        String caption = '-none-';
    
        Future<void> retrieve() async {
            const String link = 'http://worldclockapi.com/api/json/utc/now';
            Uri url = Uri.parse(link);
    
            http.Response response = await http.get(url);
            Map body = json.decode( response.body );
            String result = body['currentDateTime'];
    
            setState(() => caption = result);
        }
    
        @override
        initState() {
          super.initState();
          retrieve();
        }
    
        @override
        Widget build(BuildContext context) {
    
            return MaterialApp(
                title: 'Flutter Demo',
                home: Scaffold(
                    appBar: AppBar(title: const Text('Kitchen async demo') )
                    , body: Container(
                          color: Colors.cyan[50]
                        , child: Center( child: Text(caption) )
                    )
                )
            );
        }
    }
    

    【讨论】:

      【解决方案3】:

      基于第一个答案的工作 futureBuilder 示例。就像 initState 版本一样工作。

      import 'dart:convert';
      import 'package:flutter/material.dart';
      import 'package:http/http.dart' as http;
      
      void main() => runApp( Kitchen());
      
      class Kitchen extends StatefulWidget{
          Kitchen( {Key? key,}) : super(key: key);
      
          @override
          _KitchenState createState(){
              return _KitchenState();
          }
      }
      
      class _KitchenState extends State<Kitchen> {
      
          Future<String> retrieve() async {
              const String link = 'http://worldclockapi.com/api/json/utc/now';
              Uri url = Uri.parse(link);
      
              http.Response response = await http.get(url);
              Map body = json.decode( response.body );
              String result = body['currentDateTime'];
              return result;
          }
      
          @override
          Widget build(BuildContext context) {
      
              return FutureBuilder(
               future: retrieve(),
               builder: (context, snapshot) {
                 final caption = snapshot.data ?? '-none-';
                 return MaterialApp(
                  title: 'Flutter Demo',
                  home: Scaffold(
                      appBar: AppBar(title: const Text('Kitchen async demo') ),
                        body: Container(
                          color: Colors.cyan[50],
                          child: Center( child: Text( caption.toString() ) )
                      )
                   )
                 );
               }
             );
          }
      }
      

      【讨论】:

        【解决方案4】:

        带有进度指示器的工作示例。我猜是一种加载器的方法。

        import 'dart:convert';
        import 'package:flutter/material.dart';
        import 'package:http/http.dart' as http;
        
        void main() => runApp( Kitchen());
        
        class Kitchen extends StatefulWidget{
            Kitchen( {Key? key,}) : super(key: key);
        
            //===================================================
            // createState                                      =
            //===================================================
            @override
            _KitchenState createState(){
                return _KitchenState();
            }
        }
        
        class _KitchenState extends State<Kitchen> {
        
            Future<bool> wasteTime() {
                return Future.delayed(const Duration(seconds: 2)).then((onValue) => true);
            }
        
            Future<String> retrieve() async {
                const String link = 'http://worldclockapi.com/api/json/utc/now';
                Uri url = Uri.parse(link);
        
                http.Response response = await http.get(url);
                Map body = json.decode( response.body );
                String result = body['currentDateTime'];
        
        // here only to show the CircularProgressIndicator to the user.
        //  The network calls are fairly quick.
        
                await wasteTime();
        
                return result;
            }
        
            @override
            Widget build(BuildContext context) {
        
                return FutureBuilder(
                    future: retrieve(),
                    builder: (context, snapshot) {
                        if( snapshot.connectionState == ConnectionState.done
                            && snapshot.hasError )
                        {
                            return MaterialApp(
                                title: 'Flutter Demo',
                                home: Scaffold(
                                    appBar: AppBar(title: const Text('Kitchen async demo') ),
                                    body: Container(
                                        color: Colors.cyan[50],
                                        child: const Center( child: Text( 'Error message' ) )
                                    )
                                )
                            );
                        }
                        else if( snapshot.connectionState == ConnectionState.done
                                 && snapshot.hasData )
                        {
                            final caption = snapshot.data ?? '-none-';
                            return MaterialApp(
                                title: 'Flutter Demo',
                                home: Scaffold(
                                    appBar: AppBar(
                                        title: const Text('Kitchen async demo')
                                    ),
                                    body: Container(
                                        color: Colors.cyan[50],
                                        child: Center(
                                            child: Text( caption.toString() )
                                        )
                                    )
                                )
                            );
                        }
                        else {
                           return MaterialApp(
                                title: 'Flutter Demo',
                                home: Scaffold(
                                    appBar: AppBar(title: const Text('Kitchen async demo') ),
                                    body: Container(
                                        color: Colors.cyan[50],
                                        child: const Center(
                                            child: CircularProgressIndicator(
                                                value: null, color: Colors.blue
                                            )
                                        )
                                    )
                                )
                            );
                        }
                    }
                );
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-06-08
          • 2020-08-11
          • 2021-03-17
          • 2019-02-10
          • 2022-06-11
          • 2021-11-18
          • 1970-01-01
          • 2018-11-30
          • 2020-12-14
          相关资源
          最近更新 更多