Skip to content

Action.overridable cannot be overridden by a DoNothingAction #180435

Closed

Description

@lebeshev

Steps to reproduce

  1. Wrap a TextField with Actions and use DoNothingAction for all text editing Intents (see code sample)
  2. Run it, type in some text.
  3. Do some text editing related shortcuts like arrow left, arrow up, ctrl + A, backspace etc.

Expected results

  1. arrow up, arrow down, page up, page down, home and end or in other words ExtendSelectionVerticallyToAdjacentPageIntent and ExtendSelectionVerticallyToAdjacentLineIntent do nothing
  2. for all other intents no assertion errors is thrown

Actual results

  1. ExtendSelectionVerticallyToAdjacentPageIntent and ExtendSelectionVerticallyToAdjacentLineIntent moves caret to the start/end of the text
  2. When all other intents are executed, the following assertion error is thrown (example for SelectAllTextIntent):
════════ Exception caught by services library ══════════════════════════════════
The following assertion was thrown while processing the key message handler:
SelectAllTextIntent cannot be handled by an Action of runtime type DoNothingAction.
'package:flutter/src/widgets/actions.dart':
Failed assertion: line 926 pos 9: 'false'

Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=02_bug.yml

When the exception was thrown, this was the stack:
#2      Actions._castAction (package:flutter/src/widgets/actions.dart:926:9)
actions.dart:926
#3      Actions._maybeFindWithoutDependingOn.<anonymous closure> (package:flutter/src/widgets/actions.dart:907:33)
actions.dart:907
#4      Actions._visitActionsAncestors (package:flutter/src/widgets/actions.dart:746:18)
actions.dart:746
#5      Actions._maybeFindWithoutDependingOn (package:flutter/src/widgets/actions.dart:905:5)
actions.dart:905
#6      _OverridableActionMixin.getOverrideAction (package:flutter/src/widgets/actions.dart:1637:19)
actions.dart:1637
#7      _OverridableActionMixin.isEnabled (package:flutter/src/widgets/actions.dart:1706:39)
actions.dart:1706
#8      Action._isEnabled (package:flutter/src/widgets/actions.dart:247:45)
actions.dart:247
#9      ActionDispatcher.invokeActionIfEnabled (package:flutter/src/widgets/actions.dart:664:16)
actions.dart:664
#10     ShortcutManager.handleKeypress (package:flutter/src/widgets/shortcuts.dart:935:9)
shortcuts.dart:935
#11     _ShortcutsState._handleOnKeyEvent (package:flutter/src/widgets/shortcuts.dart:1135:20)
shortcuts.dart:1135
#12     _HighlightModeManager.handleKeyMessage (package:flutter/src/widgets/focus_manager.dart:2244:72)
focus_manager.dart:2244
#13     KeyEventManager._dispatchKeyMessage (package:flutter/src/services/hardware_keyboard.dart:1119:34)
hardware_keyboard.dart:1119
#14     KeyEventManager.handleRawKeyMessage (package:flutter/src/services/hardware_keyboard.dart:1195:17)
hardware_keyboard.dart:1195
#15     BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:259:49)
platform_channel.dart:259
#16     _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:665:35)
binding.dart:665
#17     _invoke2 (dart:ui/hooks.dart:388:13)
hooks.dart:388
#18     _ChannelCallbackRecord.invoke (dart:ui/channel_buffers.dart:45:5)
channel_buffers.dart:45
#19     _Channel.push (dart:ui/channel_buffers.dart:136:31)
channel_buffers.dart:136
#20     ChannelBuffers.push (dart:ui/channel_buffers.dart:344:17)
channel_buffers.dart:344

Code sample

Code sample
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
      home: Scaffold(
        body: Actions(
          actions: {
            DeleteCharacterIntent: DoNothingAction(),
            DeleteToNextWordBoundaryIntent: DoNothingAction(),
            DeleteToLineBreakIntent: DoNothingAction(),
            ExtendSelectionByCharacterIntent: DoNothingAction(),
            ExtendSelectionToNextWordBoundaryIntent: DoNothingAction(),
            ExtendSelectionToNextWordBoundaryOrCaretLocationIntent:
                DoNothingAction(),
            ExtendSelectionToLineBreakIntent: DoNothingAction(),
            ExtendSelectionVerticallyToAdjacentLineIntent: DoNothingAction(),
            ExtendSelectionVerticallyToAdjacentPageIntent: DoNothingAction(),
            ExtendSelectionToDocumentBoundaryIntent: DoNothingAction(),
            SelectAllTextIntent: DoNothingAction(),
            ReplaceTextIntent: DoNothingAction(),
            UpdateSelectionIntent: DoNothingAction(),
            CopySelectionTextIntent: DoNothingAction(),
            PasteTextIntent: DoNothingAction(),
          },
          child: const TextField(enableInteractiveSelection: false),
        ),
      ),
    );
  }
}

Screenshots or Video

No response

Logs

No response

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.38.5, on Pop!_OS 24.04 LTS 6.17.9-76061709-generic, locale en_US.UTF-8) [26ms]
    • Flutter version 3.38.5 on channel stable at /home/alexander/.develop/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f6ff1529fd (3 weeks ago), 2025-12-11 11:50:07 -0500
    • Engine revision 1527ae0ec5
    • Dart version 3.10.4
    • DevTools version 2.51.1
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios,
      cli-animations, enable-native-assets, omit-legacy-version-file, enable-lldb-debugging

[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [998ms]
    • Android SDK at /home/alexander/.android/sdk/
    • Emulator version 36.3.10.0 (build_id 14472402) (CL:N/A)
    • Platform android-36, build-tools 36.1.0
    • Java binary at: /home/alexander/.local/android-studio/jbr/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 21.0.8+-14196175-b1038.72)
    • All Android licenses accepted.

[✓] Chrome - develop for the web [8ms]
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop [370ms]
    • Ubuntu clang version 18.1.3 (1ubuntu1)
    • cmake version 3.28.3
    • ninja version 1.11.1
    • pkg-config version 1.8.1
    • OpenGL core renderer: Mesa Intel(R) Iris(R) Xe Graphics (RPL-P)
    • OpenGL core version: 4.6 (Core Profile) Mesa 25.1.5-1pop0~1753463422~24.04~8af185e
    • OpenGL core shading language version: 4.60
    • OpenGL ES renderer: Mesa Intel(R) Iris(R) Xe Graphics (RPL-P)
    • OpenGL ES version: OpenGL ES 3.2 Mesa 25.1.5-1pop0~1753463422~24.04~8af185e
    • OpenGL ES shading language version: OpenGL ES GLSL ES 3.20
    • GL_EXT_framebuffer_blit: yes
    • GL_EXT_texture_format_BGRA8888: yes

[✓] Connected device (2 available) [125ms]
    • Linux (desktop) • linux  • linux-x64      • Pop!_OS 24.04 LTS 6.17.9-76061709-generic
    • Chrome (web)    • chrome • web-javascript • Google Chrome 143.0.7499.169

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

• No issues found!

