1

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

enter image description here

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(),
        ));
  }
}
ibex
  • 103
  • 1
  • 6
  • 1
    This is because each time you delete a box the state is built again and the default `dropdownValue="one"` and a new Random color is assigned to the box. – Ashutosh Patole Jul 04 '21 at 04:58
  • Also as you can observe it bottom box takes the color of the box which is been deleted above it. In my case third box takes the color of the second box. – ibex Jul 04 '21 at 05:23
  • Then what should I do to repair my code? – ibex Jul 04 '21 at 05:26

1 Answers1

1

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(),
      ),
    );
  }
}
MaNDOOoo
  • 1,347
  • 1
  • 5
  • 19