1

I have two drop downs, and I want to do things when they get values selected. One of those is to change the second buttondrop items based on what's selected in the first dropdown. For example:

Dropdown1 is a list of car manufactuers

Dropdown2 is a list of their models

Dropdown1 selects mercedes

Dropdown2 gets "E Class, S Class" etc

Dropdown1 selects lexus

Dropdown2 gets "ES, LS", etc

(Eventually the second drop down will update a listview as well, but haven't gotten to that yet.)

Data wise, it works, I update the list. The problem is the UI won't update unless I do a hot reload

Currently I am just having the dropdowns fetch their data and using Future builders

Future? data1;
Future? data2;

void initState(){
   super.initState();
   data1 = _data1AsyncMethod();
   data2 = _data2AsyncMethod();
}

_data2AsyncMethod([int? item1_id]) async{
   if(item1_id == null){
     item2Classes = await DefaultItems().getAllItem2Classes();
     listOfItem2ClassNames = DefaultItems().returnListOfItemClassNames(item2Classes);
   }
   else{
     // The methods below calls the DefaultItems methods which have Futures all in them.
     // The getAllItems calls a network file with GET methods of future type to get data and decodes them, etc. 
    // They build a list of the object type, ex List<Item2>

     item2Classes = await DefaultItems().getAllItem2Classes(item1_id);
     listOfItem2ClassNames = DefaultItems().returnListOfItemClassNames(item2Classes);
   }
}

I have this Future Builder nested in some containers and paddings

FutureBuilder{
   future: data2,
   builder: (context, snapshot){
      if(snapshot.connectionState != done...)
         // return a circle progress indictator here
      else{
         return CustomDropDown{
               hintText: 'example hint'
               dropDownType: 'name'
               dropDownList: listOfItem2ClassNames
               dropDownCallback: whichDropDown,
         }

The onChanged in CustomDropDown passes the dropDownType and the dropDownValue

The callback

whichDropDown(String dropDownType, String dropDownValue){
   if(dropDownType == 'item1'){
      //so if the first dropdown was used
     // some code to get item_1's id and I call the data2 method

     _data2AsyncMethod(item1_id);
}

Again the data updates (listOfItem2ClassNames) BUT the UI won't update unless I hot reload. I've even called just setState without any inputs to refresh but doesn't work

So how do I get the UI to update with the data, and is my solution too convoluted in the first place? How should I solve? StreamBuilders? I was having trouble using them.

Thanks

2 Answers2

0

If you do a setState in the whichDropDown function, it will rebuild the UI. Although I'm not exactly sure what you want, your question is really ambiguous.

whichDropDown(String dropDownType, String dropDownValue){
   if(dropDownType == 'item1'){
      //so if the first dropdown was used
     // some code to get item_1's id and I call the data2 method

     _data2AsyncMethod(item1_id).then((_) {
       setState(() {});
     });
   }
}
Kreetchy
  • 688
  • 4
  • 14
  • Sorry I should of included, I did try using setState in whichDropDown but it doesn't work. I'm basically just trying to find why it's not updating or if there's a better way. – TheCoolest2 Feb 17 '23 at 18:19
  • Update, so I was doing .then(()... rather than doing .then((_)...,and now the UI updates. What is the underscore? – TheCoolest2 Feb 17 '23 at 19:38
  • _ is a convention in dart for unused variable. Could have also wrote "value" or "res" there, but you actually need something there for it to even work I believe (Hence why it was not behaving as expected when you put nothing). Mark as answered if it does solve your UI refresh/update. – Kreetchy Feb 17 '23 at 19:57
0

I notice a couple things:

  1. nothing is causing the state to update, which is what causes a rebuild. Usually this is done explicitly with a call to setState()
  2. in whichDropdown(), you call _data2AsyncMethod(item1_id), but that is returning a new Future, not updating data2, which means your FutureBuilder has no reason to update. Future's only go from un-completed to completed once, so once the Future in the FutureBuilder has been completed, there's no reason the widget will update again.

You may want to think about redesigning this widget a bit, perhaps rather than relying on FutureBuilder, instead call setState to react to the completion of the Futures (which can be done repeatedly, as opposed to how FutureBuilder works)

anqit
  • 780
  • 3
  • 12
  • Sorry I should of included, I did try using setState in whichDropDown but it doesn't work. Ah that makes sense, I've created a new future but FutureBuilder won't use it because it's only called once. Any known examples of just using setStates? I'm using FutureBuilders as I was reading this is the way to get the app to wait for the defaultdata to load at startup before rendering the UI, as sometimes the data wouldn't load until after the UI was already drawn. – TheCoolest2 Feb 17 '23 at 18:20
  • Also an update, I got it to work with the changes from @Kreetchy, but would still like a response about whether I'm using FutureBuilders correctly to get the app to wait for data. – TheCoolest2 Feb 17 '23 at 19:40
  • I think FutureBuilder only makes sense when you need to react to a single future once, I don't think it's appropriate to use when there are continuous state change. I would try something like storing option2 values in state, having your the `onChange` of dropdown1 trigger finding the values for option2, and set those values inside setstate. – anqit Feb 17 '23 at 20:45