I have a StatefulWidget named Root and this is the start address of my application. The Root widget contains the single and common AppBar of the application, but its body varies.
My question is: I have a page named SearchPlaces and that page uses the common AppBar of the application. There is a TextFormField under the AppBar. When I press and hold the TextFormField, the toolbar menu (copy, cut, select all, paste, etc.) is under the AppBar. How do I prevent this? I want the toolbar to appear
root.dart
class Root extends StatefulWidget {
const Root({super.key});
@override
State<Root> createState() => _RootState();
}
class _RootState extends State<Root> with TickerProviderStateMixin {
late final AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
debugPrint("Root build");
return Scaffold(
appBar: AppBar(
forceMaterialTransparency: true,
scrolledUnderElevation: 0.0,
actions: [
SizedBox(
width: 90,
height: 80,
child: _ChangeThemeToggleButton(animationController: _animationController),
).onlyPadding(right: PaddingManager.commonHorizontal),
],
),
body: Observer(
name: "Center",
builder: (_) {
return Center(
child: NavigatorRoutes()
.navPages
.elementAt(GetRooteStateManager.rootStateManager.selectedIndexOfBottomNavigationBar),
).symmetricpadding(
horizontal: PaddingManager.commonHorizontal,
vertical: PaddingManager.commonVertical,
);
},
),
bottomNavigationBar: const CustomBottomNavigationBar(
// themeState: GetThemeStateManager().themeStateManager,
)
.onlyPadding(
left: PaddingManager.horizontalOfBottomNavigationBar,
right: PaddingManager.horizontalOfBottomNavigationBar,
bottom: PaddingManager.onlyBottomOfBottomNavigationBar,
top: PaddingManager.onlyTopOfBottomNavigationBar,
),
);
}
}
search_places.dart
class SearchPlaces extends StatefulWidget {
const SearchPlaces({super.key});
@override
State<SearchPlaces> createState() => _SearchPlacesState();
}
class _SearchPlacesState extends State<SearchPlaces> with TextFormFieldValidationMixin {
@override
void initState() {
super.initState();
searchController = TextEditingController();
}
@override
void dispose() {
searchController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final places = List.generate(20, (index) => "Place $index");
return Scaffold(
resizeToAvoidBottomInset: false,
body: Overlay(
initialEntries: [
OverlayEntry(
builder: (context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SearchTextFormField(controller: searchController),
Expanded(
flex: 6,
child: ListView.builder(
itemCount: places.length,
itemBuilder: (context, index) => ListTile(
title: Text(places[index]),
),
),
),
],
),
)
],
),
);
}
}
search_text_form_field.dart
class SearchTextFormField extends StatelessWidget {
const SearchTextFormField({
super.key,
required this.controller,
});
final TextEditingController controller;
@override
Widget build(BuildContext context) {
double width = MediaQuery.sizeOf(context).width;
double height = MediaQuery.sizeOf(context).height;
return SizedBox(
width: width * 0.9,
height: height * 0.06,
child: Observer(
builder: (context) => TextFormField(
contextMenuBuilder: (context, editableTextState) {
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: editableTextState.contextMenuAnchors,
buttonItems: <ContextMenuButtonItem>[
ContextMenuButtonItem(
onPressed: () {
editableTextState.copySelection(SelectionChangedCause.toolbar);
},
type: ContextMenuButtonType.copy,
),
ContextMenuButtonItem(
onPressed: () {
editableTextState.selectAll(SelectionChangedCause.toolbar);
},
type: ContextMenuButtonType.selectAll,
),
ContextMenuButtonItem(
onPressed: () {
editableTextState.cutSelection(SelectionChangedCause.toolbar);
},
type: ContextMenuButtonType.cut,
),
ContextMenuButtonItem(
onPressed: () {
editableTextState.pasteText(SelectionChangedCause.toolbar);
},
type: ContextMenuButtonType.paste,
),
],
);
},
enableInteractiveSelection: true,
onChanged: (value) {
value.isNotEmpty
? GetSearchPlacesStateManager.searchPlacesManager.changeClearButtonState(isActive: true)
: GetSearchPlacesStateManager.searchPlacesManager.changeClearButtonState(isActive: false);
},
controller: controller,
autofocus: true,
keyboardType: TextInputType.streetAddress,
maxLines: 1,
autocorrect: false,
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
decoration: InputDecoration(
suffixIcon: GetSearchPlacesStateManager.searchPlacesManager.isActiveClearButton
? IconButton(
onPressed: () {
controller.clear();
GetSearchPlacesStateManager.searchPlacesManager.changeClearButtonState(isActive: false);
},
icon: const Icon(Icons.clear_outlined),
)
: null,
hintText: "Search Places",
prefixIcon: const Icon(Icons.search),
),
),
),
);
}
}

wrap the scaffold with SafeArea widget
then wrap the SearchPlaces Scaffold's body (overlay)
keep adjusting the MediaQuery height (exemple: * 0.05 )until you reach the perfect size