Search code examples
flutterflutter-listview

Background of List Item displaces when items are reordered


I noticed this issue with flutter ListView and I am not sure how to go about resolving it.

I have a list of "questions" being rendered with a ListView, each question is rendered with a ListTile with a white background (tile color).

There is a feature to select a number of questions and then place them after a specific question, which basically help users position specific question(s) after a particular question. So when a question is selected, I enable/show a button below all other questions except the selected. This makes it possible for users to insert the selected questions at a particular position.

Now the problem is, when the questions are inserted, I see the background of one or more tile displace (not sure if that is the correct word) from the content, and you see the question without a background in the correct position and the background stuck at the original position.

I have attached 3 images to help understand what I am talking about.

Image showing list of questions: https://i.sstatic.net/A1Cvj.png

Image showing the 2nd question selected: https://i.sstatic.net/JoSuu.png

Image showing second question placed after the original 3rd question(which is now 2nd) with displaced background: https://i.sstatic.net/HZjXF.png

This is the code snippet that renders the Questions.

ListView.builder(
              itemCount: controller.mergedQuestions.length,
              itemBuilder: (ctx, i) {
                var question = controller.mergedQuestions[i];
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                   const SizedBox(height: 5,),
                    ListTile(
                        title: Text(
                          question.question!,
                          overflow: TextOverflow.ellipsis,
                        ),
                        onTap: () {
                          if(controller.multiSelectMode){
                            question.selected= !question.selected!;
                            controller.mergedQuestions.refresh();
                          }else{
                            controller.showQuestionEditor(question);
                          }
                        },
                        onLongPress: (){
                          question.selected = !question.selected!;
                          controller.mergedQuestions.refresh();
                        },
                        tileColor: Colors.white,
                        subtitle: Text(
                          "ANS: ${question.answer}",
                          overflow: TextOverflow.ellipsis,
                        ),
                        trailing: question.selected!?const Padding(
                          padding: EdgeInsets.all(8.0),
                          child: Icon(
                            Icons.check_circle,
                            size: 30,
                            color: Colors.green,
                          ),
                        ):const Padding(
                          padding: EdgeInsets.all(8.0),
                          child: Icon(
                            Icons.drag_handle,
                            size: 30,
                          ),
                        ),
                        leading: const Icon(Icons.question_mark)),
                    Visibility(
                      visible: controller.multiSelectMode && !question.selected!,
                      child: Center(
                        child: TextButton(onPressed: (){
                          controller.insertQuestionsAfter(question);
                        }, child: Container(
                          decoration: const BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.all(Radius.circular(15)),
                          ),
                          child: const Padding(
                            padding: EdgeInsets.symmetric(horizontal: 10.0,vertical: 5),
                            child: Row(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Text("Insert Here",style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold),),
                                Icon(Icons.add_circle,color: Colors.green,)
                              ],
                            ),
                          ),
                        )),
                      ),
                    )
                  ],
                );
              })

Solution

  • In Flutter, when using a ListView and encountering issues with item display during reordering, ensure each item in the list has a key that uniquely identifies them.

    Keys are used to uniquely identify and track widgets, enabling Flutter's reconciliation process to efficiently update the user interface. Keys must be unique amongst the Elements with the same parent. Keys distinguish widgets from each other, allowing Flutter to determine whether a widget needs to be updated, inserted, or removed. By providing keys, Flutter can accurately identify each item, preventing unexpected behavior when the list is reordered.

    You could have a unique-Id in your Question objects which is generated on creation, and use that with ValueKey(question.uid).