【问题标题】:Return async value to initState()将异步值返回给 initState()
【发布时间】:2021-12-27 07:10:23
【问题描述】:

我的以下代码运行良好,从 url 读取 csv 数据并打印输出:

import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:csv/csv.dart';

void fetchUserData() async {
  final request = await HttpClient().getUrl(Uri.parse(
      'https://docs.google.com/spreadsheets/d/e/2PACX-1vQvf9tp4-fETDJbC-HRmRKvVFAXEAGO4lrYPpVeiYkB6nqqXdSs3CjX0eBMvjIoEeX9_qU6K2RWmzVk/pub?gid=0&single=true&output=csv'));
  final response = await request.close();
  List<List<dynamic>> rowsAsListOfValues;
  await for (final csvString in response.transform(const Utf8Decoder())) {
    rowsAsListOfValues =
        const CsvToListConverter().convert(csvString);
  }
  print(rowsAsListOfValues);
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    fetchUserData();
  }

  @override
  Widget build(BuildContext context) { // ... // }
}

我不需要打印输出,而是需要将其返回到一个变量中,我可以在 y 小部件中显示该变量,我尝试如下:

Future<List<List<dynamic>>> fetchUserData() async {   /// change
  final request = await HttpClient().getUrl(Uri.parse(
      'https://docs.google.com/spreadsheets/d/e/2PACX-1vQvf9tp4-fETDJbC-HRmRKvVFAXEAGO4lrYPpVeiYkB6nqqXdSs3CjX0eBMvjIoEeX9_qU6K2RWmzVk/pub?gid=0&single=true&output=csv'));
  final response = await request.close();
  List<List<dynamic>> rowsAsListOfValues;
  await for (final csvString in response.transform(const Utf8Decoder())) {
    rowsAsListOfValues =
        const CsvToListConverter().convert(csvString);
  }

  return rowsAsListOfValues;    /// change
}

class _MyHomePageState extends State<MyHomePage> {
  var rowsAsListOfValues;  /// new

  @override
  void initState() {
    super.initState();
    rowsAsListOfValues = fetchUserData();    /// new
    print(rowsAsListOfValues);   /// new
  }

  @override
  Widget build(BuildContext context) { // ... // }
}

但我得到的输出是I/flutter ( 7505): Instance of 'Future&lt;List&lt;List&lt;dynamic&gt;&gt;&gt;'

我该如何解决?

【问题讨论】:

  • initState 不能标记为异步。但是,您可以启动一个异步函数(无需等待它),然后在底层异步操作完成后设置该函数内部的状态。

标签: flutter dart


【解决方案1】:

在这种情况下,您需要从 initState 切换到 didChangeDependency。因为你需要等待一些进程,而你不能在 initState 中等待。但是你可以这样等待

@override
void didChangeDependencies() async {
  super.didChangeDependencies();
  rowsAsListOfValues = await fetchUserData();
  super.setState(() {}); // to update widget data
  /// new
  print(rowsAsListOfValues);
}

这就是结果

I/flutter (24313): [[vranches], [Dammam, 2], [Khobar, 3]]

【讨论】:

  • 可以添加 setState((){});在等待之后,当它获取数据时,它会设置正确的数据
【解决方案2】:

您可以使用 Future.delayed() 包装您的代码,如下所示。

 @override
  void initState() {
    super.initState();
    Future.delayed(Duration.zero,()async{
       rowsAsListOfValues =await fetchUserData();   
       setState(() {});
       print(rowsAsListOfValues);   // this return correct value
    });
   print(rowsAsListOfValues);  // this return null
  }

完整代码

import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:csv/csv.dart';

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

class App extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _AppState createState() => _AppState();
}

Future<List<List<dynamic>>> fetchUserData() async {
  final request = await HttpClient().getUrl(Uri.parse(
      'https://docs.google.com/spreadsheets/d/e/2PACX-1vQvf9tp4-fETDJbC-HRmRKvVFAXEAGO4lrYPpVeiYkB6nqqXdSs3CjX0eBMvjIoEeX9_qU6K2RWmzVk/pub?gid=0&single=true&output=csv'));
  final response = await request.close();
  List<List<dynamic>> rowsAsListOfValues;
  await for (final csvString in response.transform(const Utf8Decoder())) {
    rowsAsListOfValues =
        const CsvToListConverter().convert(csvString);
  }
  return rowsAsListOfValues;
}

class _AppState extends State<HomePage> {
  var rowsAsListOfValues;
  @override
  void initState() {
    super.initState();
    Future.delayed(Duration.zero, () async {
      rowsAsListOfValues = await fetchUserData();
      setState(() {});
      print(rowsAsListOfValues);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '$rowsAsListOfValues',
            ),
          ],
        ),
      ),
    );
  }
}

【讨论】:

  • 我收到了I/flutter ( 7505): null
  • 控制台值。
  • 打印 rowsAsListOfValues 的值
  • 这是给我的null
  • 兄弟,我在这里接收数据为 I/flutter (10842):[[ches], [Dammam, 2], [Khobar, 3]]
【解决方案3】:

initState 方法是同步的,不支持async。我推荐使用FutureBuilder,但您也可以将代码移至async 函数。

FutureBuilder

import 'package:flutter/material.dart' show
  Widget, FutureBuilder, AsyncSnapshot
;

class _MyHomePageState extends State<MyHomePage> {

  static Future<void> fetchUserData() {
    return Future().delayed(
      Duration(seconds: 10),
      () => 'loaded'
    );
  }

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

  @override
  Widget build(BuildContext context) {

    return FutureBuilder(
      future: Future.wait([
        fetchUserData()
      ]),

      builder: (
        BuildContext context,
        AsyncSnapshot snapshot
      ) {
        if (snapshot.hasData) {
          return Text(snapshot.data);
        }
        return Text('loading...');
      }
    );
  }
}

异步函数

@override
void initState () {
  super.initState();
  (() async {
    rowsAsListOfValues = await fetchUserData();
    print(rowsAsListOfValues);
  })();
}

或

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

void initLoad() async {
  rowsAsListOfValues = await fetchUserData();
  print(rowsAsListOfValues);
}

【讨论】:

    【解决方案4】:

    将then() 与async 函数一起使用时,我感觉更轻松。你可以试试这个:

      
    
        fetchUserData().then((value) {
           setState(() {
            rowsAsListOfValues = value;
           });
        });   
    
    
    

    或者你可以像这样使用await。

      @override
      void initState() async {
        super.initState();
    
        rowsAsListOfValues = await fetchUserData();
                          
        print(rowsAsListOfValues);  
      }
    

    【讨论】:

    • .then() 选项措辞很好,await 选项给了_MyHomePageState.initState() returned a Future.
    • 别忘了在inintState中使用async: void initState() async {
    • 我看到了,我看不到setState(() {}); 我们应该在哪里使用它?
    猜你喜欢
    • 1970-01-01
    • 2013-07-19
    • 1970-01-01
    • 2020-12-09
    • 2021-12-23
    • 1970-01-01
    • 2018-01-25
    • 2012-12-20
    • 2011-06-01
    相关资源
    最近更新 更多