In my program I put the add button to create stateful box with stateful drop down button inside of it, each time I add the box I add it to Map<int, Widget> and pass it to the column. When I click on the cross button it delete the widget from the map in parent. But when I click on cross button on the widget, it show wrong colour of the box and wrong drop down value.Watch the GIF I posted to get the overview of the problem
Link to dart pad to run the example : dart pad code link here
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
@override
_StateMyWidget createState() => _StateMyWidget();
}
class _StateMyWidget extends State<MyWidget> {
Map<int, Widget> widgetList = {};
int boxCount = 0;
@override
initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return ListView(children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: widgetList.values.toList()),
TextButton(
onPressed: () {
widgetList[boxCount] =
new MyBox(boxIndex: boxCount, deleteFunction: deleteBox);
setState(() {});
boxCount += 1;
},
child: Text("Add"))
],
)
]);
}
deleteBox(boxIndex) {
widgetList.remove(boxIndex);
setState(() {});
}
}
class MyBox extends StatefulWidget {
final int boxIndex;
final Function deleteFunction;
MyBox({required this.boxIndex, required this.deleteFunction});
_StateMyBox createState() => _StateMyBox();
}
class _StateMyBox extends State<MyBox> {
var containerColor;
@override
initState() {
super.initState();
containerColor =
Colors.primaries[Random().nextInt(Colors.primaries.length)];
}
@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
margin: EdgeInsets.all(17),
padding: EdgeInsets.all(10),
color: containerColor,
child: Column(children: [
Row(children: [
Text("Box Number: ${widget.boxIndex}"),
Spacer(),
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
widget.deleteFunction(widget.boxIndex);
},
),
]),
RegistrationDropdown(listData: ['One', 'Two', 'Three', 'Four']),
]));
}
}
class RegistrationDropdown extends StatefulWidget {
final List<String> listData;
RegistrationDropdown({
required this.listData,
});
@override
_StateRegistrationDropdown createState() {
return _StateRegistrationDropdown();
}
}
class _StateRegistrationDropdown extends State<RegistrationDropdown> {
String dropdownValue = 'One';
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
padding: EdgeInsets.only(left: 10, right: 10),
child: DropdownButton<String>(
isExpanded: true,
underline: SizedBox(),
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? newValue) {
print("Previous dropdown value $dropdownValue");
print("New value $newValue");
setState(() {
dropdownValue = newValue!;
});
},
items: widget.listData.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
));
}
}
The solution is a Key
of the widget. The When to Use Keys: Flutter Youtube will be helpful.
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
@override
_StateMyWidget createState() => _StateMyWidget();
}
class _StateMyWidget extends State<MyWidget> {
Map<int, Widget> widgetList = {};
int boxCount = 0;
@override
initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return ListView(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: widgetList.values.toList(),
),
TextButton(
onPressed: () {
widgetList[boxCount] = new MyBox(
key: UniqueKey(), // <---------------------
boxIndex: boxCount,
deleteFunction: deleteBox,
);
setState(() {});
boxCount += 1;
},
child: Text("Add"),
)
],
)
],
);
}
deleteBox(boxIndex) {
widgetList.remove(boxIndex);
setState(() {});
}
}
class MyBox extends StatefulWidget {
final int boxIndex;
final Function deleteFunction;
MyBox({
Key? key, // <---------------------
required this.boxIndex,
required this.deleteFunction,
}) : super(key: key); // <---------------------
_StateMyBox createState() => _StateMyBox();
}
class _StateMyBox extends State<MyBox> {
var containerColor;
@override
initState() {
super.initState();
containerColor =
Colors.primaries[Random().nextInt(Colors.primaries.length)];
}
@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
margin: EdgeInsets.all(17),
padding: EdgeInsets.all(10),
color: containerColor,
child: Column(children: [
Row(children: [
Text("Box Number: ${widget.boxIndex}"),
Spacer(),
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
widget.deleteFunction(widget.boxIndex);
},
),
]),
RegistrationDropdown(listData: ['One', 'Two', 'Three', 'Four']),
]));
}
}
class RegistrationDropdown extends StatefulWidget {
final List<String> listData;
RegistrationDropdown({
required this.listData,
});
@override
_StateRegistrationDropdown createState() {
return _StateRegistrationDropdown();
}
}
class _StateRegistrationDropdown extends State<RegistrationDropdown> {
String dropdownValue = 'One';
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
padding: EdgeInsets.only(left: 10, right: 10),
child: DropdownButton<String>(
isExpanded: true,
underline: SizedBox(),
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? newValue) {
print("Previous dropdown value $dropdownValue");
print("New value $newValue");
setState(() {
dropdownValue = newValue!;
});
},
items: widget.listData.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
);
}
}