5

I am using the package flutter_bloc for state management. I want to create a search screen, and found the showSearch Flutter function, and have been having issues providing a BLoC instance to the ListView my SearchDelegate implementation creates. I finally made it work, but would like to ask what the best way of doing this is. Here is the code (excerpts, starting from a button that is placed in an AppBar within a Scaffold):

class ItemSearchButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return IconButton(
      icon: Icon(Icons.search),
      onPressed: () {
        final itemListBloc = context.bloc<ItemListBloc>();
        showSearch(
          context: context,
          delegate: _ItemSearchDelegate(itemListBloc),
        );
      },
    );
  }
}

class _ItemSearchDelegate extends SearchDelegate<String> {
  final ItemListBloc itemListBloc;

  _ItemSearchDelegate(this.itemListBloc);

  // other overridden methods

  @override
  Widget buildSuggestions(BuildContext context) {
    return BlocProvider.value(
      value: itemListBloc,
      child: ItemListWidget(),
    );
  }
}

Basically, the context that invokes the showSearch method has the correct BLoC instance, but it is not available within my SearchDelegate implementation, unless I re-provide it again explicitly in buildSuggestions.

Why is the BLoC not available by default? The showSearch function internally pushes a new Navigator Route, is this the issue?

What is the canonical way of dealing with things like this?

wujek
  • 10,112
  • 12
  • 52
  • 88

3 Answers3

1

Yes, when the route changes, buildContextchanges too. So you have to provide that bloc to the new context. Just wrap your page where you want to navigate with BlocProvider:

Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => 
BlocProvider(create: Mybloc(),child:MyPage()); 
dKen
  • 3,078
  • 1
  • 28
  • 37
  • 2
    The problem is the navigation is performed by the `showSearch` function, i.e. I can't change it. I already do what you are saying for my own routes without any issues. – wujek May 23 '20 at 16:09
1

In the end it works as intended - the pushed route has a new context that is not a child of a context that has my BLoC, it is a child of the Navigator. The solution is to either do what I did initially - pass the BLoC explicitly as constructor argument - or make sure the Navigator context has the BLoCs, which is what I eventually did; to do this, make sure the Navigator is a child of the (Multi)BlocProvider.

wujek
  • 10,112
  • 12
  • 52
  • 88
0
class DiscoverSearchDelegate extends SearchDelegate {
final AppSearchBloc appSearchBloc = AppSearchBloc();
....
}

and then use Bloc provider+builder to show changes

@override
  Widget buildSuggestions(BuildContext context) {
    appSearchBloc.add(AppSearchEvent.changeQuery(query));
    return BlocProvider.value(
      value: appSearchBloc,
      child: BlocBuilder<AppSearchBloc, AppSearchState>(
        builder: (context, state) {
          return SearchHistory(
            state.searchStory,
            onSearch: (value) {
              query = value;
              // showResults(context);
            },
          );
        },
      ),
    );
  }
Hamdam Muqimov
  • 319
  • 2
  • 7
  • 1
    I don't think this is good advice. `buildSuggestions` is called from `build`, which is supposed not to have side effects, and you are sending events to `appSearchBloc` there, which triggers them. – wujek May 12 '23 at 08:34
  • I just found an example in bloc docs [Using bloc with SearchDelegate](https://gist.github.com/felangel/11769ab10fbc4076076299106f48fc95) – Hamdam Muqimov May 12 '23 at 09:19