【问题标题】:ListView not updating correctlyListView 没有正确更新
【发布时间】:2018-07-14 14:04:02
【问题描述】:

我正在尝试用新数据替换 ListView,但这并不符合预期。重点是

ListView.builder(
          itemBuilder: (context, i) {
            return Order(_orders[i]);
          }

_getOrders() async {
    String authToken = await Auth.getAuthToken();

    http.Response response = await http.get(
        API_URL + '?date=' + _formatDate(_activePeriod),
        headers: {'x-auth': authToken});

    var parsedJson = json.decode(response.body);


    setState(() {
      if (parsedJson.runtimeType.toString() != 'List<dynamic>') {
        _orders = [];
      } else {
        _orders = parsedJson;
      }
    });
  }

当应用程序启动时第一次渲染是正确的,但是当我在 Prev/Next 按钮上调用 _changeMonth 方法时,该方法又调用 _getOrders,在我覆盖 @ 后它不会重新加载 ListView 987654325@ 变量。 如果数组的项目比前一个项目多,那么我会看到最后添加的新项目,但前一个(第一次渲染)项目保留在屏幕上。 我可以正确看到新数据,但在 API 调用后覆盖 _orders 变量不会在 ListView 中呈现新数据。除了使用setState(),我还需要做些什么吗?

主文件的完整代码。 Order 类在其他文件中定义,但我相信这里不需要。

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return HomeState();
  }
}

 class HomeState extends State {
      final DateTime _thisMonth = DateTime.now();
      DateTime _activePeriod = DateTime.now();

  final String app = 'Orders';

  List<dynamic> _orders = [];

  bool isLoggedIn = false;

  @override
  initState() {
    super.initState();
    Auth.init().then((loggedIn) {
      if (loggedIn) {
        setState(() {
          isLoggedIn = true;
        });
        _getOrders();
      }
    });
  }

  HomeState() {}

  @override
  build(BuildContext context) {
    return MaterialApp(
      title: app,
      home: Scaffold(
        appBar: new AppBar(
          title: Text(app),
        ),
        body: isLoggedIn
            ? ListView.builder(
          itemBuilder: (context, i) {
            return Order(_orders[i]);
          },
          itemCount: _orders.length,
        )
            : Auth.showLoginForm(),
        persistentFooterButtons: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[              
              FlatButton(
                child: Text('Prev'),
                onPressed: () {
                  _changeMonth(-1);
                },
              ),
              FlatButton(
                child: Text('Next'),
                onPressed: _activePeriod.month == _thisMonth.month
                    ? null
                    : () {
                  _changeMonth(1);
                },
              ),
            ],
          )
        ],
      ),
    );
  }

  _changeMonth(int increment) async {
    setState(() {
      _activePeriod =
          DateTime(_activePeriod.year, _activePeriod.month + increment, 1);
      _getOrders();
    });
  }

  _formatDate(DateTime date) {
    return '${date.year}/${date.month}/${date.day}';
  }

  _getOrders() async {
    String authToken = await Auth.getAuthToken();

    http.Response response = await http.get(
        API_URL + '?date=' + _formatDate(_activePeriod),
        headers: {'x-auth': authToken});

    var parsedJson = json.decode(response.body);
    //print(parsedJson);// new data appears fine
    setState(() {
      if (parsedJson.runtimeType.toString() != 'List<dynamic>') {
        _orders = [];
      } else {
        _orders = parsedJson;
      }
    });
  }
}

【问题讨论】:

    标签: listview dart flutter


    【解决方案1】:

    尝试为Order添加密钥

    // constructor    
    Order(..., {Key key}): super(key: key)
    
    // you should use unique key for stateful widgets, otherwise the list rebuild will be incorrect
    itemBuilder: (context, i) {
       return Order(_orders[i], Key(_orders[i].id));
    },
    

    see article

    您可以使用键来控制框架匹配哪些小部件 小部件重建时与其他小部件一起使用。默认情况下,框架 根据其匹配当前和先前构建中的小部件 runtimeType 和它们出现的顺序。用钥匙, 框架要求两个小部件具有相同的键以及 相同的 runtimeType。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-03
      • 1970-01-01
      • 1970-01-01
      • 2012-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多