Android List.remove()删除正确的对象,但对象';列表中的状态发生变化,就好像最后一个条目被删除一样

Android List.remove()删除正确的对象,但对象';列表中的状态发生变化,就好像最后一个条目被删除一样,android,flutter,dart,Android,Flutter,Dart,所以我已经尝试解决这个问题好几天了,我似乎已经走到了死胡同 问题是(我在下面的代码中简化了这一点)当我试图从列表中删除一个项目时,它实际上从列表中删除了正确的对象。这是很明显的,因为我任意分配给对象的ID确实会发生移动,就像我移除某些东西时所期望的那样。然而,奇怪的是,即使ID在列表中移动并且正确的ID被删除,所有小部件的状态似乎都好像最后一项总是被删除一样,即使删除的对象位于列表的中间甚至开始 例如,如果列表看起来是这样的: Data(id: 630) // This one starts b

所以我已经尝试解决这个问题好几天了,我似乎已经走到了死胡同

问题是(我在下面的代码中简化了这一点)当我试图从
列表
中删除一个项目时,它实际上从列表中删除了正确的对象。这是很明显的,因为我任意分配给对象的ID确实会发生移动,就像我移除某些东西时所期望的那样。然而,奇怪的是,即使ID在列表中移动并且正确的ID被删除,所有小部件的状态似乎都好像最后一项总是被删除一样,即使删除的对象位于列表的中间甚至开始

例如,如果列表看起来是这样的:

Data(id: 630) // This one starts blue
Data(id: 243) // Let's say the user made this one red
Data(id: 944) // Also blue
假设我试图从列表中删除中间的项目。现在的情况是,列表将如下所示:

Data(id: 630)
Data(id: 944)
一开始它看起来很棒,因为它看起来正是我想要的,但由于某种原因,颜色没有改变顺序,仍然是先红后蓝。颜色状态数据似乎独立于实际对象运行,我无法找到明确的解决方案

我有一个示例程序的代码来重现这个问题,下面还有一些程序的图片,以便更清楚问题是什么

import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: HomeScreen(),
    );
  }
}

// Home screen class to display app
class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  List<DataWidget> widgetsList = List<DataWidget>();

  // Adds a new data object to the widgets list
  void addData() {
    setState(() {
      widgetsList.add(DataWidget(removeData));
    });
  }

  // Removes a given widget from the widgets list
  void removeData(DataWidget toRemove) {
    setState(() {
      widgetsList = List.from(widgetsList)..remove(toRemove);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      /* I used a SingleChildScrollView instead of ListView because ListView
      resets an objects state when it gets out of view so I wrapped the whole
      list in a column and then passed that to the SingleChildScrollView to
      force it to stay in memory. It is worth noting that this problem still
      exists even when using a regular ListView. */
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          // Added a SizedBox just to make column take up whole screen width
          children: [...widgetsList, SizedBox(width: double.infinity)],
        ),
      ),
      // FloatingActionButton just to run addData function for example
      floatingActionButton: FloatingActionButton(onPressed: addData),
    );
  }
}

// Simple class that is a red square that when clicked turns blue when pressed
class DataWidget extends StatefulWidget {
  DataWidget(this.onDoubleTap);
  final Function(DataWidget) onDoubleTap;

  // Just a random number to keep track of which widget this is
  final String id = Random.secure().nextInt(1000).toString();

  @override
  String toStringShort() {
    return id;
  }

  @override
  _DataWidgetState createState() => _DataWidgetState();
}

class _DataWidgetState extends State<DataWidget> {
  /* Here the example state information is just a color, but in the full version
  of this problem this actually has multiple fields */
  Color color = Colors.red;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: GestureDetector(
        // onDoubleTap this passes itself to the delete method of the parent
        onDoubleTap: () => widget.onDoubleTap(widget),
        // Changes color on tap to whatever color it isn't
        onTap: () {
          setState(() {
            color = color == Colors.red ? Colors.blue : Colors.red;
          });
        },
        child: Container(
          child: Center(child: Text(widget.id)),
          width: 200,
          height: 200,
          color: color,
        ),
      ),
    );
  }
}

