flutter

Flutter DropdownMenu goes out of bounds when enableFilter is on


tl;dr With filtering on, I get out-of-bounds errors in my DropdownMenu and I can't figure out why.

I'm learning Flutter. My DropdownMenu crashes with RangeError Index out of range if I select anything other than the first entry in the drop down AND enableFilter: true, is set. It does not do this with enableFilter: false.

Things I've tried:

Can someone help me understand either:

--or--

import 'dart:developer';
import 'package:flutter/material.dart';

class PageFive extends StatefulWidget
{
  const PageFive({super.key});
  
  @override
  State<StatefulWidget> createState()
  {
    return PageFiveState();
  } 
}

enum Villian { Gargamil, Doofenschmirtz, Kinderlumper }
enum LevelOfBadness {I, II, III, IV, V, X}

class PageFiveState extends State
{
  Villian? theBiggestVillian;
  LevelOfBadness? selectedBadnessRating;
  TextEditingController tfc = TextEditingController();

@override
  Widget build(BuildContext context)
  {
    return MaterialApp(
      home: SafeArea(
        child: Scaffold(
          backgroundColor: Colors.amber[200] ,
          appBar: AppBar(
            centerTitle: true,
            title: const Text("This is light red"),
            backgroundColor: Colors.red[300]),
          body:
           Container(
                margin: const EdgeInsets.all(15.0),
                padding: const EdgeInsets.all(3.0),
                decoration: BoxDecoration(border: Border.all(color: Colors.blueAccent)),
           child: Row (
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Flexible(
                child: Container(
                  margin: const EdgeInsets.all(15.0),
                  padding: const EdgeInsets.all(3.0),
                  decoration: BoxDecoration(border: Border.all(color: Colors.redAccent)),
                  child: SizedBox(
                    child: Column(
                      children: [
                        const Text("Choose a Villian"),
                        RadioListTile<Villian> (
                          tileColor: Colors.blue,
                          title: Text(Villian.Gargamil.name),
                          groupValue: theBiggestVillian,
                          value: Villian.Gargamil,
                          onChanged: (Villian? newValue) {
                            setState(() {
                            theBiggestVillian = newValue;
                          });
                          },
                        ),
                        RadioListTile<Villian> (
                          tileColor: Colors.green,
                          title: Text(Villian.Doofenschmirtz.name),
                          groupValue: theBiggestVillian,
                          value: Villian.Doofenschmirtz,
                          onChanged: (Villian? newValue) {
                            setState(() {
                            theBiggestVillian = newValue;
                          });
                          },
                        ),
                        RadioListTile<Villian> (
                          tileColor: Colors.orange,
                          title: Text(Villian.Kinderlumper.name),
                          groupValue: theBiggestVillian,
                          value: Villian.Kinderlumper,
                          onChanged: (Villian? newValue) {
                            setState(() {
                            theBiggestVillian = newValue;
                            log(theBiggestVillian.toString());
                          });
                          },
                        ),

                        const Spacer(),
                        const Text("Select a Badness Rating"),
                        DropdownMenu(
                          // enableFilter: true, // If I turn this on, I get out of bounds exceptions whenever I select anything but the first entry. Why?
                          onSelected: (currentBadnessSelectedInDropdownMenu) {
                            if (currentBadnessSelectedInDropdownMenu !=null)
                            {
                              setState(() 
                              {
                                log("Type is: " + currentBadnessSelectedInDropdownMenu.runtimeType.toString());
                                log("Value is: " + currentBadnessSelectedInDropdownMenu.toString());
                                selectedBadnessRating = currentBadnessSelectedInDropdownMenu;
                              });
                            }
                          },
                          dropdownMenuEntries: const <DropdownMenuEntry<LevelOfBadness>>[
                          DropdownMenuEntry(value: LevelOfBadness.I, label: "Underwhelming"),
                          DropdownMenuEntry(value: LevelOfBadness.II, label: "Rookie"),
                          DropdownMenuEntry(value: LevelOfBadness.III, label: "Moderate"),
                          DropdownMenuEntry(value: LevelOfBadness.IV, label: "Fully-Operational"),
                          DropdownMenuEntry(value: LevelOfBadness.V, label: "Level 5 Baddie"),
                          DropdownMenuEntry(value: LevelOfBadness.X, label: "Mega-baddie!"),
                        ],
                      ),

                        const Spacer(),
                        const Text("Type a Characteristic"),
                          TextField(
                            controller: tfc,
                            onChanged: textFieldChangedCallback,
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
                              hintText: 'Write something here',
                            ),
                          ),
                        const Spacer(),
                        ],
                      ),
                  ),
                ),
              ),
              Container(
                margin: const EdgeInsets.all(15.0),
                padding: const EdgeInsets.all(3.0),
                decoration: BoxDecoration(border: Border.all(color: Colors.greenAccent)),
                child: SizedBox(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        const Spacer(),
                        const Padding(padding: EdgeInsets.all(10)),
                        SizedBox(
                          width: 150,
                          child: Text("Our villian, ${theBiggestVillian?.name??""}, is of calibre $selectedBadnessRating. Most folks would describe them as ${tfc.value.text}. In conclusion, don't be a villian."),
                          ),
                        const Spacer(),
                        Row( 
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            ElevatedButton(onPressed: onPressedSubmit, child: const Text("Submit")),
                            // Spacer(), // if I put a Spacer() here, I get unbounded size! Grr.
                            ElevatedButton(onPressed: onPressedReset, child: const Text("Reset")),
                          ],
                        ),
                        const Spacer(),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

void onPressedSubmit()
{
setState(() {
    // todo
  });
}

void onPressedReset()
{
  setState(() {
    tfc.text = "";
    theBiggestVillian = null;
    // selectedBadnessRating = null; // This doesn't do what I want yet either. I want to be able to deselect the item....
    log("Reset ran");
  });
}

void textFieldChangedCallback(String newValue)
{
  setState(() {});
}

}



Solution

  • You need to provide filtercallback when enabledFilter:true

    child: DropdownMenu<LevelOfBadness>(
      enableFilter: true,
      filterCallback: (entries, filter) {
        return entries;
      },