Skip to content

Actions.handler always returns null since 3.47.0 (Actions.maybeFind no longer forwards intent type) #191045

Closed

Description

@adam-langley-minrisk

Steps to reproduce

  1. Register an Action for a custom Intent subtype via an Actions widget:
    Actions(
      actions: <Type, Action<Intent>>{
        MyIntent: CallbackAction<MyIntent>(onInvoke: (intent) { ... }),
      },
      child: ...,
    )
  2. Further down the tree, resolve a callback with:
    final callback = Actions.handler(context, MyIntent(...));
  3. callback is always null, even though a matching Actions ancestor exists.

Expected results

Actions.handler<T>() finds the registered Action<T> for the intent's runtime type and returns a working VoidCallback, as it did before 3.47.0.

Actual results

Actions.handler always returns null.

Root cause

Actions.handler (packages/flutter/lib/src/widgets/actions.dart) was changed by #180883 ("Fix some type checks in the Actions widget") from:

static VoidCallback? handler<T extends Intent>(BuildContext context, T intent) {
  final Action<T>? action = Actions.maybeFind<T>(context);
  ...
}

to:

static VoidCallback? handler<T extends Intent>(BuildContext context, T intent) {
  final Action<Intent>? action = Actions.maybeFind(context);
  ...
}

The new call no longer passes an explicit type argument <T> nor the intent: parameter. Dart's downward type inference resolves maybeFind's type parameter from the declared local variable type (Action<Intent>?), so it infers T = Intent instead of the caller's actual intent type. Inside maybeFind -> _getActionForIntent<T>, the lookup key becomes:

actionsMarker.actions[intent?.runtimeType ?? T]

Since intent was never forwarded, this evaluates to actionsMarker.actions[Intent] (the literal Intent type) instead of the registered subtype key (e.g. MyIntent). Apps register actions keyed by their concrete Intent subtype, not by Intent itself, so the lookup always misses and Actions.handler returns null universally, for any intent type.

Actions.invoke / Actions.maybeInvoke are unaffected, since they forward intent directly to _getActionForIntent.

Suggested fix

Forward the intent and type explicitly in Actions.handler:

static VoidCallback? handler<T extends Intent>(BuildContext context, T intent) {
  final Action<Intent>? action = Actions.maybeFind<T>(context, intent: intent);
  ...
}

Flutter/Dart/Device info

Flutter 3.47.0 • channel stable • revision 4cf24164269a5ebf0c16a028a00727d0e77bbb05
Engine • revision 5f77625673248ee5846fbcaf5d3e1a3878386fd7
Dart 3.13.0

Regressed by commit f7689c7 (#180883), first shipped in 3.47.0-0.1.pre, present in 3.47.0 stable.

Activity

  1. suryaKommana2662 commented on Aug 13, 2026

    @suryaKommana2662
    Contributor

    @adam-langley-minrisk Thanks for the detailed report. I was able to reproduce the issue on Flutter 3.47.0. With the reproduction below, Actions.handler returns null, and the terminal prints:
    Actions.handler returned: null
    I also tested the same reproduction on Flutter 3.44.8, where I was unable to reproduce the issue and Actions.handler returned a non-null callback as expected.

    Here is the reproduction code I used:

    import 'package:flutter/material.dart';
    
    class MyIntent extends Intent {
      const MyIntent();
    }
    
    void main() {
      runApp(
        Actions(
          actions: <Type, Action<Intent>>{
            MyIntent: CallbackAction<MyIntent>(
              onInvoke: (MyIntent intent) {
                debugPrint('MyIntent invoked');
                return null;
              },
            ),
          },
          child: const MaterialApp(
            home: TestPage(),
          ),
        ),
      );
    }
    
    class TestPage extends StatelessWidget {
      const TestPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Builder(
          builder: (context) {
            final VoidCallback? callback =
                Actions.handler(context, const MyIntent());
    
            debugPrint('Actions.handler returned: $callback');
    
            return Scaffold(
              appBar: AppBar(title: const Text('Actions.handler test')),
              body: Center(
                child: ElevatedButton(
                  onPressed: callback,
                  child: const Text('Invoke MyIntent'),
                ),
              ),
            );
          },
        );
      }
    }

    Flutter Doctor Output

    [✓] Flutter (Channel stable, 3.47.0, on macOS 26.4 25E246 darwin-arm64, locale en-IN) [168ms]
        • Flutter version 3.47.0 on channel stable at /Users/suryakommana/develop/flutter
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 4cf2416426 (35 hours ago), 2026-08-11 11:53:49 -0700
        • Engine revision 5f77625673
        • Dart version 3.13.0
        • DevTools version 2.60.0
        • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets,
          enable-record-use, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [1,237ms]
        • Android SDK at /Users/suryakommana/Library/Android/sdk
        • Emulator version 37.1.11.0 (build_id 15917651) (CL:N/A)
        • Platform android-37.0, build-tools 36.0.0
        • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
          This is the JDK bundled with the latest Android Studio installation on this machine.
          To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
        • Java version OpenJDK Runtime Environment (build 25.0.2+-15348964-b329.117)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 26.6) [413ms]
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Build 17F113
        • CocoaPods version 1.17.0
    
    [✓] Chrome - develop for the web [3ms]
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Connected device (3 available) [5.6s]
        • iPhone 17 (mobile) • E354ECD5-7984-44E0-9C38-082B39E72C88 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-26-5 (simulator)
        • macOS (desktop)    • macos                                • darwin-arm64   • macOS 26.4 25E246 darwin-arm64
        • Chrome (web)       • chrome                               • web-javascript • Google Chrome 151.0.7922.109
    
    [✓] Network resources [489ms]
        • All expected network resources are available.
    
    • No issues found!
  2. added
    c: regressionIt was better in the past than it is now
    frameworkflutter/packages/flutter repository. See also f: labels.
    and removed
    in triagePresently being triaged by the triage team
    on Aug 13, 2026
  3. added a commit that references this issue on Aug 13, 2026
    8876d37
  4. added
    P1High-priority issues at the top of the work list
    on Aug 18, 2026
  5. TomKrauss commented on Aug 19, 2026

    @TomKrauss

    This is breaking a large part of my application with Flutter 3.47. Please merge PR #191052 as soon as possible.

  6. added a commit that references this issue on Aug 20, 2026
    4adbf28
  7. added a commit that references this issue on Sep 2, 2026
    4440754
  8. github-actions commented on Sep 3, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  9. locked as resolved and limited conversation to collaborators on Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

P1High-priority issues at the top of the work listc: regressionIt was better in the past than it is nowframeworkflutter/packages/flutter repository. See also f: labels.team-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