【问题标题】:Flutter Execute Http request on startup using FutureBuilder not workingFlutter 在启动时使用 FutureBuilder 执行 Http 请求不起作用
【发布时间】:2021-06-28 08:25:13
【问题描述】:

这是我目前正在开发的应用程序的主页代码,我必须从 API 获取所有数据。因此,我提出了_getLatest,它从作为地图列表的 URL 获取数据,并且数据进入_latest。而且我基于Flutter: Execute a function on Startup - wait for HTTP response parser to extract image URL 实现了future builder,但事情还没有完成。

首先有两条蓝色下划线: 1. Future<String> _getLatest() async { 表示 此函数的返回类型为“FutureOr”,但不以 return 语句结尾。 2. builder: (BuildContext context, AsyncSnapshot<String> snapshot) {此函数的返回类型为“Widget”,但没有'不要以 return 语句结束。

主要问题是主页是snapshot.connectionState 不会更改为完成状态,因此它会永久加载数据。而且我很确定这是因为代码而不是 URL,API 工作正常。

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

import 'package:kzstats/common/AppBar.dart';
import 'package:kzstats/common/Drawer.dart';

class Homepage extends StatefulWidget {
  @override
  _HomepageState createState() => _HomepageState();
}

class _HomepageState extends State<Homepage> {
  final String currentPage = 'KZStats';
  var _latest = [];

  Future<String> _getLatest() async {
    var url =
        'https://kztimerglobal.com/api/v2.0/records/top/recent?stage=0&tickrate=128&modes_list_string=kz_timer&limit=3';
    var httpClient = new HttpClient();
    var result;
    try {
      var request = await httpClient.getUrl(Uri.parse(url));
      var response = await request.close();
      if (response.statusCode == HttpStatus.ok) {
        var json = await response.transform(utf8.decoder).join();
        var data = jsonDecode(json);
        result = data;
      } else {}
    } catch (exception) {}

    setState(() {
      _latest = result;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: HomepageAppBar(currentPage),
      drawer: HomepageDrawer(),
      body: FutureBuilder<String>(
        future: _getLatest(),
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return ListView.builder(
              itemCount: 3,
              itemBuilder: (context, index) {
                return new Card(
                  elevation: 5.0,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.all(
                      Radius.circular(10.0),
                    ),
                  ),
                  color: Colors.white,
                  margin: const EdgeInsets.all(20),
                  child: Text('${_latest[index]}'),
                );
              },
            );
          } else if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  SizedBox(
                    child: CircularProgressIndicator(),
                    width: 60,
                    height: 60,
                  ),
                  Padding(
                    padding: EdgeInsets.only(top: 16),
                    child: Text('Loading data from API...'),
                  )
                ],
              ),
            );
          }
        },
      ),
      floatingActionButton: Builder(builder: (builderContext) {
        return FloatingActionButton(onPressed: () {
          _getLatest();
        });
      }),
    );
  }
}

