I've build a suggestion list for my location search delegate that always has an item at the top that pushes the user to a new page. On this new page the user can choose a location on a map and submit it or go back to the search page. If the user submits the location I want to close the underlying location search delegate with the chosen location as result.
For this behavior I'm using callbacks in my stateless suggestion list widget but in the case of the onMapTapped callback the app throws an exception:
class LocationSearchDelegate extends SearchDelegate<Location> {
// ...
@override
Widget buildSuggestions(BuildContext context) {
return _SuggestionList(
query: query,
onSelected: (Location suggestion) {
result = suggestion;
close(context, result);
},
onMapTapped: (Location location) {
result = location;
close(context, result); // <- Throws exception
},
);
}
// ...
}
The Exception:
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.
The context of the buildSuggestions
method becomes stale in the meantime because of the navigation to the ChooseOnMapPage
:
class _SuggestionList extends StatelessWidget {
// ...
void _handleOnChooseOnMapTap(BuildContext context) async {
Location location = await Navigator.of(context).push(
MaterialPageRoute<Location>(builder: (context) {
return ChooseLocationPage();
}),
);
onMapTapped(location);
}
// ...
}
The workaround
The current workaround is to show the results and then close the search delegate immediately:
class LocationSearchDelegate extends SearchDelegate<Location> {
// ...
@override
Widget buildSuggestions(BuildContext context) {
return _SuggestionList(
query: query,
onSelected: (Location suggestion) {
result = suggestion;
close(context, result);
},
onMapTapped: (Location location) {
result = location;
showResults(context); // <- Throws NO exception
},
);
}
// ...
@override
Widget buildResults(BuildContext context) {
Future.delayed(Duration.zero, () {
close(context, result);
});
return Container();
}
// ...
}
I don't like this workaround so any suggestions on how to fix this?