flutter 什么是未来?我如何使用它?

bnl4lu3b  于 2022-11-25  发布在  Flutter
关注(0)|答案(5)|浏览(152)

出现以下错误:

A value of type 'Future<int>' can't be assigned to a variable of type 'int'

它可能是int之外的另一种类型,但基本上模式是:

A value of type 'Future<T>' can't be assigned to a variable of type 'T'

因此:

  • Future到底是什么?
  • 如何获得想要的实际值?
  • 当我只有一个Future<T>时,我使用什么小部件来显示我的值?
qvtsj1bj

qvtsj1bj1#

如果您熟悉Task<T>Promise<T>以及async/await模式,那么您可以直接跳到“如何使用Future和Flutter中的小部件”部分。

什么是未来,我该如何利用它?

documentation表示:
对象,表示延迟的计算。
没错。它也有点抽象和枯燥。通常,一个函数返回一个结果。按顺序。函数被调用、运行并返回它的结果。在此之前,调用者会等待。有些函数,尤其是当它们访问硬件或网络等资源时,需要一点时间来完成。想象一下,一个头像图片从Web服务器加载,从数据库加载用户数据,或者只是从设备内存加载应用程序的多语言文本,这可能会很慢。
默认情况下,大多数应用程序都有一个控制流。当这个控制流被阻塞时,例如,等待一个需要花费时间的计算或资源访问时,应用程序就会冻结。如果你足够大,你可能会记得这是一个标准的控制流,但在今天的世界里,这会被视为一个bug。即使某个东西需要时间,我们也会得到一个小动画。一个旋转器,一个沙漏,也许是一个进度条。2但是一个应用程序如何运行并显示一个动画,同时还在等待结果呢?3答案是:异步操作。当你的代码等待某个东西的时候,这些操作仍然在运行。现在编译器怎么知道,它是应该停止一切并等待一个结果,还是继续所有的后台工作并只在这个示例中等待?嗯,它自己无法判断出这一点。我们必须告诉它。
这是通过一种称为asyncawait的模式来实现的。它并不特定于flutterdart,它在许多其他语言中以相同的名称存在。您可以找到Dart here的文档。
由于一个需要一些时间的方法不能立即返回,它将返回一个在完成时传递值的承诺。
这被称为Future,因此,从数据库加载一个数字的承诺将返回Future<int>,而从互联网搜索返回一个电影列表的承诺可能返回Future<List<Movie>>Future<T>是 * 在未来 * 会给你T的东西。
让我们尝试一种不同的解释:
future表示异步操作的结果,可以有两种状态:未完成的或完成的。
很有可能,你并不是为了好玩才这么做的,你实际上需要Future<T>的结果来推进你的应用程序。你需要显示数据库中的数字或者找到的电影列表。所以你想等待,直到结果出现。这就是await的用武之地:

Future<List<Movie>> result = loadMoviesFromSearch(input);

// right here, you need the result. So you wait for it:
List<Movie> movies = await result;

但是等等,我们不是又回到原点了吗?我们不是又在等待结果了吗?是的,我们确实是在等待。如果程序没有一些类似于顺序流的东西,程序会完全混乱。但关键是,使用关键字await,我们已经告诉编译器,在这一点上,当我们想等待结果时,我们不希望我们的应用程序只是冻结。2我们希望所有其他运行的操作,例如动画继续。
但是,你只能在本身标记为async的函数中使用await关键字,并返回Future<T>。因为当你await某个东西时,等待的函数就不能再立即返回它们的结果了。你只能返回你所拥有的,如果你必须等待它,你就必须返回一个稍后交付它的承诺。

Future<Pizza> getPizza() async {
    Future<PizzaBox> delivery = orderPizza();        

    var pizzaBox = await delivery;

    var pizza = pizzaBox.unwrap();
    
    return pizza;   
}

我们的getPizza函数必须 * 等待 * 比萨饼,因此它必须返回比萨饼 * 将来会出现 * 的承诺,而不是立即返回Pizza。现在,您可以在某处依次执行await getPizza函数。

如何在Flutter中使用Future和小部件?

flutter中的所有小部件都期望得到真实的值,而不是一个以后会出现的值的承诺,当一个按钮需要一个文本时,它不能使用一个以后会出现文本的承诺,它需要显示按钮 now,所以它需要文本 now
但有时候,你所拥有的只是一个Future<T>。这就是FutureBuilder的用武之地。当你有一个未来时,你可以使用它,在你等待它的时候显示一件事(例如进度指示器),在它完成的时候显示另一件事(例如结果)。
让我们看一下我们的比萨饼示例。您希望订购比萨饼,希望在等待过程中显示进度指示器,希望在发送后看到结果,并且可能在出现错误时显示错误消息:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

