Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does route.didPop(result) is equal to false in Flutter Navigator 2.0

One of the main mecanism of Flutter Navigator 2.0 it the function onPopPage inside RouterDelegate > build > Navigator. However, I do not understand when route.didPop(result) returns false.

We can use the John Ryan's famous example to show my question. His demo code.

onPopPage: (route, result) {
  if (!route.didPop(result)) {
    return false;
  }

  // Update the list of pages by setting _selectedBook to null
  _selectedBook = null;
  show404 = false;
  notifyListeners();

  return true;
},

On all of my tests, using AppBar autogenerated back button, route.didPop(result) returns true.

The doc stays :

bool didPop(dynamic result)
package:flutter/src/widgets/navigator.dart

A request was made to pop this route. If the route can handle it internally (e.g. because it has its own stack of internal state) then return false, otherwise return true (by returning the value of calling super.didPop). Returning false will prevent the default behavior of [NavigatorState.pop].

When this function returns true, the navigator removes this route from the history but does not yet call [dispose]. Instead, it is the route's responsibility to call [NavigatorState.finalizeRoute], which will in turn call [dispose] on the route. This sequence lets the route perform an exit animation (or some other visual effect) after being popped but prior to being disposed.

This method should call [didComplete] to resolve the [popped] future (and this is all that the default implementation does); routes should not wait for their exit animation to complete before doing so.

See [popped], [didComplete], and [currentResult] for a discussion of the result argument.

But was does "If the route can handle it internally (e.g. because it has its own stack of internal state) then return false" mean ? The route has its own stack of internal state ? How to produce this result ?

Thank you, stay safe

like image 598
Dimitri Leurs Avatar asked Nov 10 '20 16:11

Dimitri Leurs


People also ask

How does routing work in Flutter?

Flutter has an imperative routing mechanism, the Navigator widget, and a more idiomatic declarative routing mechanism (which is similar to build methods as used with widgets), the Router widget. The two systems can be used together (indeed, the declarative system is built using the imperative system).

How do I route to the same page in Flutter?

If the new page is the same as the current page then use Navigator. push(context,route) . If the new page is the different then use Navigator. pushReplacement(context,route) .

What is my current route in Flutter?

When a button in the bottom bar is clicked, its current route path (/a/b/c) is saved and previously saved route is restored according to the button click. Conceptually user will think each button as a workspace and its state is never get lost (including back stack).


1 Answers

After some research to fully understand the Navigator 2.0, I think this might be the answer to the question: route.didPop(result) will return false, when the Route, which are asked to pop, keeps local history entries and they have to be removed before popping the complete Route.

So what are local history entries (the stack of internal states)?

Local history entries are a way to implement local navigation within a page. You can do so using the method addLocalHistoryEntry. To understand this better, take a look at the official Flutter Docs sample:

The following example is an app with 2 pages: HomePage and SecondPage. The HomePage can navigate to the SecondPage. The SecondPage uses a LocalHistoryEntry to implement local navigation within that page. Pressing 'show rectangle' displays a red rectangle and adds a local history entry. At that point, pressing the '< back' button pops the latest route, which is the local history entry, and the red rectangle disappears. Pressing the '< back' button a second time once again pops the latest route, which is the SecondPage, itself. Therefore, the second press navigates back to the HomePage.

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (BuildContext context) => HomePage(),
        '/second_page': (BuildContext context) => SecondPage(),
      },
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage();

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Text('HomePage'),
            // Press this button to open the SecondPage.
            ElevatedButton(
              child: Text('Second Page >'),
              onPressed: () {
                Navigator.pushNamed(context, '/second_page');
              },
            ),
          ],
        ),
      ),
    );
  }
}

class SecondPage extends StatefulWidget {
  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {

  bool _showRectangle = false;

  void _navigateLocallyToShowRectangle() async {
    // This local history entry essentially represents the display of the red
    // rectangle. When this local history entry is removed, we hide the red
    // rectangle.
    setState(() => _showRectangle = true);
    ModalRoute.of(context).addLocalHistoryEntry(
        LocalHistoryEntry(
            onRemove: () {
              // Hide the red rectangle.
              setState(() => _showRectangle = false);
            }
        )
    );
  }

  @override
  Widget build(BuildContext context) {
    final localNavContent = _showRectangle
      ? Container(
          width: 100.0,
          height: 100.0,
          color: Colors.red,
        )
      : ElevatedButton(
          child: Text('Show Rectangle'),
          onPressed: _navigateLocallyToShowRectangle,
        );

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            localNavContent,
            ElevatedButton(
              child: Text('< Back'),
              onPressed: () {
                // Pop a route. If this is pressed while the red rectangle is
                // visible then it will will pop our local history entry, which
                // will hide the red rectangle. Otherwise, the SecondPage will
                // navigate back to the HomePage.
                Navigator.of(context).pop();
              },
            ),
          ],
        ),
      ),
    );
  }
}

To see the sample in the docs, click here.

I hope I answered the question in an understandable way.

like image 125
Nico Avatar answered Oct 11 '22 19:10

Nico