-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Steps to reproduce
- Run code sample on the iOS simulator or physical iPhone on iOS 16.0+ where the system context menu is supported.
- Tap anywhere.
- Tap again on the collapsed selection to reveal the context menu.
- Tap on "Select All".
Expected results
Tapping "Select All" selects all the text and reveals the selection handles.
Screen.Recording.2025-05-14.at.12.41.30.PM.mov
Actual results
Tapping "Select All" selects all the text but does not reveal the selection handles.
Screen.Recording.2025-05-14.at.12.36.37.PM.mov
What is happening
Using the system context menu on iOS we do not get any signals that we pressed a button in the context menu. That update comes through EditableTextState.updateEditingValue, and in that case we hit this line which classifies the cause of the update to be the keyboard
| cause = SelectionChangedCause.keyboard; |
When this happens we hit the following case which prevents the selection handles from being shown.
flutter/packages/flutter/lib/src/material/text_field.dart
Lines 1349 to 1351 in cd3b0a5
| if (cause == SelectionChangedCause.keyboard) { | |
| return false; | |
| } |
Potential solution
Use a custom button for "Select All" and other buttons that would reveal selection handles with a callback that calls editableTextState.selectAll(SelectionChangedCause.toolbar) and similar methods. The "paste" button doesn't reveal selection handles so we can still get secure paste functionality by using the default button.
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 Selection Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Selection Playground'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController(
text: 'one two three four five',
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: TextField(
controller: _controller,
style: TextStyle(fontSize: 20),
),
),
);
}
}