[Android] Text in FlatList not selectable #37130
Labels
Accessibility
Component: FlatList
Component: Text
Needs: Triage 🔍
Newer Patch Available
Platform: Android
Android applications.
Stale
There has been a lack of activity on this issue and it may be closed soon.
Description
Summary
Text components rendered within the confines of a
FlatList
are not selectable even when theselectable
property is set to true. Setting theremoveClippedSubviews
tofalse
has a negative performance impact.This bug can likely be attributed to the bug captured in Google's Issue Tracker here. I was able to override our
RCTText
manager andReactTextView
implementation with a subclass that performs the suggested fix here, and the views immediately became selectable.Citations
Previous issues:
#12342
#14746
#26264
#27107
Previous fix:
#28952
Proposed Fix
Per the suggestion I linked above, a quick toggle of the
textIsSelectable
flag works just fine. Here's my implementation for a subclass in Kotlin:I propose that this toggle of the
textIsSelectable
property be added directly to theReactTextView
class in itsonAttachedToWindow
override. This fix is similar in spirit to the one in the "Previous fix" section above.It's important to note that the
textIsSelectable
property is correctly set, alongside thefocusable
,focusableInTouchMode
,clickable
, andlongClickable
properties. The underlying recycler view simply does not honor these settings.Underlying cause
The
Editor
class, responsible for enabling text selection in Android, during itsprepareCursorControllers
phase attempts to access the root view from theTextView
. On normal text views (i.e. not mounted in aFlatList
), the root view is aDecorView
with layout parameters. On text views mounted by a FlatList, the root view is aReactViewGroup
with null layout parameters. Subsequently, the check to ensure that the window can handle selections fails and theEditor
sets its internalmSelectionControllerEnabled
property to false.The
Editor
has aperformLongClick
handler where normal selection events would fire. Towards the tail end of the function's implementation, it tries to callselectCurrentWordAndStartDrag
. The third check,checkField
, returns false because its call tocanSelectText
returns false. ThecanSelectText
function is checking for that aforementionedmSelectionControllerEnabled
property. This is what causes the debug warning to fire "TextView does not support text selection. Selection cancelled."So why does firing
setTextIsSelectable
first with false, then true, work? There's a check within everyTextView
that sees if it's internal reference to theEditor
mEditor
and the givenselectable
property match. Sure enough, they do! So the function returns early and doesn't executemEditor.prepareCursorControllers
. But by flipping the selectable property to false, we end up falling through the entire function to executeprepareCursorControllers
. By this point, we know the view has properly been attached to a window and not to the ephemeralRecyclerView
, so executing that function should re-enable the selection controller.It's the same reason why the solution linked in the Google tracker uses
setEnabled
instead ofsetTextIsSelectable
--that function also executesprepareCursorControllers
. We could callsetText
too, orsetMovementMode
, butsetTextIsSelectable
felt like the leanest, safest way to do it.React Native Version
0.70.4
Output of
npx react-native info
We have a pretty custom setup where our NPM packages are managed internally, but here's my device:
Steps to reproduce
FlatList
render item with aText
that has itsselectable
property set totrue
TextView does not support text selection. Selection cancelled.
Snack, code example, screenshot, or link to a repository
https://snack.expo.dev/@abbondanzo/react-native-android---text-not-selectable-in-flatlist
Here's a quick demo of the warnings that are thrown from the native platform using the code from the Snack linked above:
https://user-images.githubusercontent.com/10366495/234964124-1bbecae9-a6a0-48f7-9d34-858a5c5be460.mov
The text was updated successfully, but these errors were encountered: