如何在Flutter中将Future<>分配给小部件?

34
假设我有一个SingleChildScrollView,它的内容从文件中读取:
singleChildScrollView(
        padding: EdgeInsets.all(8.0),
        child: nw Text(
          getTextFromFile(), //<---read from file
          style: TextStyle(
            fontWeight: FontWeight.bold,
            fontSize: 19.0,
          ),
        ));

  Future<String> getFileData(String path) async {
    return await rootBundle.loadString(path);
  }

  Future<String> getTextFromFile() async {
      return getFileData("test.txt");
  }

我收到了以下错误信息:
The argument type 'Future<String>' can't be assigned to the parameter
type 'String'.

如何解决这个问题?


你可能需要在那个周围使用一个 FutureBuilder。 - Randal Schwartz
我认为使用FutureBuilder的答案应该是首选。它更简单,而且Flutter也推荐使用。 - Srikanth
6个回答

64

使用 FutureBuilder 应该可以解决您的问题。我修改了您的代码,以便您看到如何使用它。initialData 并不是必需的。

  @override
  Widget build(BuildContext context) {
    return new FutureBuilder(
      future: getTextFromFile(),
      initialData: "Loading text..",
      builder: (BuildContext context, AsyncSnapshot<String> text) {
        return new SingleChildScrollView(
          padding: new EdgeInsets.all(8.0),
          child: new Text(
            text.data,
            style: new TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 19.0,
            ),
          ));
      });
  }

  Future<String> getFileData(String path) async {
    return await new Future(() => "test text");
  }

  Future<String> getTextFromFile() async {
    return getFileData("test.txt");
  }
}

3
但是这种方法太过繁琐,达不到解决简单问题的效果。 - Bensal
1
@Bensal 我不这么认为,如果你仔细看的话,它只是在 return 后的前四行代码,因为答案包括完整组件实现的细节。我认为,如果你计划在组件中使用 future response,这真是太棒了。在四行代码中,我完全掌控了异步操作。没有 try、catch 或 then catch 的复杂性,只有简单的代码片段。 - Cesar Leonardo Ochoa Contreras
我相信这是正确的方法,并且应该被接受为答案。Future和Stream是Dart中最突出的特点之一,Flutter提供了FutureBuilder和StreamBuilder来处理这些数据类型。在使用Flutter时,我会避免使用.then()方法。 - Erdi İzgi
惊讶于这不是被接受的答案。 - user1012500

43

StatefulWidget可以用于此目的。在您的State类中声明一个成员变量String _textFromFile = "",并使用setState()方法在未来解析时更新其值。

我从构造函数中调用了你的getTextFromFile()方法,但你可以在任何地方调用它。

运行代码:

import 'package:flutter/material.dart';
import 'dart:async';

class StatefullWidgetDemo extends StatefulWidget {
  @override
  _StatefulWidgetDemoState createState() {
    return new _StatefulWidgetDemoState();
  }
}

class _StatefulWidgetDemoState extends State<StatefullWidgetDemo> {
  String _textFromFile = "";

  _StatefulWidgetDemoState() {
    getTextFromFile().then((val) => setState(() {
          _textFromFile = val;
        }));
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Stateful Demo'),
      ),
      body: new SingleChildScrollView(
        padding: new EdgeInsets.all(8.0),
        child: new Text(
          _textFromFile,
          style: new TextStyle(
            fontWeight: FontWeight.bold,
            fontSize: 19.0,
          ),
        ),
      ),
    );
  }

  Future<String> getFileData(String path) async {
    return "your data from file";
  }

  Future<String> getTextFromFile() async {
    return await getFileData("test.txt");
  }
}

这个看起来更直观,所以我标记为已接受。我也喜欢Doge的答案,所以我投了票。 - camino
非常感谢您!只是补充一下,Flutter文档建议在initState()中添加获取数据的方法。 - Omar Pervez Khan

12

这里是简单的答案=>

调用该函数的类:

@override
Widget build(BuildContext context) {
return new Scaffold(
 child: FutureBuilder(
  future: function(),
  builder: (BuildContext context, AsyncSnapshot<String> text) {
    return new Text(text.data);
  });
)}

并且这个函数:

Future<String> function() async {
   return 'abc';
}

2

1

另一个在初始化时获取数据的解决方案是在initState()中调用getTextFromFile(),使用新字符串设置状态,并在小部件中使用该字符串:

String fileData = '';

Future<String> getFileData(String path) async {
    return await rootBundle.loadString(path);
  }

void getTextFromFile() async {
    try {
      String data = await getFileData("test.txt");
      setState(() {
        fileData = data;
      });
    } catch (ex) {
      print(ex);
    }
  }

  @override
  void initState() {
    super.initState();
    getTextFromFile();
  }

new singleChildScrollView(
        padding: new EdgeInsets.all(8.0),
        child: new Text(
          fileData,
          style: new TextStyle(
            fontWeight: FontWeight.bold,
            fontSize: 19.0,
          ),
        ));

0

我想再添加一个答案,因为我测试了其他答案后出现了错误。 将异步部分(加上setState)从构造函数移动到initState()中解决了我的问题。

祝愉快

class TestAsyncInStateful extends StatefulWidget {
  const TestAsyncInStateful({super.key});

  @override
  State<TestAsyncInStateful> createState() => _TestAsyncInStatefulState();
}

class _TestAsyncInStatefulState extends State<TestAsyncInStateful> {
  @override
  void initState() {
    super.initState();

    getTextFromServer().then(
      (value) => setState(() {
        textFromServer = value;
      }),
    );
  }

  String? textFromServer;

  @override
  Widget build(BuildContext context) {
    return textFromServer == null? const SizedBox() :Text(textFromServer!);
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接