So I have been trying to solve this issue for a couple of days now and I seem to have hit a real dead end here.
The issue (which I have simplified in the code below) is that when I try to remove an item from a List<"SomeDataObject">
, it does actually remove the correct object from the list. This is evident because the ID that I arbitrarily assigned to the objects does shift just as I would expect it to when I remove something. However, oddly enough, even though the IDs shift in the list and the correct ID is removed, the states of all the widgets seem to act as though the last item is always removed, even when the deleted object is at the middle or even beginning of the list.
An example would be if the list looked as such:
Data(id: 630) // This one starts blue
Data(id: 243) // Let's say the user made this one red
Data(id: 944) // Also blue
And let's say I tried to remove the middle item from this list. Well what happens is that the list will look like this now:
Data(id: 630)
Data(id: 944)
This seems at first to be great because it looks like exactly what I wanted, but for some reason, the colors did not change their order and are still Red first, then Blue. The color state data seems to function independently from the actual objects and I could not find a clear solution.
I have code of an example program to reproduce this problem and I also have some pictures below of the program so that it is clearer what the issue is.
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,
),
),
);
}
}
Before user changes any colors (object ID is displayed as text on container):
User changes middle container to blue by tapping:
User attempts to delete middle (blue) container by double tapping:
As you can see from the image above, the ID was deleted and the ID from below was moved up on the screen, but the color information from the state did not get deleted.
Any ideas for things to try would be greatly appreciated.