I'm trying to do a search in my application and I'm using this SearchDelegate for that. Previously, when I used a provider, everything worked, but I had to make serious changes in the code and now this algorithm below is responsible for finding routes. I am trying to put RouteWithStops in SearchDelegate, and after that to use FutureBuilder inside Widget buildSuggestions. So the code is like this:

The algorithm for searching routes with stops with dart models:

Future<List<RouteWithStops>> getMarshrutWithStops(int ttId) async {
    if (routesbyTransportType.isEmpty) {
      await fetchTransportWithRoutes();
    }
    List<Routes> routes = routesbyTransportType[ttId].routes;
    List<ScheduleVariants> variants = [];

    variants.addAll(await api.fetchSchedule());

    List<RouteWithStops> routesWithStops = [];

    for (Routes route in routes) {
      final routeWithStops = RouteWithStops();

      routesWithStops.add(routeWithStops);
      routeWithStops.route = route;

      routeWithStops.variant =
          variants.where((variant) => variant.mrId == route.mrId).first;
    }
    return routesWithStops;
  }

  Future<RouteWithStops> fetchStopsInfo(routeWithStops) async {
    List<RaceCard> cards = [];
    List<StopList> stops = [];
    cards.addAll(await api.fetchRaceCard(routeWithStops.variant.mvId));
    stops.addAll(await api.fetchStops());
    print(cards);
    List<StopList> currentRouteStops = [];

    cards.forEach((card) {
      stops.forEach((stop) {
        if (card.stId == stop.stId) {
          currentRouteStops.add(stop);
        }
      });
    });
    routeWithStops.stop = currentRouteStops;
    return routeWithStops;
  }

}

the models:

@HiveType(typeId: 0)
class RouteWithStops {
  @HiveField(0)
  Routes route;
  @HiveField(1)
  List<StopList> stop;
  @HiveField(2)
  List<RaceCard> cards;
  @HiveField(3)
  ScheduleVariants variant;
  @HiveField(4)
  Transport transport;

}

The SearchDelegate which I want to use to search in a list of routes, the single route I want:

class SearchBar extends SearchDelegate<RouteWithStops> {
  final int ttId;
  final RouteWithStops routeWithStops;
  TransportService service = getIt<TransportService>();

  SearchBar({this.ttId, this.routeWithStops});

  @override
  List<Widget> buildActions(BuildContext context) {
    return [
      IconButton(
          icon: Icon(Icons.clear),
          onPressed: () {
            query = '';
          })
    ];
  }

  @override
  Widget buildLeading(BuildContext context) {
    return IconButton(
        icon: AnimatedIcon(
          icon: AnimatedIcons.menu_arrow,
          progress: transitionAnimation,
        ),
        onPressed: () {
          close(context, null);
        });
  }

  @override
  Widget buildResults(BuildContext context) {

    return FutureBuilder(
        future: service.getMarshrutWithStops(ttId),
        builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
          List<RouteWithStops> routes = snapshot.data;
          List<RouteWithStops> routes = [];
          List<RouteWithStops> recentRoutes = [];
          final suggestion = query.isEmpty
              ? recentRoutes
              : routes
              .where((element) => element.route.mrTitle.startsWith(query))
              .toList();
          print(routes?.toString());
          return (routes == null)
              ? Center(child: CircularProgressIndicator())
              : ListView.builder(
                  itemCount: suggestion.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: RichText(
                        text: TextSpan(
                          text: suggestion[index]
                              .route
                              .mrTitle
                              .substring(0, query.length),
                          style: TextStyle(
                              color: Colors.black, fontWeight: FontWeight.bold),
                        ),
                      ),
                    );
                  });
        });
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    return FutureBuilder(
        future: service.getMarshrutWithStops(ttId),
        builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
          List<RouteWithStops> routes = snapshot.data;
          List<RouteWithStops> recentRoutes = [];
          final suggestion = query.isEmpty
              ? recentRoutes
              : routes
                  .where((element) => element.route.mrTitle.startsWith(query))
                  .toList();
          print(routes?.toString());
          return (routes == null)
              ? Center(child: CircularProgressIndicator())
              : ListView.builder(
                  itemCount: suggestion.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: RichText(
                        text: TextSpan(
                          text: suggestion[index]
                              .route
                              .mrTitle
                              .substring(0, query.length),
                          style: TextStyle(
                              color: Colors.black, fontWeight: FontWeight.bold),
                        ),
                      ),
                    );
                  });
        });
  }
}

When i am trying to serach an item in items I get the progress indicator and after I trying to type one letter my app crushes and I got this error:

The method 'where' was called on null.
Receiver: null
Tried calling: where(Closure: (RouteWithStops) => bool)

I understand that this error is pretty straightforward and it sayas that something is null inside that algorithm, but I use this algo in the whole app and everything works fine without any errors. But may be I think wrongly in using SearchDelegate. Can somebody, please, help me?


Solution 1: nvoigt

future: service.getMarshrutWithStops(ttId),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
List<RouteWithStops> routes = snapshot.data;
List<RouteWithStops> recentRoutes = [];
final suggestion = query.isEmpty
? recentRoutes
: routes
.where((element) => element.route.mrTitle.startsWith(query))
.toList();
print(routes?.toString());

Your snapshot.data is null. You have a FutureBuilder and no check on the status of the future at all. It might have failed or still be processed.

Also, don't call your Future generating method in your build method. Use a variable. Your build method can be called many times and you don't want to generate a new future every time that happens.