Skip to content

Android: selectionActions never take effect — race between async configureSelectionActions and synchronous attachNavigator() #213

Description

@a-martynyshyn

Summary

On Android, selectionActions passed to ReadiumReaderWidget never take effect on first open: long-pressing to select text always shows the plain system selection menu (Copy/Share/…) instead of the custom ActionMode.Callback built from selectionActions. This reproduces the same way on 0.3.3 and 0.4.0.

Root cause (as far as I can tell from reading the source)

ReadiumReader.selectionActions (a static var, ReadiumReader.kt) is only ever populated by the async configureSelectionActions platform-channel method:

  • Dart (lib/reader_widget.dart) sends it from _onPlatformViewCreated, i.e. only after the native platform view has already been created and Flutter has been notified:
    • 0.4.0: lib/reader_widget.dart:386 (_onPlatformViewCreated) → lib/reader_widget.dart:406-407 (_channel!.configureSelectionActions(widget.selectionActions))
    • 0.3.3: same shape, lib/reader_widget.dart:401-403
  • Android (ReadiumReaderWidget.kt) handles it by just setting the static field:
    • 0.4.0: android/.../ReadiumReaderWidget.kt:465-468 ("configureSelectionActions" -> { ... ReadiumReader.selectionActions = ... })
    • 0.3.3: android/.../ReadiumReaderWidget.kt:438-441, same shape

Meanwhile, EpubReaderFragment.attachNavigator() reads that same static field synchronously, at fragment onViewCreated() time, to decide whether to register the custom ActionMode.Callback at all:

  • 0.4.0: attachNavigator() at fragments/EpubReaderFragment.kt:595, called from onViewCreated() (:531) via lifecycleScope.launch { ... attachNavigator() } (:549-552); the callback decision is at :640 (selectionActionModeCallback = if (ReadiumReader.selectionActions.isNotEmpty()) createSelectionActionModeCallback() else null)
  • 0.3.3: same shape — attachNavigator() at fragments/EpubReaderFragment.kt:592, decision at :636-641

onViewCreated() runs as part of the native PlatformViewFactory creating the Android view, which happens before Flutter's engine calls back into Dart's onPlatformViewCreated. So by the time Dart can send configureSelectionActions, attachNavigator() has generally already run once with ReadiumReader.selectionActions still empty, and the custom ActionMode.Callback is never registered for that navigator instance.

Note that ReadiumReaderWidget (the Dart widget) does also pass selectionActions synchronously through creationParams at view-creation time:

  • 0.4.0: lib/reader_widget.dart:308-309
  • 0.3.3: lib/reader_widget.dart:303-304

...but as far as I can find, neither the 0.3.3 nor 0.4.0 Android factory/fragment code ever reads creationParams["selectionActions"] — only configureSelectionActions (the async path) ever assigns ReadiumReader.selectionActions. (0.4.0 does read other creationParams keys synchronously at factory time, e.g. fontFamilyDeclarations at :119 — so that mechanism exists and works for fonts, just not for selectionActions.)

Reproduction

  1. flutter_readium: ^0.4.0 (also reproduced on 0.3.3), Android (tested on an emulator, API 35/36).
  2. Build a ReadiumReaderWidget with a non-empty selectionActions list.
  3. Open any EPUB, long-press to select text.
  4. Expected: the custom ActionMode with the configured selectionActions (optionally alongside system defaults, per allowedDefaultActions).
  5. Actual: plain system selection menu (Copy, Share, Select all, …) — the custom actions never appear, consistently, not just intermittently.

Suggested fix directions

  • Read selectionActions from creationParams synchronously in the Android factory (the same way fontFamilyDeclarations already is), instead of relying solely on the async configureSelectionActions channel call.
  • And/or: have attachNavigator()'s first run wait for an explicit "config applied" signal from Dart before deciding on selectionActionModeCallback, rather than deciding based on whatever ReadiumReader.selectionActions happens to contain at that instant.

Happy to send a PR for the creationParams read if that's the preferred fix — wanted to check the intended design first, since configureSelectionActions existing as a separate async method suggests per-session reconfiguration might also be a supported use case, and I don't want to break that.

Environment

  • flutter_readium: 0.4.0 (and 0.3.3)
  • Flutter: 3.47.0, Dart 3.13.0
  • Platform: Android (emulator, API 35/36)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions