The dropdown_search library in flutter does not refresh my user list

Viewed 37

Hello everyone and thank you for taking the time to try to help me.

I use this library in flutter on mobile only => pub.dev dropdown_search

When I click on the dropdown, I load with an API call a list of users. I want to call them 20 by 20. The first call is done well, I have my first 20 users displayed and when I get to the bottom of my list I have a new API call that goes to display the next 20. This part also works.

My problem is that the list view in the dropdown doesn't refresh and doesn't show me my next 20 users and yet in the console I have my request that leaves with my 20 new users showing. If I close and reopen the dropdown the list is updated.

I contacted the developer who told me first to try to use this => myKey.currentState.reassemble() with my scrollController but it did not work.

He then explained that to solve my problem he could only see the use of a streambuilder. I tried it but it didn't work.

Would you have some hints to give me or something else please?

Here is a bit of what I did in code. In this example I not use StreamBuilder.

final GlobalObjectKey<DropdownSearchState<ListValueOptionList>> myKey = new GlobalObjectKey<DropdownSearchState<ListValueOptionList>>(50);
  bool isListValueModify = false;
  List<delegationModele.Delegation> listDelegations = [];
  int indexDelegationList;
  int pageCounter = 0;
  ScrollController _scrollController = ScrollController();

  List<ListValueOptionList> listValueOptionListModule;
  List<ListValueOptionList> listValueOptionListTypeOfDelegation;
  List<ListValueOptionList> listValueOptionsUserDelegation;
  List userAlreadyUse = [];

  @override
  void initState() {
    super.initState();

    _scrollController.addListener(() {
      if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
        print("Scroll event");
        _loadValueOptionListSpecificPeople();
        setState(() {});
        myKey.currentState.reassemble();
      }
    });
  } 

  @override
  Widget build(BuildContext context) {
    return Padding(
      /// Allows resize when kerboard is open if
      /// padding == MediaQuery.of(context).viewInsets
      padding: padding,
      child: Container(
        decoration:BoxDecoration(
          color: appStyleMode.primaryBackgroundColor,
          borderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
        ),
        height: modalHeight,
        child: Padding(
          padding: const EdgeInsets.only(left:30, top:30.0, right: 30, bottom: 20),
          child: ListView(
            shrinkWrap: false,
            children: [
                getModificationItemListDelegationBuilder(),
              ],
            ),
          ),
        ),
    );
  }

  SingleChildScrollView getModificationItemListDelegationBuilder(){
    final appStyleMode = Provider.of<AppStyleModeNotifier>(context);
    return SingleChildScrollView(
      child: Column(
        children: [
          ///Add specific people
          Container(
            width: MediaQuery.of(context).size.width,
            margin: EdgeInsets.only(bottom: 10, top: 10),
            child: Text(
              "${getTranslated(context, "my_delegation_specific_user")} : ",
              style: TextStyle(
                color: specificPeopleisEmpty ? Colors.red : appStyleMode.blackWhiteColor,
                fontSize: 16,
              ),
              textAlign: TextAlign.start,
            ),
          ),
          Container(
            child: ListView.builder(
              shrinkWrap: true,
              itemCount: delegation.userDelegationRecipients.length ?? 0,
              itemBuilder: (context, index){
                return specificPeople(listValueOptionsUserDelegation, index);
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget specificPeople(List<ListValueOptionList> listValueOptionList, int index){
    final appStyleMode = Provider.of<AppStyleModeNotifier>(context);
    delegationModele.UserDelegationRecipients userDelegation = delegation.userDelegationRecipients[index];

    String optionUserDescription;

    if(listValueOptionList != null){

      listValueOptionList.removeWhere((element) => userLoad.id.stringOf == element.idValue);

      for(ListValueOptionList valueOption in listValueOptionList){
          if(valueOption.idValue == userDelegation.delegationUserId){
            optionUserDescription = valueOption.listValueDescription;
          }
      }

      return Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Container(
            width: MediaQuery.of(context).size.width * 0.6,
            child: DropdownSearch<ListValueOptionList>(
              key: myKey,
              dropdownButtonProps: DropdownButtonProps(
                  color: appStyleMode.blackWhiteColor
              ),
              popupProps: PopupProps.menu(
                showSelectedItems: true,
                menuProps: MenuProps(
                  borderRadius: BorderRadius.all(Radius.circular(10.0)),
                  elevation: 5.0,
                  backgroundColor: appStyleMode.primaryBackgroundColor,
                ),
                textStyle: TextStyle(
                    color: appStyleMode.blackWhiteColor
                ),
                scrollbarProps: ScrollbarProps(
                  thumbVisibility: true,
                  thumbColor: appStyleMode.categorySelectorSelectedBackgroundColor,
                  interactive: true
                ),
                listViewProps: ListViewProps(
                  controller: _scrollController,
                  shrinkWrap: true,
                ),
                interceptCallBacks: true,
                itemBuilder: (context, list, isSelected){
                  return new Container(
                    child: ListTile(
                      selected: isSelected,
                      title: Text(
                        list?.listValueDescription,
                        style: TextStyle(
                            color: appStyleMode.blackWhiteColor
                        ),
                      ),
                    ),
                  );
                },
                loadingBuilder: (context, loadingText){
                  return Align(
                    alignment: Alignment.topCenter,
                    child: CircularProgressIndicator(),
                  );
                },
                emptyBuilder: (context, text){
                  return Align(
                    alignment: Alignment.topCenter,
                    child: Text(
                      "${getTranslated(context, "my_delegation_empty_search_user")}",
                      style: TextStyle(
                        color: appStyleMode.blackWhiteColor,
                      ),
                    ),
                  );
                },
                showSearchBox: true,
                searchDelay: Duration(seconds: 1),
                searchFieldProps: TextFieldProps(
                  decoration: InputDecoration(
                    enabled: true,
                    enabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0),
                        borderSide: BorderSide(
                            color: appStyleMode.categorySelectorSelectedBackgroundColor,
                            style: BorderStyle.solid
                        )
                    ),
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0),
                        borderSide: BorderSide(
                            style: BorderStyle.solid
                        )
                    ),
                    prefixIcon: Icon(
                      Icons.search_rounded,
                      size: 20,
                    ),
                    hintText: "${getTranslated(context, "my_delegation_search_hint_text")}",
                    hintStyle: TextStyle(
                        fontSize: 13
                    ),
                  ),
                  style: TextStyle(
                    color: appStyleMode.blackWhiteColor,
                  ),
                ),
              ),
              onChanged: (newValue){
                //OnChanges is well used
              },
              compareFn: (value1, value2) => value1.idValue == value2.idValue,
              dropdownDecoratorProps: DropDownDecoratorProps(
                  dropdownSearchDecoration: InputDecoration(
                      border: InputBorder.none
                  )
              ),
              //items: listValueOptionsUserDelegation,
              asyncItems: (filter) async{
                return _loadValueOptionListSpecificPeople();
              },
              itemAsString: (ListValueOptionList list) => list.listValueDescription,
              selectedItem: new ListValueOptionList(idValue: userDelegation.delegationUserId, listValueDescription: optionUserDescription),
              dropdownBuilder: (context, selectedItem){
                if(optionUserDescription == null || optionUserDescription.isEmpty){
                  return Container();
                }else if(optionUserDescription == ""){
                  return Text("");
                }else{
                  selectedItem.listValueDescription = optionUserDescription;
                }
                return textValueDescription(selectedItem.listValueDescription, appStyleMode, TextAlign.start);
              },
            ),
          ),
        ],
      );
    }else{
      return Container();
    }
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }
}
1 Answers

I've had a similar app. Maybe this code will help:

class PostsList extends StatefulWidget {
  @override
  State<PostsList> createState() => _PostsListState();
}

class _PostsListState extends State<PostsList> {
  final _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<PostBloc, PostState>(
      builder: (context, state) {
        switch (state.status) {
          case PostStatus.failure:
            return const Center(child: Text('failed to fetch posts'));
          case PostStatus.success:
            if (state.posts.isEmpty) {
              return const Center(child: Text('no posts'));
            }
            return ListView.builder(
              itemBuilder: (BuildContext context, int index) {
                return index >= state.posts.length
                    ? const BottomLoader()
                    : PostListItem(post: state.posts[index]);
              },
              itemCount: state.hasReachedMax
                  ? state.posts.length
                  : state.posts.length + 1,
              controller: _scrollController,
            );
          default:
            return const Center(child: CircularProgressIndicator());
        }
      },
    );
  }

  @override
  void dispose() {
    _scrollController
      ..removeListener(_onScroll)
      ..dispose();
    super.dispose();
  }

  void _onScroll() {
    if (_isBottom) context.read<PostBloc>().add(PostFetched());
  }

  bool get _isBottom {
    if (!_scrollController.hasClients) return false;
    final maxScroll = _scrollController.position.maxScrollExtent;
    final currentScroll = _scrollController.offset;
    return currentScroll >= (maxScroll * 0.9);
  }
}

I've used bloc for this but it should work as well with future builder and using snapshot. Basically user scrolls down, and if scrolled more than 90% of the list another getter is send to get more items from api. If you want to replicate the whole bloc (which I strongly recommend) take look at this documentation.

Related