【问题讨论】:

    标签: flutter dart


    【解决方案1】:
    1. 将返回类型更改为:Future&lt;void&gt;。你什么都没有返回。

    2. 您有一个if 和一个else if,但当两个条件都是false 时,您仍然需要默认大小写。

    【讨论】:

    • 感谢您的回复 2 成功!但是对于 1,如果我将返回类型更改为 Futurefuture: _getLatest(), 会出现异常,说 type 'Future' is not a subtype of type 'Future? 位于 builder 方法中。
    • 你使用了 FutureBuilder 和 AsyncSnapshot 所以你得到了那个错误
    【解决方案2】:
    import 'package:flutter/material.dart';
    import 'dart:io';
    import 'dart:convert';
    
    import 'package:kzstats/common/AppBar.dart';
    import 'package:kzstats/common/Drawer.dart';
    
    class Homepage extends StatefulWidget {
      @override
      _HomepageState createState() => _HomepageState();
    }
    
    class _HomepageState extends State<Homepage> {
      final String currentPage = 'KZStats';
      var _latest = [];
    
      Future<String> _getLatest() async {
        var url =
            'https://kztimerglobal.com/api/v2.0/records/top/recent?stage=0&tickrate=128&modes_list_string=kz_timer&limit=3';
        var httpClient = new HttpClient();
        var result;
        try {
          var request = await httpClient.getUrl(Uri.parse(url));
          var response = await request.close();
          if (response.statusCode == HttpStatus.ok) {
            var json = await response.transform(utf8.decoder).join();
            var data = jsonDecode(json);
            result = data;
          } else {}
        } catch (exception) {}
    
        setState(() {
          _latest = result;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: HomepageAppBar(currentPage),
          drawer: HomepageDrawer(),
          body: FutureBuilder<String>(
            future: _getLatest(),
            builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                return ListView.builder(
                  itemCount: 3,
                  itemBuilder: (context, index) {
                    return new Card(
                      elevation: 5.0,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.all(
                          Radius.circular(10.0),
                        ),
                      ),
                      color: Colors.white,
                      margin: const EdgeInsets.all(20),
                      child: Text('${_latest[index]}'),
                    );
                  },
                );
              } else if (snapshot.connectionState == ConnectionState.waiting) {
                return Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      SizedBox(
                        child: CircularProgressIndicator(),
                        width: 60,
                        height: 60,
                      ),
                      Padding(
                        padding: EdgeInsets.only(top: 16),
                        child: Text('Loading data from API...'),
                      )
                    ],
                  ),
                );
              }
            },
          ),
          floatingActionButton: Builder(builder: (builderContext) {
            return FloatingActionButton(onPressed: () => 
              _getLatest;
            
          }),
        );
      }
    }
    

    代码的正确版本

    【讨论】:

    • 我改了,但还是不行。我刚刚通过将整个 FutureBuilder 替换为 Container(child: Text('$_latest'),), 来测试代码,并且 _latest 已经从 API 获取数据,所以我的 FutureBuilder 肯定有问题,但 _getLatest 没有问题
    【解决方案3】:

    首先,你做错了一些事情,

    • 如果您使用的是FutureBuilder,您可以在其中使用快照,因此不需要_latest 变量。
    • 您还可以使用http 包轻松请求数据。
    • 在您的 _getLatest() 函数中,您没有返回值,它也不是 String
    • 您还可以使用 Model 类在获取 json 后轻松评估数据。

    对于您的问题,我的解决方案是

    import 'package:flutter/material.dart';
    
    import 'dart:io';
    import 'dart:convert';
    import 'package:http/http.dart' as http;
    
    
    class Homepage extends StatefulWidget {
      @override
      _HomepageState createState() => _HomepageState();
    }
    
    class _HomepageState extends State<Homepage> {
        
    
        
      final String currentPage = 'KZStats';
    
      Future<List<KzTimer>> _getLatest() async {
        var url =
            'https://kztimerglobal.com/api/v2.0/records/top/recent?stage=0&tickrate=128&modes_list_string=kz_timer&limit=3';
        
        List<KzTimer> result;
        
        try {
          var response = await http.get(Uri.parse(url));
          
          
          if (response.statusCode == HttpStatus.ok) {
            
            
             result = kzTimerFromJson(response.body);
            
          } else {
              print('Something went wrong!');
          }
        } catch (exception) {}
    
        return result;
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: FutureBuilder<List<KzTimer>>(
            future: _getLatest(),
            builder: (BuildContext context, AsyncSnapshot<List<KzTimer>> snapshot) {
                return snapshot.hasData ?
                ListView.builder(
                  itemCount: 3,
                  itemBuilder: (context, index) {
                    return new Card(
                      elevation: 5.0,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.all(
                          Radius.circular(10.0),
                        ),
                      ),
                      color: Colors.white,
                      margin: const EdgeInsets.all(20),
                      child: Text('${snapshot.data[index].playerName}'),
                      //_latest[index].playerName
                    );
                  },
                ) :
                Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      SizedBox(
                        child: CircularProgressIndicator(),
                        width: 60,
                        height: 60,
                      ),
                      Padding(
                        padding: EdgeInsets.only(top: 16),
                        child: Text('Loading data from API...'),
                      )
                    ],
                  ),
                ) ;
              
            },
          ),
          floatingActionButton: Builder(builder: (builderContext) {
            return FloatingActionButton(onPressed: () {
              _getLatest();
            });
          }),
        );
      }
    }
    

    将响应数据转换为模型类数据的方法

    List<KzTimer> kzTimerFromJson(String str) => List<KzTimer>.from(json.decode(str).map((x) => KzTimer.fromJson(x)));
    

    模型类

    class KzTimer {
        KzTimer({
            this.id,
            this.steamid64,
            this.playerName,
            this.steamId,
            this.serverId,
            this.mapId,
            this.stage,
            this.mode,
            this.tickrate,
            this.time,
            this.teleports,
            this.createdOn,
            this.updatedOn,
            this.updatedBy,
            this.place,
            this.top100,
            this.top100Overall,
            this.serverName,
            this.mapName,
            this.points,
            this.recordFilterId,
            this.replayId,
        });
    
        int id;
        String steamid64;
        String playerName;
        String steamId;
        int serverId;
        int mapId;
        int stage;
        String mode;
        int tickrate;
        double time;
        int teleports;
        DateTime createdOn;
        DateTime updatedOn;
        int updatedBy;
        int place;
        int top100;
        int top100Overall;
        String serverName;
        String mapName;
        int points;
        int recordFilterId;
        int replayId;
    
        factory KzTimer.fromJson(Map<String, dynamic> json) => KzTimer(
            id: json["id"],
            steamid64: json["steamid64"],
            playerName: json["player_name"],
            steamId: json["steam_id"],
            serverId: json["server_id"],
            mapId: json["map_id"],
            stage: json["stage"],
            mode: json["mode"],
            tickrate: json["tickrate"],
            time: json["time"].toDouble(),
            teleports: json["teleports"],
            createdOn: DateTime.parse(json["created_on"]),
            updatedOn: DateTime.parse(json["updated_on"]),
            updatedBy: json["updated_by"],
            place: json["place"],
            top100: json["top_100"],
            top100Overall: json["top_100_overall"],
            serverName: json["server_name"],
            mapName: json["map_name"],
            points: json["points"],
            recordFilterId: json["record_filter_id"],
            replayId: json["replay_id"],
        );
    }
    
    
    

    【讨论】:

    猜你喜欢
    • 2020-02-09
    • 1970-01-01
    • 2023-01-02
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    相关资源
    最近更新 更多