/// ordering a pizza takes 5 seconds and then gives you a pizza salami with extra cheese
Future<String> orderPizza() {
  return Future<String>.delayed(const Duration(seconds: 5), () async => 'Pizza Salami, Extra Cheese');
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: Scaffold(
        body: Center(
          child: PizzaOrder(),
        ),
      ),
    );
  }
}

class PizzaOrder extends StatefulWidget {
  @override
  _PizzaOrderState createState() => _PizzaOrderState();
}

class _PizzaOrderState extends State<PizzaOrder> {
  Future<String>? delivery;

  @override
  Widget build(BuildContext context) {
    return Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          ElevatedButton(
            onPressed: delivery != null ? null : () => setState(() { delivery = orderPizza(); }),
            child: const Text('Order Pizza Now')
          ),
          delivery == null
            ? const Text('No delivery scheduled')
            : FutureBuilder(
              future: delivery,
              builder: (context, snapshot) {
                if(snapshot.hasData) {
                  return Text('Delivery done: ${snapshot.data}');
                } else if(snapshot.hasError) {
                  return Text('Delivery error: ${snapshot.error.toString()}');
                } else {
                  return const CircularProgressIndicator();
                }
              })
        ]);
  }
}

这就是你如何使用FutureBuilder来显示你未来的结果。

ttygqcqt

ttygqcqt2#

下面是其他语言中与Dart的Future的类比列表:

  • JS:Promise
  • Java语言:Future
  • Python皮:Future
  • C#:Task

就像其他语言一样,Future是一种特殊类型的对象,允许使用async/await语法sugar,以同步/线性方式编写异步代码。您可以从async方法返回Future,而不是接受回调作为参数,从而避免回调地狱-Future和回调都解决了相同的问题(在稍后的时间触发一些代码),但方式不同。

e0bqpujr

e0bqpujr3#

Future<T>返回将由async完成的工作的电位值
例如:

Future<int> getValue() async {
    return Future.value(5);
  }

上面的代码返回int类型的Future.value(5),但是当从方法接收值时,我们不能使用Future<int>类型,即

Future<int> value = await getValue();  // Not Allowed
// Error
A value of type 'Future<int>' can't be assigned to a variable of type 'int'

要解决上面的getValue()应该在int类型下接收

int value = await getValue(); // right way as it returning the potential value.
erhoui1w

erhoui1w4#

我希望这一要点能提供一些信息,我将以两种不同的异步方法进行演示:

请注意以下方法,其中showLoading()getAllCarsFromApi()hideLoading()是内部Async方法。
如果我将await关键字放在showLoading()之前,Operation会等待它完成,然后转到下一行,但我有意删除了await,因为我需要在处理getAllCarsFromApi()的同时显示Loading对话框,因此这意味着showLoading()getAllCarsFromApi()方法在不同的线程上处理。最后hideLoading()隐藏了加载对话框。

Future<List<Car>> getData() async{
   showLoading();
   final List<Car> cars = await getAllCarsFromApi();
   hideLoading();
   return cars;
}

现在看另一个Async方法,这里getCarByIdFromApi()方法需要一个id,它是从getCarIdFromDatabase()计算出来的,因此在第一个方法之前必须有一个await关键字,以使Operation等待,直到计算出id并将其传递给第二个方法。因此,这里的两个方法是在单个Thread中依次处理的。

Future<Car> getCar() async{
   int id = await getCarIdFromDatabase();
   final Car car = await getCarByIdFromApi(id);
   return car;
}
bmp9r5qi

bmp9r5qi5#

一个简单的答案是,如果一个函数返回它的值时带有一个delay,那么Future就被用来获取它的值。

Future<int> calculate({required int val1, required int val2}) async {
    await Future.delayed(const Duration(seconds: 2));
    return val1 + val2;
  }

如果我们调用上面的函数

getTotal() async { 

      int result = calculate(val1: 5, val2: 5);

    print(result);
  }

我们将得到以下错误:

A value of type 'Future<int>' can't be assigned to a variable of type 'int'

但是如果我们在函数调用之前使用await,它将在延迟之后给出函数的实际返回值

getTotal() async { 

    int result = await calculate(val1: 5, val2: 5);

   print(result);
 }

使用await for the Future获取返回值需要关键字async

相关问题