The toolbar of TextFormField is under the AppBar

59 Views Asked by At

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

enter image description here

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),
          ),
        ),
      ),
    );
  }
}
1

There are 1 best solutions below

8
Hachem Mohamed Mahmoud On

wrap the scaffold with SafeArea widget

SafeArea(
child: Scaffold(......)
,);

then wrap the SearchPlaces Scaffold's body (overlay)

    Container(
 margin: EdgeInsets.symmetric(vertical:MediaQuery.of(context).size.height * 0.05),
    child : overlay(....)
    ,),

keep adjusting the MediaQuery height (exemple: * 0.05 )until you reach the perfect size