为什么我的 Flutter JSON 数据没有从 setState 更新?

问题描述

我所做的JSON数据,并将其与ListView.builder插件出现成FutureBuilder。我想在 ListView.builder 的尾部创建一个最喜欢的图标。所以我用 IconButton 创建了它,但是当我创建 setState 以将某些项目设为收藏时,数据没有更新。

这是我的代码

import 'package:Flutter/material.dart';
import 'package:json_test/class/doa.dart';
import 'package:json_test/page/DoaPage.dart';

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  Future<List<Doa>> fetchDoa(BuildContext context) async {
    final jsonstring =
        await DefaultAssetBundle.of(context).loadString('assets/doa.json');
    return doaFromJson(jsonstring);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("JSON Data test"),),body: Container(
            child: FutureBuilder(
                future: fetchDoa(context),builder: (context,snapshot) {
                  if (snapshot.hasData) {
                    return ListView.builder(
                      itemCount: snapshot.data.length,itemBuilder: (BuildContext context,int index) {
                        Doa doa = snapshot.data[index];
                        return Card(
                            margin: EdgeInsets.all(8),child: ListTile(
                                title: Text(doa.judul),onTap: () {
                                  Navigator.of(context).push(MaterialPageRoute(
                                      builder: (BuildContext context) =>
                                          DoaPage(
                                            doa: doa,)));
                                },trailing: IconButton(
                                  icon: Icon(
                                    doa.fav
                                        ? Icons.favorite
                                        : Icons.favorite_border,color: doa.fav ? Colors.red : null,onpressed: () =>
                                      setState(() => doa.fav = !doa.fav),)));
                      },);
                  }
                  return CircularProgressIndicator();
                })));
  }
}

这是 preview

解决方法

问题是,当您调用 setState 时,您会再次运行 build,然后又使用原始 Doa 对象再次运行 FutureBuilder。

您需要在 build 方法之外保留一个变量来保存 _MainPageState 中的更改,有几种方法可以做到这一点,在您的情况下,它有点复杂,因为您需要 fetchDoa 中的上下文。

一种解决方法是创建一个 doaList 变量来保存构建外部获取的数据并更改 fetchDoa 函数以设置 doaList 而不是返回它(这就是它现在是 Future 的原因。 但这还不够,因为 FutureBuilder 只会在每次构建运行时从 scrach 设置 doaList,所以我们将添加一个 _isInit bool 来检查它是否是第一次运行构建。 之后你应该用 doaList 替换所有的“snapshot.data”,因为快照没有任何内容

  class _MainPageState extends State<MainPage> {
  List<Doa> doaList;
  bool _isInit = true; 

  Future<void> fetchDoa(BuildContext context) async {
    final jsonstring =
        await DefaultAssetBundle.of(context).loadString('assets/doa.json');
    doaList = doaFromJson(jsonstring);
    _isInit = false;
  }

  @override
   Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("JSON Data test"),),body: Container(
            child: FutureBuilder(
                future: _isInit? fetchDoa(context): Future(),builder: (context,_) {
              

试试这个并告诉我它是否有效:)