import'dart:math';
进口“包装:颤振/材料.省道”;
void main(){
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:“颤振演示”,
主页:主屏幕(),
);
}
}
//显示应用程序的主屏幕类
类主屏幕扩展StatefulWidget{
@凌驾
_HomeScreenState createState()=>\u HomeScreenState();
}
类_homescrenstate扩展状态{
List-widgetsList=List();
//将新数据对象添加到窗口小部件列表
void addData(){
设置状态(){
add(DataWidget(removeData));
});
}
//从窗口小部件列表中删除给定的窗口小部件
void removeData(DataWidget toRemove){
设置状态(){
widgetsList=List.from(widgetsList)…删除(toRemove);
});
}
@凌驾
小部件构建(构建上下文){
返回脚手架(
/*我使用了SingleChildScrollView而不是ListView,因为ListView
在对象离开视图时重置对象状态,因此我将整个对象包装起来
列表,然后将其传递给SingleChildScrollView以
强制将其保留在内存中。值得注意的是,此问题仍然存在
即使在使用常规ListView时也存在*/
正文:SingleChildScrollView(
子:列(
mainAxisAlignment:mainAxisAlignment.center,
//添加了一个大小框,使列占据整个屏幕宽度
子项:[…widgetsList,SizedBox(宽度:double.infinity)],
),
),
//例如,FloatingActionButton只是为了运行addData函数
floatingActionButton:floatingActionButton(按下:添加数据),
);
}
}
//一个简单的类,它是一个红色的正方形,当单击它时,按下它时会变成蓝色
类DataWidget扩展了StatefulWidget{
DataWidget(this.onDoubleTap);
最终功能(DataWidget)onDoubleTap;
//只是一个随机数来跟踪这是哪个小部件
最终字符串id=Random.secure().nextInt(1000.toString();
@凌驾
字符串到字符串短(){
返回id;
}
@凌驾
_DataWidgetState createState();
}
类_DataWidgetState扩展状态{
/*这里的示例状态信息只是一种颜色,但是完整版本
这个问题实际上有多个字段*/
颜色=颜色。红色;
@凌驾
小部件构建(构建上下文){
返回填充(
填充:常数边集全部(8.0),
儿童:手势检测器(
//onDoubleTap将其自身传递给父级的delete方法
onDoubleTap:()=>widget.onDoubleTap(widget),
//将点击时的颜色更改为其他颜色
onTap:(){
设置状态(){
color=color==Colors.red?Colors.blue:Colors.red;
});
},
子:容器(
子:中心(子:文本(widget.id)),
宽度:200,
身高:200,
颜色:颜色,
),
),
);
}
}
用户更改任何颜色之前(对象ID在容器上显示为文本):

用户通过点击将中间容器更改为蓝色:

用户试图通过双击删除中间(蓝色)容器:

从上图中可以看到,ID被删除,下面的ID在屏幕上向上移动,但该州的颜色信息没有被删除


如果您有任何想法可以尝试,我们将不胜感激。

您需要对
DataWidget
类进行一些更改,以使其正常工作:

// Simple class that is a red square that when clicked turns blue when pressed
class DataWidget extends StatefulWidget {

  Color color = Colors.red; // moved from _DataWidgetState class

  DataWidget(this.onDoubleTap);
  final Function(DataWidget) onDoubleTap;

  // Just a random number to keep track of which widget this is
  final String id = Random.secure().nextInt(1000).toString();

  @override
  String toStringShort() {
    return id;
  }

  @override
  _DataWidgetState createState() => _DataWidgetState();
}

class _DataWidgetState extends State<DataWidget> {
  /* Here the example state information is just a color, but in the full version
  of this problem this actually has multiple fields */

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: GestureDetector(
        // onDoubleTap this passes itself to the delete method of the parent
        onDoubleTap: () => widget.onDoubleTap(widget),
        // Changes color on tap to whatever color it isn't
        onTap: () {
          setState(() {
            widget.color =
                widget.color == Colors.red ? Colors.blue : Colors.red;
          });
        },
        child: Container(
          child: Center(child: Text(widget.id)),
          width: 200,
          height: 200,
          color: widget.color,
        ),
      ),
    );
  }
}
//一个简单的类,它是一个红色的正方形,单击时会变成蓝色
类DataWidget扩展了StatefulWidget{
Color Color=Colors.red;//从_DataWidgetState类移动
DataWidget(this.onDoubleTap);
最终功能(DataWidget)onDoubleTap;
//只是一个随机数来跟踪这是哪个小部件
最终字符串id=Random.secure().nextInt(1000.toString();
@凌驾
字符串到字符串短(){
返回id;
}
@凌驾
_DataWidgetState createState();
}
类_DataWidgetState扩展状态{
/*这里的示例状态信息只是一种颜色,但是完整版本
这个问题实际上有多个字段*/
@凌驾
小部件构建(构建上下文){
返回填充(
填充:常数边集全部(8.0),
儿童:手势检测器(
//onDoubleTap将其自身传递给父级的delete方法
onDoubleTap:()=>widget.onDoubleTap(widget),
//变色