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
flutter_readium: ^0.4.0 (also reproduced on 0.3.3), Android (tested on an emulator, API 35/36).
- Build a
ReadiumReaderWidget with a non-empty selectionActions list.
- Open any EPUB, long-press to select text.
- Expected: the custom
ActionMode with the configured selectionActions (optionally alongside system defaults, per allowedDefaultActions).
- 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)
Summary
On Android,
selectionActionspassed toReadiumReaderWidgetnever take effect on first open: long-pressing to select text always shows the plain system selection menu (Copy/Share/…) instead of the customActionMode.Callbackbuilt fromselectionActions. 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 asyncconfigureSelectionActionsplatform-channel method: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:lib/reader_widget.dart:386(_onPlatformViewCreated) →lib/reader_widget.dart:406-407(_channel!.configureSelectionActions(widget.selectionActions))lib/reader_widget.dart:401-403ReadiumReaderWidget.kt) handles it by just setting the static field:android/.../ReadiumReaderWidget.kt:465-468("configureSelectionActions" -> { ... ReadiumReader.selectionActions = ... })android/.../ReadiumReaderWidget.kt:438-441, same shapeMeanwhile,
EpubReaderFragment.attachNavigator()reads that same static field synchronously, at fragmentonViewCreated()time, to decide whether to register the customActionMode.Callbackat all:attachNavigator()atfragments/EpubReaderFragment.kt:595, called fromonViewCreated()(:531) vialifecycleScope.launch { ... attachNavigator() }(:549-552); the callback decision is at:640(selectionActionModeCallback = if (ReadiumReader.selectionActions.isNotEmpty()) createSelectionActionModeCallback() else null)attachNavigator()atfragments/EpubReaderFragment.kt:592, decision at:636-641onViewCreated()runs as part of the nativePlatformViewFactorycreating the Android view, which happens before Flutter's engine calls back into Dart'sonPlatformViewCreated. So by the time Dart can sendconfigureSelectionActions,attachNavigator()has generally already run once withReadiumReader.selectionActionsstill empty, and the customActionMode.Callbackis never registered for that navigator instance.Note that
ReadiumReaderWidget(the Dart widget) does also passselectionActionssynchronously throughcreationParamsat view-creation time:lib/reader_widget.dart:308-309lib/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"]— onlyconfigureSelectionActions(the async path) ever assignsReadiumReader.selectionActions. (0.4.0 does read othercreationParamskeys synchronously at factory time, e.g.fontFamilyDeclarationsat:119— so that mechanism exists and works for fonts, just not for selectionActions.)Reproduction
flutter_readium: ^0.4.0(also reproduced on 0.3.3), Android (tested on an emulator, API 35/36).ReadiumReaderWidgetwith a non-emptyselectionActionslist.ActionModewith the configuredselectionActions(optionally alongside system defaults, perallowedDefaultActions).Suggested fix directions
selectionActionsfromcreationParamssynchronously in the Android factory (the same wayfontFamilyDeclarationsalready is), instead of relying solely on the asyncconfigureSelectionActionschannel call.attachNavigator()'s first run wait for an explicit "config applied" signal from Dart before deciding onselectionActionModeCallback, rather than deciding based on whateverReadiumReader.selectionActionshappens to contain at that instant.Happy to send a PR for the
creationParamsread if that's the preferred fix — wanted to check the intended design first, sinceconfigureSelectionActionsexisting 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)