Activity

  1. PurplePolyhedron commented on Jan 3, 2026

    @PurplePolyhedron
    Contributor

    It seems that DoNothingAction couldn't actually bind to any Intent in this case, despite the document saying it should be able to.
    A possible workaround is to create your own Intent specific DoNothingAction.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const ActionApp());
    }
    
    class MyDoNothingAction<T extends Intent> extends Action<T> {
      @override
      void invoke(T intent) {}
    }
    
    class ActionApp extends StatelessWidget {
      const ActionApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple)),
          home: Scaffold(
            body: Actions(
              actions: {
                DeleteCharacterIntent: MyDoNothingAction<DeleteCharacterIntent>(),
                DeleteToNextWordBoundaryIntent: MyDoNothingAction<DeleteToNextWordBoundaryIntent>(),
                DeleteToLineBreakIntent: MyDoNothingAction<DeleteToLineBreakIntent>(),
                ExtendSelectionByCharacterIntent: MyDoNothingAction<ExtendSelectionByCharacterIntent>(),
                ExtendSelectionToNextWordBoundaryIntent:
                    MyDoNothingAction<ExtendSelectionToNextWordBoundaryIntent>(),
                ExtendSelectionToNextWordBoundaryOrCaretLocationIntent:
                    MyDoNothingAction<ExtendSelectionToNextWordBoundaryOrCaretLocationIntent>(),
                ExtendSelectionToLineBreakIntent: MyDoNothingAction<ExtendSelectionToLineBreakIntent>(),
                ExtendSelectionVerticallyToAdjacentLineIntent:
                    MyDoNothingAction<ExtendSelectionVerticallyToAdjacentLineIntent>(),
                ExtendSelectionVerticallyToAdjacentPageIntent:
                    MyDoNothingAction<ExtendSelectionVerticallyToAdjacentPageIntent>(),
                ExtendSelectionToDocumentBoundaryIntent:
                    MyDoNothingAction<ExtendSelectionToDocumentBoundaryIntent>(),
                SelectAllTextIntent: MyDoNothingAction<SelectAllTextIntent>(),
                ReplaceTextIntent: MyDoNothingAction<ReplaceTextIntent>(),
                UpdateSelectionIntent: MyDoNothingAction<UpdateSelectionIntent>(),
                CopySelectionTextIntent: MyDoNothingAction<CopySelectionTextIntent>(),
                PasteTextIntent: MyDoNothingAction<PasteTextIntent>(),
              },
              child: const TextField(enableInteractiveSelection: false),
            ),
          ),
        );
      }
    }
  2. lebeshev commented on Jan 3, 2026

    @lebeshev
    Author

    A possible workaround is to create your own Intent specific DoNothingAction.

    Yes, that naturally fixes assertion errors. This does not fix ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent still running their default actions however.

  3. lebeshev commented on Jan 3, 2026

    @lebeshev
    Author

    I did some digging and I think I found the issue for why overriding ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent does not work.

    Here is the code from editable_text.dart :

    // line 5455
      late final _UpdateTextSelectionVerticallyAction<DirectionalCaretMovementIntent>
      _verticalSelectionUpdateAction =
          _UpdateTextSelectionVerticallyAction<DirectionalCaretMovementIntent>(this);
    // ...
    
    // line 5512
      late final Map<Type, Action<Intent>> _actions = <Type, Action<Intent>>{
    // ...
    // line 5570
        ExtendSelectionVerticallyToAdjacentLineIntent: _makeOverridable(_verticalSelectionUpdateAction),
        ExtendSelectionVerticallyToAdjacentPageIntent: _makeOverridable(_verticalSelectionUpdateAction),
    // ...
    }

    Action used for both ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent is bound to DirectionalCaretMovementIntent, which I assume should not be the case.

    And indeed overriding the DirectionalCaretMovementIntent itself like

    DirectionalCaretMovementIntent: MyDoNothingAction<DirectionalCaretMovementIntent>()

    fixes the issue and caret does not move anymore on arrow up and similar shortcuts.

  4. tirth-patel-nc commented on Jan 6, 2026

    @tirth-patel-nc
    Member

    Thanks for the report. Seeing the same behaviour with latest SDK versions.

    stable : 3.38.5
    master : 3.40.0-1.0.pre-413
    
  5. added
    a: text inputEntering text in a text field or keyboard related problems
    c: crashStack traces logged to the console
    frameworkflutter/packages/flutter repository. See also f: labels.
    p: material_uimaterial_ui package in flutter/packages
    a: error messageError messages from the Flutter framework
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    in triagePresently being triaged by the triage team
    on Jan 6, 2026
  6. 3 remaining items

  7. LongCatIsLooong commented on Jan 9, 2026

    @LongCatIsLooong
    Contributor

    Hmm maybe we can construct a const Function<T> in each Action<T> and does the type check like this:

    mappedAction._functionSignature is Function<T>

    But this means people won't be able to implement Action<T>. Let me try this out.

  8. LongCatIsLooong commented on Jan 10, 2026

    @LongCatIsLooong
    Contributor

    The above tricks seems to work but unforunately some of our public APIs return Action<T> and the language only allows for covariant type parameters so there's no way you can return a Action<Intent> when T is a more specialized type. And Function is final so we can't do Action<T> extends Function<T>, and also Function type literal has a special syntax Function(U).

  9. lebeshev commented on Jan 10, 2026

    @lebeshev
    Author

    I did some digging and I think I found the issue for why overriding ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent does not work.

    Here is the code from editable_text.dart :

    // line 5455
    late final _UpdateTextSelectionVerticallyAction
    _verticalSelectionUpdateAction =
    _UpdateTextSelectionVerticallyAction(this);
    // ...

    // line 5512
    late final Map<Type, Action> _actions = <Type, Action>{
    // ...
    // line 5570
    ExtendSelectionVerticallyToAdjacentLineIntent: _makeOverridable(_verticalSelectionUpdateAction),
    ExtendSelectionVerticallyToAdjacentPageIntent: _makeOverridable(_verticalSelectionUpdateAction),
    // ...
    }

    Action used for both ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent is bound to DirectionalCaretMovementIntent, which I assume should not be the case.

    And indeed overriding the DirectionalCaretMovementIntent itself like

    DirectionalCaretMovementIntent: MyDoNothingAction()

    fixes the issue and caret does not move anymore on arrow up and similar shortcuts.

    Should I create a separate issue for this bug?

  10. LongCatIsLooong commented on Jan 12, 2026

    @LongCatIsLooong
    Contributor

    Action used for both ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent is bound to DirectionalCaretMovementIntent, which I assume should not be the case.

    Hmm I'm not sure what you mean, it makes sense to me that ExtendSelectionVerticallyToX should be a subclass of DirectionalCaretMovementIntent?

  11. lebeshev commented on Jan 14, 2026

    @lebeshev
    Author

    Action used for both ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent is bound to DirectionalCaretMovementIntent, which I assume should not be the case.

    Hmm I'm not sure what you mean, it makes sense to me that ExtendSelectionVerticallyToX should be a subclass of DirectionalCaretMovementIntent?

    The issue is that you cannot override ExtendSelectionVerticallyToAdjacentPageIntent and ExtendSelectionVerticallyToAdjacentLineIntent individually even when using something likeMyDoNothingAction<ExtendSelectionVerticallyToAdjacentPageIntent>() and MyDoNothingAction<ExtendSelectionVerticallyToAdjacentLineIntent>(). You need to use MyDoNothingAction<DirectionalCaretMovementIntent>(), which will override them both.

    And my guess is that this happens because action used in editable_text.dart for both these intents is defined bound to DirectionalCaretMovementIntent like this:

      late final _UpdateTextSelectionVerticallyAction<DirectionalCaretMovementIntent>
      _verticalSelectionUpdateAction =
          _UpdateTextSelectionVerticallyAction<DirectionalCaretMovementIntent>(this);
  12. LongCatIsLooong commented on Jan 14, 2026

    @LongCatIsLooong
    Contributor

    Action used for both ExtendSelectionVerticallyToAdjacentLineIntent and ExtendSelectionVerticallyToAdjacentPageIntent is bound to DirectionalCaretMovementIntent, which I assume should not be the case.

    Hmm I'm not sure what you mean, it makes sense to me that ExtendSelectionVerticallyToX should be a subclass of DirectionalCaretMovementIntent?

    The issue is that you cannot override ExtendSelectionVerticallyToAdjacentPageIntent and ExtendSelectionVerticallyToAdjacentLineIntent individually even when using something likeMyDoNothingAction<ExtendSelectionVerticallyToAdjacentPageIntent>() and MyDoNothingAction<ExtendSelectionVerticallyToAdjacentLineIntent>(). You need to use MyDoNothingAction<DirectionalCaretMovementIntent>(), which will override them both.

    And my guess is that this happens because action used in editable_text.dart for both these intents is defined bound to DirectionalCaretMovementIntent like this:

    late final _UpdateTextSelectionVerticallyAction
    _verticalSelectionUpdateAction =
    _UpdateTextSelectionVerticallyAction(this);

    Ah I might have fixed that in #180883. It's probably because we were using the statically inferred type T extends Intent to find the override action instead of using the Intent's runtime type. Let me add a test for that later.

    Update: Yeah I think I might have fixed that case in the said PR. But the limitation is that you won't be able to access callingAction, which is basically the equivalent of the super implementation when you're implementing the override.

  13. added
    P2Important issues not at the top of the work list
    on Jan 29, 2026
  14. solvejet-team commented on Apr 11, 2026

    @solvejet-team
    Contributor

    I can reproduce this on master. The root cause is in Actions._castAction — it does mappedAction is Action? which fails for DoNothingAction because Dart's reified generics are invariant ( Action is not Action at runtime). I have a fix that wraps Action actions in a typed delegate when intentType == Intent. Would like to submit a PR — does this approach sound reasonable?

  15. added 2 commits that reference this issue on Apr 11, 2026
    13febe8
    71d4e00
  16. github-actions commented on Jul 14, 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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: error messageError messages from the Flutter frameworka: text inputEntering text in a text field or keyboard related problemsc: crashStack traces logged to the consolefound in release: 3.38Found to occur in 3.38found in release: 3.40Found to occur in 3.40frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packagesteam-text-inputOwned by Text Input teamtriaged-text-inputTriaged by Text Input team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions