【问题标题】:Flutter - how to update screen with latest api responseFlutter - 如何使用最新的 api 响应更新屏幕
【发布时间】:2021-12-18 02:29:48
【问题描述】:

我想在调用 API 时更新屏幕。现在我有以下

Future<String> getData() async {
var response = await http.get(
    Uri.parse('https://www.api_endpoint.com'),
    headers: {
      'Accept':'application/json'
    }
);

Timer.periodic(Duration(microseconds: 1000), (_) {
  this.setState(() {
    data = json.decode(response.body);
    print(data); //I can see this in the console/logcat
  });
});

}

@override
void initState() {
  this.getData();
}

print(data); 上面的行中,我可以在控制台/logcat 中看到最新的 api 响应,但屏幕不会使用新值更新。我无法理解为什么当使用计时器每秒调用this.setState() 时屏幕上没有显示最新的响应......欢迎所有反馈。谢谢

【问题讨论】:

  • 不知道你的构建方法是什么样子就不可能说出来。

标签: flutter api dart flutter-state


【解决方案1】:

使用答案 found here... 将调用 this.setState() 的 Timer 移至 initState 方法

@override
void initState() {
 this.getData();
 _everySecond = Timer.periodic(Duration(seconds: 5), (Timer t) {
   setState(() {
    getData();
   });
 });
}

曾经搜索过如何更新状态、改变状态等,很快就找到了解决方案……

【讨论】:

    【解决方案2】:

    Future 执行一次,只返回one 结果。 initState() 在创建小部件时执行,通常也是一次。对于您的任务,最好使用Streams,我的解决方案在架构方面不是最好的,但作为一个例子它可以工作。

      //We create a stream that will constantly read api data
      Stream<String> remoteApi = (() async* {
        
        const url = "http://jsonplaceholder.typicode.com/todos/1";
        
        //Infinite loop is not good, but I have a simple example
        while (true) {
          try {
    
            var response = await Dio().get(url);
            
            if (response.statusCode == 200) {
              
                 //remote api data does not change, so i will add a timestamp
                  yield response.data.toString() +
                  DateTime.now().millisecondsSinceEpoch.toString();
            }
    
            //Pause of 1 second after each request
            await Future.delayed(const Duration(seconds: 1));
    
          } catch (e) {
            print(e);
          }
        }
      })();
    
      //On the screen we are waiting for data and display it on the screen
     // A new piece of data will refresh the screen
    
    @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: StreamBuilder<String>(
            stream: remoteApi,
            builder: (
              BuildContext context,
              AsyncSnapshot<String> snapshot,
            ) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else if (snapshot.connectionState == ConnectionState.active ||
                  snapshot.connectionState == ConnectionState.done) {
                if (snapshot.hasError) {
                  return const Text('Error');
                } else if (snapshot.hasData) {
                  return Center(
                    child: Padding(
                      padding: const EdgeInsets.all(15.0),
                      child: Text(
                        snapshot.data.toString(),
                        textAlign: TextAlign.center,
                      ),
                    ),
                  );
                } else {
                  return const Center(child: Text('Empty data'));
                }
              } else {
                return Center(child: Text('State: ${snapshot.connectionState}'));
              }
            },
          ),
        );
      }
    

    或最简单的解决方案

     Future<String> remoteApi() async {
        try {
          const url = "http://jsonplaceholder.typicode.com/todos/1";
          var response = await Dio().get(url);
          if (response.statusCode == 200) {
            return response.data.toString() +
                DateTime.now().millisecondsSinceEpoch.toString();
          } else {
            throw ("Error happens");
          }
        } catch (e) {
          throw ("Error happens");
        }
      }
      
     var displayValue = "Empty data";
      
     @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(),
            body: Padding(
              padding: const EdgeInsets.all(15.0),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Center(child: Text(displayValue)),
                  Center(
                    child: ElevatedButton.icon(
                      onPressed: () async {
                        displayValue = await remoteApi();
                        setState(() {});
                      },
                      label: const Text('Get API'),
                      icon: const Icon(Icons.download),
                    ),
                  )
                ],
              ),
            ));
      }
    

    【讨论】:

    • 感谢@Александр Инженер 的回复,我喜欢最简单的解决方案,但在尝试将计时器集成到其中时遇到问题。你能帮忙吗...
    【解决方案3】:

    啊,您实际上并没有在每个计时器滴答声中调用您的 API,您只是从第一次调用中解码相同的主体

    如果您想定期调用您的 API,您需要将实际的 http.get 调用移动到 timer 方法中。

    【讨论】:

    • 感谢@nvoigt 的回复,但只是为了澄清一下,我确实在响应中看到了新值,所以这不是每次都是新调用还是您引用了其他内容?
    • 如果您在每个计时器滴答声中看到 不同 值,那么这是您未在此处发布的代码的结果。您发布的内容不能每次都有不同的结果。
    猜你喜欢
    • 2021-04-03
    • 1970-01-01
    • 2021-12-08
    • 2022-10-25
    • 2020-12-23
    • 1970-01-01
    • 2019-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多