I’m trying to implement an overlay function for a text field that shows some particular state depending on what’s the user input. When the user taps the text field, it will display an overlay that should show some information while the user is writing, much like a search bar on a web browser works.
My problem is that the content of the OverlayEntry doesn’t update at the same time the user input change, it only changes when I reopen the Overlay.
Expected behavior
Current behaviour
For testing this functionality, I’m just passing the same value from the text field to the overlay.
This is my code:
Home class
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CustomTextField(),
]),
// This trailing comma makes auto-formatting nicer for build methods.
),
));
}
}
Custom Text field class:
class CustomTextField extends StatefulWidget {
CustomTextField({Key? key}) : super(key: key);
@override
State<CustomTextField> createState() => _CustomTextFieldState();
}
class _CustomTextFieldState extends State<CustomTextField>
with TickerProviderStateMixin {
TextEditingController controlador = TextEditingController();
FocusNode nodeUno = FocusNode();
OverlayEntry? _overlayEntry;
GlobalKey globalKey = GlobalKey();
final LayerLink _layerLink = LayerLink();
String inputText = '';
@override
void initState() {
super.initState();
OverlayState? overlayState = Overlay.of(context);
WidgetsBinding.instance!.addPostFrameCallback((_) {
globalKey;
});
nodeUno.addListener(() {
if (nodeUno.hasFocus) {
_overlayEntry = _createOverlay(inputText);
overlayState!.insert(_overlayEntry!);
} else {
_overlayEntry!.remove();
}
});
}
OverlayEntry _createOverlay([String? text]) {
RenderBox renderBox = context.findRenderObject() as RenderBox;
var size = renderBox.size;
return OverlayEntry(
builder: (context) => Positioned(
width: size.width,
child: CompositedTransformFollower(
link: _layerLink,
showWhenUnlinked: false,
offset: Offset(0.0, size.height + 5.0),
child: Material(
elevation: 5.0,
child: Column(
children: [
ListTile(
title: text != '' ? Text(text!) : const Text('data'),
),
],
),
),
),
));
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: 250,
child: CompositedTransformTarget(
link: _layerLink,
child: TextField(
focusNode: nodeUno,
controller: controlador,
onChanged: (value) {
print(value);
setState(() {
inputText = value;
});
},
),
),
);
}
}
2
Answers
Well, for what I could find, the only way to update the content of an overlay entry is to remove and insert again that entry, so this is what I found that works:
Just call this: