【问题标题】:Cannot communicate with global variables in async functions in Flutter无法与 Flutter 中的异步函数中的全局变量通信
【发布时间】:2020-05-07 19:48:05
【问题描述】:

我在 Flutter 中工作,但在从异步函数内部与程序的其余部分通信时遇到问题。具体来说,我正在使用一个http请求(它必须是一个异步函数,所以这不是一个做不同的选择)并尝试(在异步完成后)设置一个先前实例化的全局变量等于响应的一部分从http请求。

为了尝试解析问题,我从基本的颤振项目中创建了一个最小代码示例。

要遵循的代码示例。

String username = 'xxx';
String password = 'xxx';
String basicAuth = 'Basic ' + base64Encode(utf8.encode('$username:$password'));
String url = 'xxx';
String user_phone = '5555555555';
String locationId1 = 'xxx';
String locationId2 = 'xxx';
Map info;



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

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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


  final String title;

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

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(

        title: Text(widget.title),
      ),
      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Session ID:',
            ),
            Text(
              '$sessionID',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: createSessionID,
        tooltip: 'Create Session ID',
        child: Icon(Icons.add),
      ), 
    );
  }
}

Future<void> createSessionID() async {

  http.Response response = await http.post(
    url,
    headers: <String, String>{'authorization': basicAuth},
    body: {'user_phone': user_phone, 'locationId1': locationId1, 'locationId2': locationId2},

  );
  info  = jsonDecode(response.body);
  sessionID = info['session_id'];
  print (info['session_id']);
  return info;
}

实际上问题是我可以将值打印到异步函数内的调试控制台,但是如果我尝试将全局 Map 变量设置为任何值,它始终为空,并且永远不会设置。

我尝试将其设置为 Future 并返回变量,但这并不能改变它仍未正确设置的事实,因为它出于某种未知原因返回 null。

所以在上面的例子中,如果我把它改成:

Future<Map> createTrip() async { 
  <Same Code, but with 

  Map data = jsonDecode(response.body);
  return data;

  > 
}

<void> updateInfo() async {
  info = await createSessionID();
}

然后打印 info 中的值,它仍然告诉我同样的事情,即 info 本身为空。因此,它永远不会从异步函数内部设置。我就是不明白为什么。

想法?想法?

(编辑两次以添加原始代码,并消除混乱)

【问题讨论】:

  • 我很困惑为什么flutter不允许你使用await并通过函数返回值。您能否发布您尝试通过函数传递变量并使用await 时的代码,谢谢
  • 更新了代码,您可以看到我在说什么。我使用“await”选项,但是当我输入那行代码时,flutter 给出了这个错误代码,“await 表达式只能在异步函数中使用。尝试用 'async' 或 'async* 标记函数体’。”但正如你所见,它是一个异步函数。
  • 好吧,我明白了为什么它不会让我在这种情况下等待,但它仍然没有帮助我。我需要 updateInfo 函数也可以是异步的。 (愚蠢的我之前没有弄清楚那部分)。但是,这并没有改变我可以将 sessionID 打印到调试控制台的事实,但从不在程序的其他地方使用它。为什么?
  • 好吧,虽然仍在解决问题,但我确实发现了其他东西,显然我正在设置变量,但我调用它的速度太快了,我的程序中需要信息的部分需要它立即,我不能告诉他们等待......所以我不完全确定该怎么做......但至少我可能有领先优势。

标签: http asynchronous flutter async-await


【解决方案1】:

所以这里的问题是我没有正确理解未来的概念。 (老实说,我可能仍然不知道,哈哈)。

所以我想如果你等待响应,它会自动将变量类型的 Future 更改为它应该是类型的变量。

那...不会发生。

因此,您需要以不同的方式与程序的不同部分进行通信。我最终使用的解决方案(可能不是最好的,但它有效,所以 shrug 对我来说已经足够好了)是使用全局变量来存储信息,然后使用 getter 来设置我的程序需要的局部变量等于服务器调用用来设置的全局变量。

举个例子:

Map data;  // Set globally, outside the function;

Future<void> createTrip() {
  // Code for server call
  data = jsonDecode(response.body);
  return;
}

Map getData () {
  return data;
}

因此,完全避免了尝试返回未来并对其进行解码的问题。同样,这可能效率很低,我认为有更好的方法可以做到这一点......但至少它有效,哈哈。

【讨论】:

    【解决方案2】:

    我很确定你这里的一切都是正确的,你只是没有告诉 Flutter 更新小部件,因为你已经更新了值。

    确保您致电setState 处理您的新任务。

    Future<void> createSessionID() async {
    
      http.Response response = await http.post(
        url,
        headers: <String, String>{'authorization': basicAuth},
        body: {'user_phone': user_phone, 'locationId1': locationId1, 'locationId2': locationId2},
    
      );
      info  = jsonDecode(response.body);
      setState(() => sessionID = info['session_id']); // <--------new code
      print (info['session_id']);
      return info;
    }
    

    【讨论】:

    • 我想你已经接近正确了,(我能够验证只是我需要找到某种方法让程序在 Map info 变量不再为空时更新可见值.)。但是,当我尝试在您建议的地方输入您的代码时,我的系统不允许这样做。 (它说 setState 未定义)所以即使它是由有状态小部件调用的函数,它也不是有状态的?奇怪...
    • 最终设法弄明白了!基本上我需要创建一个函数,在其中我设置一个 timer.periodic 持续检查信息变量是否已经更新,然后一旦它已经更新,只有 THEN 执行设置状态。此外,我必须使用的设置状态变体与您的建议略有不同。 "mapState.setState(() {}); (mapState as MapSampleState).resetState(); " (或者类似的东西)
    • @ArthurEKing,不允许 setState() 的原因是因为您使用的是无状态小部件。要管理状态,您必须使用 Stateful Widget。希望它有所帮助......很高兴你想通了
    • 啊,我以为 createSessionID 函数在 _MyHomePageState 类(它是一个 StatefulWidget)中。您需要将此函数移到该类中才能使我的建议生效。对于那个很抱歉。很高兴你知道了@ArthurEKing
    • 无论如何,谢谢你们的帮助!即使这不是我问题的实际答案,它也会引导我找到答案,这同样好!大声笑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多