Skip to content

Specifying Page.onPopInvoked results in an exception #153150

@wujek-srujek

Description

@wujek-srujek

Steps to reproduce

  1. Start the sample app.
  2. tap 'Push'.
  3. Tap 'Pop' to pop the route with a result.

Expected results

No exception is thrown and the callback is correctly called with correct information, e.g. the value passed to pop.

Actual results

An exception is thrown:

The following _TypeError was thrown building Builder:
type '(bool, String?) => void' is not a subtype of type '(bool, Object?) => void'

The relevant error-causing widget was:
    MaterialApp MaterialApp:file:///Users/wujek/Development/DMC/VSCodeProjects/renault_group_media_control/rgmc/packages/srouter/example/lib/main.dart:22:17

When the exception was thrown, this was the stack:
#0      Route.onPopInvokedWithResult (package:flutter/src/widgets/navigator.dart:390:12)
#1      ModalRoute.onPopInvokedWithResult (package:flutter/src/widgets/routes.dart:1782:11)
#2      _RouteEntry.handlePop (package:flutter/src/widgets/navigator.dart:3171:11)
#3      NavigatorState._flushHistoryUpdates (package:flutter/src/widgets/navigator.dart:4340:22)
#4      NavigatorState._updatePages (package:flutter/src/widgets/navigator.dart:4270:5)
#5      NavigatorState.didUpdateWidget (package:flutter/src/widgets/navigator.dart:3911:7)
#6      StatefulElement.update (package:flutter/src/widgets/framework.dart:5789:55)
#7      Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
#8      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
#9      Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
#10     StatelessElement.update (package:flutter/src/widgets/framework.dart:5693:5)
#11     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
#12     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
#13     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
#14     ProxyElement.update (package:flutter/src/widgets/framework.dart:5946:5)
#15     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
#16     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
#17     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
#18     ProxyElement.update (package:flutter/src/widgets/framework.dart:5946:5)
#19     Element.updateChild (package:flutter/src/widgets/framework.dart:3941:15)
#20     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5642:16)
#21     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5780:11)
#22     Element.rebuild (package:flutter/src/widgets/framework.dart:5333:7)
#23     BuildScope._tryRebuild (package:flutter/src/widgets/framework.dart:2693:15)
#24     BuildScope._flushDirtyElements (package:flutter/src/widgets/framework.dart:2752:11)
#25     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:3048:18)
#26     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:1162:21)
#27     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:468:5)
#28     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1397:15)
#29     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1318:9)
#30     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1176:5)
#31     _invoke (dart:ui/hooks.dart:312:13)
#32     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:419:5)
#33     _drawFrame (dart:ui/hooks.dart:283:31)

The problem seems to be this line: https://github.com/flutter/flutter/blob/3.24.0/packages/flutter/lib/src/widgets/navigator.dart#L389

It is

final Page<Object?> page = settings as Page<Object?>;

and when I change it locally to

final Page<T> page = settings as Page<T>;

the exception is no longer thrown and the callback is correctly called.

Code sample

Code sample
import 'dart:async';

import 'package:flutter/material.dart';

Page<String> _createPage<String>(Widget child) {
  return MaterialPage<String>(
    child: child,
    onPopInvoked: (didPop, result) {}, // <===
  );
}

final _routerDelegate = _RouterDelegate()
  ..push(_createPage(_FirstPageWidget()));
final _routerConfig = RouterConfig<Uri>(
  routerDelegate: _routerDelegate,
);

void main() {
  runApp(
    MaterialApp.router(
      routerConfig: _routerConfig,
    ),
  );
}

class _FirstPageWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            final result = await _routerDelegate.push(
              _createPage(_SecondPageWidget()),
            );

            print('<<<>>> Result: $result');
          },
          child: const Text('Push'),
        ),
      ),
    );
  }
}

class _SecondPageWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () => _routerDelegate.pop('FooBarBazQux'),
          child: const Text('Pop'),
        ),
      ),
    );
  }
}

class _RouterDelegate extends RouterDelegate<Uri> with ChangeNotifier {
  List<Page<String>> _pages = [];
  final Map<Page<String>, Completer<String?>> _pageCompleters = Map.identity();

  @override
  Widget build(BuildContext context) {
    return Navigator(
      onDidRemovePage: _pages.remove,
      pages: _pages,
    );
  }

  Future<String?> push(Page<String> page) {
    _pages = [..._pages..add(page)];
    final completer = Completer<String?>();
    _pageCompleters[page] = completer;

    notifyListeners();

    return completer.future;
  }

  void pop(String? result) {
    final page = _pages.removeLast();
    _pages = [..._pages];
    final completer = _pageCompleters.remove(page);

    completer?.complete(result);

    notifyListeners();
  }

  @override
  Future<bool> popRoute() => throw StateError('irrelevant for this repro');

  @override
  Future<void> setNewRoutePath(Uri configuration) =>
      throw StateError('irrelevant for this repro');
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.24.0, on macOS 14.6.1 23G93 darwin-arm64, locale en-DE)
    • Flutter version 3.24.0 on channel stable at /Users/wujek/fvm/versions/3.24.0
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 80c2e84975 (10 days ago), 2024-07-30 23:06:49 +0700
    • Engine revision b8800d88be
    • Dart version 3.5.0
    • DevTools version 2.37.2

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/wujek/Library/Android/sdk
    • Platform android-34, build-tools 34.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15F31d
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314)

[✓] VS Code (version 1.92.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.94.0

[✓] Connected device (5 available)
    • SM S908B (mobile)               • R3CTB08NJTT               • android-arm64  • Android 14 (API 34)
    • iPhone 14 (mobile)              • 00008110-000C2CD80281401E • ios            • iOS 17.5.1 21F90
    • macOS (desktop)                 • macos                     • darwin-arm64   • macOS 14.6.1 23G93 darwin-arm64
    • Mac Designed for iPad (desktop) • mac-designed-for-ipad     • darwin         • macOS 14.6.1 23G93 darwin-arm64
    • Chrome (web)                    • chrome                    • web-javascript • Google Chrome 127.0.6533.100

[✓] Network resources
    • All expected network resources are available.

• No issues found!

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listc: crashStack traces logged to the consolef: routesNavigator, Router, and related APIs.found in release: 3.24Found to occur in 3.24frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer versionteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions