I am trying to show an overlay of the same width as of the TextField. The below code sample works fine but when I run it in the browser and resize the window, The Textfield resizes but the overlay stays with the old dimensions.
How can we update (not rerender the overlay) the dimensions of the overlay on resizing the window?
Here’s a sample code I have tried
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _scrollController = ScrollController();
Widget _list() {
return Container(
height: 5 * 40,
child: Scrollbar(
controller: _scrollController,
thumbVisibility: true,
child: ListView.builder(
controller: _scrollController,
padding: EdgeInsets.zero,
itemCount: 20,
itemBuilder: (context, index) => ListTile(
title: Text('item $index'),
onTap: () {
_overlayEntry.remove();
},
))),
);
}
final LayerLink _layerLink = LayerLink();
OverlayEntry _createOverlay() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) => Positioned(
left: offset.dx,
width: size.width,
child: CompositedTransformFollower(
offset: Offset(0, 50),
link: _layerLink,
child: Material(color: Colors.red, child: _list())),
));
}
late OverlayEntry _overlayEntry;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
_overlayEntry = _createOverlay();
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CompositedTransformTarget(
link: _layerLink,
child: TextFormField(
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black.withOpacity(0.8),
),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red),
),
),
onTap: () {
if (_overlayEntry.mounted) {
_overlayEntry.remove();
}
Overlay.of(context).insert(_overlayEntry);
},
onChanged: (query) {},
),
),
],
),
),
);
}
}
Heres the output of the above code
Notice that Overlay dimensions do not match the window size (when window was resized)
We could force the overlay dimensions by adjusting the Positioned.right
parameter
OverlayEntry(
builder: (context) => Positioned(
left: 0,
right: 0, // custom offset from right
// width: textFieldSize.width,
child: CompositedTransformFollower(
offset: Offset(0, 50),
link: _layerLink,
child: Material(color: Colors.red, child: _list())),
));
But this would fail if we were to give padding around the Textfield
e.g if I set Positioned.right:0
and Give Padding around Textfield the overlay would always take up the whole width.
The final solution I tried is to set the width of the textfield to overlay width, But I am not sure how do we update the Textfield renderBox so that the overlay takes up the expected dimensions.
OverlayEntry _createOverlay() {
/// textfield dimensions
final textFieldRenderBox =
textFieldKey.currentContext!.findRenderObject() as RenderBox;
final textFieldOffset = textFieldRenderBox.localToGlobal(Offset.zero);
final textFieldSize = textFieldRenderBox.size;
print('$textFieldSize $textFieldOffset ');
return OverlayEntry(
builder: (context) => Positioned(
left: 0,
width: textFieldSize.width,
child: CompositedTransformFollower(
offset: Offset(0, 50),
link: _layerLink,
child: Material(color: Colors.red, child: _list())),
));
}
Edit: I tried using didChangeMetrics to listen to MediaQueyChanges and update the Overlay using ValueListenableBuilder but its janky and updates with a delay heres the output and the code
2
Answers
We could solve this using OverLayPortal a new widget introduced in flutter 3.10.
Originally answered here https://github.com/flutter/flutter/issues/78746#issuecomment-1564893844
i think that your use case could be resolved by using the AutoComplete Widget, have you tried it yet ?
will will show you something like this
enter image description here