How do I update selected value in DropdownButtonFormfield reliable when the list changes (shrinks) in a Streambuilder

16 Views Asked by At

Following code fills a DropdownButtonFormField with items (initial selection is null), when user selects a selections which is i.e. index of 15. All works ok so far. "Suddenly" Streambuilder refreshs list with less items than 15, lets say 10. And tries to "redraw" the dropdownButtonFormField and provides another index (null or 0), code suddenly fails with following:

enter image description here

How do I reliably change the selected value to less than provided list, when it seems that my new value is not taken into consideration ? Or is there a bug in DropdownButtonFormField according to changing the selected value?

Expanded(
 child: Padding(
   padding: const EdgeInsets.all(4.0),
   child: StreamBuilder(
     stream: globals.mPortalDatabase.getGoogleAddresses().watch(),
     builder: (context, snapshot) {
       if(snapshot.hasError){
         return DropdownButtonFormField<int>(
           decoration: InputDecoration(
             label: Text("Assignment location"),
             border: OutlineInputBorder()
           ),
           items: [
             DropdownMenuItem(child: Text(snapshot.error.toString(), style: TextStyle(color: Colors.red),))
           ],
           onChanged: null,
         );
       }
       if(snapshot.connectionState==ConnectionState.waiting){
         return DropdownButtonFormField<int>(
           decoration: InputDecoration(
             label: Text("Assignment location"),
             border: OutlineInputBorder()
           ),
           items: [],
           onChanged: null,
         );
       }

       List<DropdownMenuItem<int>> dropdownMenuItemList=[];

       if(snapshot.hasData){
         var googleDataElements = snapshot.data;
         print("Data present for dropdown = ${googleDataElements?.length}");
         if(googleDataElements!=null){
           if(googleDataElements.isNotEmpty){
             dropdownMenuItemList = List.generate(googleDataElements.length, (index) {
               return DropdownMenuItem<int>(
                 child: Text("${googleDataElements[index].formattedAddress}; ${googleDataElements[index].geometryLocationType}"),
                 value: index,
               );
             });
           }
           if(selectedAddressIndex!=null){
             if(selectedAddressIndex!<dropdownMenuItemList.length){
               if(googleDataElements[selectedAddressIndex!].formattedAddress!=selectedAddressItem){
                 selectedAddressIndex = null;
               }
             } else {
               selectedAddressIndex = null;
             }
           } 
         } else {
           selectedAddressIndex = null;
         }

       } else {
         print("No data present for dropdown");
       }
       
       print("Data present for dropdown = ${dropdownMenuItemList.length}");
       print("selectedAddressIndex = $selectedAddressIndex");
       
       return DropdownButtonFormField<int>(
         value: selectedAddressIndex,
         decoration: InputDecoration(
           label: Text("Assignment location"),
           border: OutlineInputBorder(),
         ),
         items: dropdownMenuItemList,
         onChanged: (value){

       }
     );
   }
 ),
),
),

Typical log content, as you see, the selectedAddressIndex is changed, but fails anyway...:

enter image description here

1

There are 1 best solutions below

0
pcba-dev On

Flutter is not detecting that your DropdownButtonFormField widget has changed when it is rebuilding the tree. It can be fixed by providing a Key to your widget which shall change when the stream returns new data.

The easiest way is creating a ValueKey created with unique values from the list of elements, for example:

final Key dropdownKey = ValueKey(googleDataElements.fold("", (v, e) => "$v-${e.geohash}"))

And then provide that key in the constructor of the DropdownButtonFormField widget.