Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

go_router's canPop is updated one frame late #124637

Copy link
Copy link
Open
@justinmc

Description

@justinmc
Issue body actions

GoRouter's canPop method gives out of date results until the following frame after the navigation.

Steps to reproduce

  1. Run the app below, which listens to GoRouter and logs its state.
  2. Navigate to the next route.

Expected:
When GoRouter's listener is called, its canPop is true, because there are two routes on the stack.

GoRouter changed. Location: /one. canPop? true
After the post frame callback. Location: /one. canPop? true

Actual:
GoRouter's listener is called, and the location is correct, but canPop is still out of date and gives false. After a post frame callback, it is up to date and returns true.

GoRouter changed. Location: /one. canPop? false
After the post frame callback. Location: /one. canPop? true
Example app
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/scheduler.dart';
import 'package:go_router/go_router.dart';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

class _MyApp extends StatefulWidget {

  @override
  State<_MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<_MyApp> {
  final GoRouter _router = GoRouter(
    routes: <RouteBase>[
      GoRoute(
        path: '/',
        builder: (BuildContext context, GoRouterState state) => _LinksPage(
          title: 'Home page',
          backgroundColor: Colors.indigo,
          buttons: <Widget>[
            TextButton(
              onPressed: () {
                context.push('/one');
              },
              child: const Text('Go to one'),
            ),
          ],
        ),
      ),
      GoRoute(
        path: '/one',
        builder: (BuildContext context, GoRouterState state) => _LinksPage(
          title: 'Page one',
          backgroundColor: Colors.indigo.withRed(255),
          buttons: <Widget>[
            TextButton(
              onPressed: () {
                context.push('/one/two');
              },
              child: const Text('Go to one/two'),
            ),
          ],
        ),
      ),
      GoRoute(
        path: '/one/two',
        builder: (BuildContext context, GoRouterState state) => _LinksPage(
          title: 'Page one/two',
          backgroundColor: Colors.indigo.withBlue(255),
        ),
      ),
    ],
  );

  void _routerChanged() {
    print('GoRouter changed. Location: ${_router.location}. canPop? ${_router.canPop()}');
    SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
      print('After the post frame callback. Location: ${_router.location}. canPop? ${_router.canPop()}');
      SystemNavigator.updateNavigationStackStatus(_router.canPop());
    });
  }

  @override
  void initState() {
    super.initState();
    _router.addListener(_routerChanged);
  }

  @override
  void dispose() {
    _router.removeListener(_routerChanged);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

class _LinksPage extends StatelessWidget {
  const _LinksPage ({
    required this.backgroundColor,
    this.buttons = const <Widget>[],
    required this.title,
  });

  final Color backgroundColor;
  final List<Widget> buttons;
  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: backgroundColor,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(title),
            ...buttons,
            if (GoRouter.of(context).canPop())
              TextButton(
                onPressed: () {
                  context.pop();
                },
                child: const Text('Go back'),
              ),
          ],
        ),
      ),
    );
  }
}

Filed this issue after discovering while working on predictive back and talking to @chunhtai offline.

john-regalado-novade, AhmedLSayed9, tenhobi, ryzizub and Kuhlemann

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listImportant issues not at the top of the work listfound in release: 3.10Found to occur in 3.10Found to occur in 3.10found in release: 3.7Found to occur in 3.7Found to occur in 3.7has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packageThe go_router packagepackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.team-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.