For adding the zero-width-space to the TextEditingController it was enough to provide "\u200b"
in the initializer and when updating the value of the controller.
As for how to make this detect delete presses, you can try the code below, it have a callback onEmptyTextField
which triggers when the TextEditingController's text turns empty or already is empty.
import 'package:flutter/material.dart';
const zeroWidthSpace = "\u200b";
class DetectDeleteTextField extends StatelessWidget {
const DetectDeleteTextField({
Key? key,
required this.onEmptyTextField,
required this.controller,
}) : super(key: key);
/// Triggered when the TextField turns/is empty, such as when the zero-width-space ("\u200b")
/// or another character is deleted, or delete is pressed repeatedly.
final Function() onEmptyTextField;
final TextEditingController controller;
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (text) {
print("onChanged - Length: ${text.length}, Text runes:${text.runes.toString()}");
// Remove the zero-width-space character, handle cursor position
// correctly, and ensure no zero-width-space is present if text is pasted.
if (text.length >= 2 && text.contains(zeroWidthSpace)) {
String trimmedText = text.replaceAll(zeroWidthSpace, "");
controller.value = TextEditingValue(
text: trimmedText,
selection: TextSelection.fromPosition(TextPosition(offset: trimmedText.length)),
);
print("Trimming - Length: ${controller.text.length}, Text runes:${controller.text.runes.toString()}");
}
// Trigger the callback and reset the text to the zero-width-space character.
// The selection is needed for detecting backspace when only the
// zero-width-space exist in the text.
if (text.isEmpty) {
onEmptyTextField();
controller.value = const TextEditingValue(text: zeroWidthSpace);
controller.selection = const TextSelection(baseOffset: 0, extentOffset: 1);
print("After delete - Length: ${controller.text.length}, Text runes:${controller.text.runes.toString()}");
}
},
);
}
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DetectDeleteTextField(
controller: TextEditingController(text: zeroWidthSpace),
onEmptyTextField: () => print("Delete triggered!")
),
],
),
),
);
}
}
void main() => runApp(const App());
Tested on Android and Windows.