【问题标题】:SetState doesn't seem to be updating FutureBuilder in FlutterSetState 似乎没有在 Flutter 中更新 FutureBuilder
【发布时间】:2021-09-06 03:57:32
【问题描述】:

我已经构建了一个应用程序,我使用这些指令从互联网获取数据https://flutter.dev/docs/cookbook/networking/fetch-data

然后,我向 Web 服务发送一个 http POST 请求,其中的参数取自用户选择,它会即时更新数据。

我可以看到我的这部分工作是在服务器上立即更新 JSON 数据,当我重新启动应用程序时,最新数据会通过。但是,我正在尝试实现一个按钮,该按钮将“实时”刷新此数据,并且它似乎可以工作,但当我单击该按钮两次时。

我的(简化的)代码如下:

import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:async';
import 'package:intl/intl.dart';

//get Current Date
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd').format(now);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Hide the debug banner
      debugShowCheckedModeBanner: false,
      title: '',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class Match {
  final List matches;
  final String date;
  final String title;

  Match({
    required this.matches,
    required this.date,
    required this.title,
  });

  factory Match.fromJson(Map<String, dynamic> json) {
    return Match(
      matches: json['matches'],
      date: json['date'],
      title: json['title'],
    );
  }
}

class _HomePageState extends State<HomePage> {
  var keyName = "";
  var id = "";
  final Map myMap = {};
  late Future<Match> futureMatch;
  @override
  void initState() {
    super.initState();
    futureMatch = readJson();
  }

  // Fetch content from the json file
  Future<Match> readJson() async {
    final response =
        await http.get(Uri.parse('<myServer/myFile.json>'));

    if (response.statusCode == 200) {
      return Match.fromJson(jsonDecode(response.body));
    } else {
      throw Exception('Failed to load data');
    }
  }

  void _showPopup() {
    Future<Match> makePostRequest() async {
      final response = await http.post(Uri.parse(
          '<myServer/myWebService.php?date=' +
              formattedDate +
              '&id=' +
              id +
              '&' +
              keyName.toString() +
              '=' +
              myMap[keyName]));
      if (response.statusCode == 200) {
        return Match.fromJson(jsonDecode(response.body));
      } else {
        throw Exception('Failed to load data');
      }
    }

    var list = new List<int>.generate(20, (i) => i + 1);

    // set up the AlertDialog
    MaterialApp gridHolder = MaterialApp(
        home: new Align(
      alignment: Alignment.center,
      child: new Container(
        width: 500.0,
        height: 500.0,
        child: GridView.count(crossAxisCount: 5, children: [
          for (var i in list)
            GestureDetector(
              onTap: () {
                myMap[keyName] = i.toString();
                makePostRequest();
                Navigator.pop(
                  context,
                  MaterialPageRoute(builder: (context) => HomePage()),
                );
              },
              child: Card(
                child: Center(
                    child: Text(i.toString(),
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 40, color: Colors.blue))),
              ),
            ),
        ]),
      ),
    ));

    // show the dialog
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return gridHolder;
      },
    );
  }

    Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text(
            'Matches',
          ),
        ),
        body: Padding(
          padding: const EdgeInsets.all(25),
          child: Column(children: [
            ElevatedButton(
              onPressed: () {
                setState(() {
                  futureMatch = readJson();
                });
              },
              child: Text('Refresh Data'),
            ),
            Padding(
              padding: EdgeInsets.only(top: 5),
            ),
            FutureBuilder<Match>(
              future: futureMatch,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return snapshot.data!.matches.length > 0
                      ? Expanded(
                          child: ListView.builder(
                              itemCount: snapshot.data!.matches.length,
                              itemBuilder: (context, index) {
                                return Card(
                                ...
                                );
                              }),
                        )
                      : Container();
                } else if (snapshot.hasError) {
                  return Text("${snapshot.error}");
                }
                return CircularProgressIndicator();
              },
            ),
          ]),
        ));
  }
}

尤其是这部分似乎不起作用:

ElevatedButton(
                  onPressed: () {
                    setState(() {
                      futureMatch = readJson();
                    });
                  },
                  child: Text('Refresh Data'),
                ),

请谁能帮我弄清楚为什么我需要按两次按钮才能获取最新数据?

谢谢!

【问题讨论】:

  • setState 立即重建,但 readJson 返回一个 Future,因此它需要一些时间来获取数据。将您的函数切换为异步,然后在 setState() 之前等待 readJson。你可以试试这个: ElevatedButton( onPressed: () async { futureMatch = await readJson(); setState(() { }); }, child: Text('Refresh Data'), ),
  • @SuatÖzkaya 感谢您的回复!我已经尝试过了,但得到错误“无法将“匹配”类型的值分配给“未来”类型的变量。一旦我删除“等待”,就再也没有错误了,你知道这是为什么吗?谢谢

标签: flutter dart setstate http-get


【解决方案1】:

如@suat Ozkaya 所说,readJson 返回一个未来,这就是您需要在 onPress 调用中使用 async await 的原因。 试试这个:

ElevatedButton(
                onPressed: async() {
                    var response = await readJson();

                    setState(() {
                      futureMatch = response;
                    });
                  },
                  child: Text('Refresh Data'),
                ),

或者您也可以编写一个返回未来的异步函数,您可以在 onPressed 函数中调用该函数。

【讨论】:

    【解决方案2】:

    根据@Suat Özkaya 的回应,他建议 readJson 需要一些时间来获取数据,我决定将它包装在一个 Future.delayed 类中,这似乎已经成功了:

      Future.delayed(Duration(milliseconds: 1200), () {                
                      setState(() {
                        futureMatch = readJson();
                      });
                    });
                  },
    

    这暂时会有所帮助,但如果有其他想法,我们将不胜感激。

    谢谢!

    【讨论】:

      猜你喜欢
      • 2021-12-18
      • 2018-04-14
      • 1970-01-01
      • 2023-03-07
      • 2022-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多