繁体   English   中英

了解未来,在 Flutter 中等待

[英]Understanding Future, await in Flutter

我已经阅读了 Flutter 中关于期货的大部分文档。 但我仍然不明白如何从 function 返回一个无未来值。

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 所写的。 那么,如果我永远不能从这个 function 中返回任何东西,而只是一个 Future,为什么还要使用 await 关键字呢? 为什么 await 语句的返回类型不是 Future。 我不明白。

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

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

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

上面, someLongComputation将立即返回一个未来,一段时间后,未来将用一个字符串解析。

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

您不能从异步 function 返回字符串,字符串必须立即具有值,但异步 function 永远不会立即具有值,您应该做的是返回未来的字符串:

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;
}

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

你有几个选择:

您可以使用 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 上调用 function 而不是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小部件需要两个 arguments: futurebuilderfuture只是你想要使用的未来,而builder是一个 function,它接受两个参数并返回一个小部件。 FutureBuilder将在未来完成之前和之后运行这个 function。 两个 arguments 是当前上下文和snapshot ,其中有一些有用的东西:

您可以使用snapshot.data访问未来的结果,如果未来尚未完成,它将为空!

您还可以查看是否有带有snapshot.hasData的数据。

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

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

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

例如,检查使用if (snapshot.connectionState == ConnectionState.done)

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

快照(实际称为异步快照): https://api.flutter.dev/flutter/widgets/AsyncSnapshot-class.html

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

这是一个在屏幕中间打印当前时间的工作示例。 创建初始小部件时,它将打印 -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) )
                )
            )
        );
    }
}

基于第一个答案的工作 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() ) )
                )
             )
           );
         }
       );
    }
}

带有进度指示器的工作示例。 我猜一种做装载机的方法。

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
                                    )
                                )
                            )
                        )
                    );
                }
            }
        );
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM