Widget removed from tree after pushing route in Flutter

Viewed 54

I've come across some unexpected behaviour when pushing and popping a route that the Flutter docs haven't been able to explain.

Specifically, when pushing a page route (which is then subsequently popped) from within the results of a SearchDelegate that is added to the widget tree via the showSearch() function, the widget that is used to display the results is removed from the widget tree and enters an unmounted state.

Consequently, the BuildContext that belongs to the widget doing the routing is disposed of, and thus any use of context in the build method causes an error. I've provided a sample Flutter program below that illustrates this issue (it's a bit lengthy so bear with me):

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Builder(
            builder: (BuildContext context) {
              return TextButton(
                child: Text("Search"),
                onPressed: () {
                  showSearch(context: context, delegate: Search());
                }
              );
            }
          )
        )
      )
    );
  }
}

class Search extends SearchDelegate {
  @override
  Widget buildResults(BuildContext context) => SearchResults();
  // Irrelevant
  @override
  List<Widget> buildActions(BuildContext context) => null;
  @override
  Widget buildLeading(BuildContext context) => null;
  @override
  Widget buildSuggestions(BuildContext context) => Text("Suggestions");
}

class SearchResults extends StatefulWidget {
  @override
  SearchResultsState createState() => SearchResultsState();
}

class SearchResultsState extends State<SearchResults> {
  @override
  Widget build(BuildContext context) {
    return TextButton(
      child: Text("Push"),
      onPressed: () async {
        await Navigator.push<String>(
          context,
          MaterialPageRoute<String>(
            builder: (BuildContext context) => RouteA()
          )
        );
        // ============================= ERROR =============================
        print(mounted); // This should print true (spoiler: it prints false)!
        // =================================================================
      }
    );
  }
}

class RouteA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child:
        TextButton(
          child: Text("Pop"),
          onPressed: () {
            Navigator.of(context).pop();
          }
        )
      )
    );
  }
}

Please note that this is a simplified version of the program in which I originally witnessed this issue. I'm aware that the widget hierarchy I've presented here may seem a bit nonsensical; however, I'm more interested in why this is happening in the first place for this specific configuration.

Any explanations and appropriate workarounds would be greatly appreciated!

0 Answers
Related