3

I am trying to build a searchable dropdown which will load value from service on every button click. So, for that I have encapsulated DropDownButton and TextField in Stack Widget.

On keypress we get response from api, so far so good. But after getting data from api dropdown was not opening. After digging a bit I came to know it was not opening because we need to manually tap it to open, but since its in stack and second children is TextField I can't tap it.

But opening DropDownButton button programmatically is not possible. So I tried second solution from https://stackoverflow.com/a/59499191/10423593 but it didn't work.

Below is my code without solution from stackoverflow.

import 'package:flutter/material.dart';
import 'package:giphy/services/gifs_service.dart';
import 'package:giphy/shared/autocomplete.dart';

class TestDropDown extends StatefulWidget {
  // const TestDropDown({Key? key}) : super(key: key);
  @override
  _TestDropDownState createState() => _TestDropDownState();
}

class _TestDropDownState extends State<TestDropDown> {
  final GifyService _service = GifyService();

  final TextEditingController _gifSearchController = TextEditingController();

  List<SearchData> _dropDownResult = <SearchData>[];

  GlobalKey key = GlobalKey();

  // T? _findChildWidgetFromKey<T extends Widget>(
  //     BuildContext? context, T childWidget) {
  //   T? detector;
  //   context!.visitChildElements((element) {
  //     if (element.widget == childWidget) {
  //       detector = element.widget as T;
  //     }
  //   });

  //   return detector;
  // }

  Widget _buildDropDown(List<SearchData> searchData) => DropdownButton<String>(
        isExpanded: true,
        key: key,
        onChanged: (String? value) {},
        items: searchData
            .map(
              (e) => DropdownMenuItem<String>(
                child: Text(e.name ?? ''),
                value: e.name ?? '',
              ),
            )
            .toList(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          _buildDropDown(_dropDownResult),
          Container(child: () {
            if (_dropDownResult.length > 0) {
              _buildDropDown(_dropDownResult);
            }
          }()),
          TextField(
            controller: _gifSearchController,
            decoration: InputDecoration(
              border: OutlineInputBorder(
                borderSide: BorderSide(width: 0.5),
                borderRadius: BorderRadius.circular(21),
              ),
            ),
            onChanged: (String value) async {
              AutoComplete result = await _service.getSearchKeywords(value);
              setState(() {
                _dropDownResult = result.data;
              });
            },
          ),
        ],
      ),
    );
  }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Yaseen
  • 214
  • 1
  • 11

3 Answers3

1

If you are looking to autocomplete the text field then you can use autocomplete_textfield package.

Or if you want to build it on your own then you can try a different approach by using container instead of dropdown menu.

0

After trying some packages. I wasn't able to do flutter autocomplete based on api calls.

So decided to try some other approach and used Overlay in flutter

import 'dart:async';

import 'package:flutter/material.dart';

class TestDropDown extends StatefulWidget {
  @override
  _TestDropDownState createState() => _TestDropDownState();
}

class _TestDropDownState extends State<TestDropDown> {
  late OverlayEntry _overlayEntry;
  Timer? _debounce;
  _showOverlay(BuildContext context) {
    OverlayState? state = Overlay.of(context);
    final RenderBox? box = key.currentContext!.findRenderObject() as RenderBox;
    Size size = box!.size;
    Offset position = box.localToGlobal(Offset.zero);
    _overlayEntry = OverlayEntry(
      builder: (context) => Positioned(
        top: size.height + position.dy,
        left: position.dx,
        child: Card(
          child: Container(
              height: 200,
              width: size.width,
              decoration: BoxDecoration(
                border: Border.all(),
              ),
              child: Column(
                children: [
                  Container(
                    width: size.width,
                    child: IconButton(
                      onPressed: () {
                        if (_overlayEntry.mounted) {
                          _overlayEntry.remove();
                        }
                      },
                      icon: Icon(Icons.close),
                      alignment: Alignment.topRight,
                    ),
                  ),
                  Expanded(
                    child: ListView(
                      children: []..addAll(List.generate(
                          200, (index) => Text(index.toString()))),
                    ),
                  ),
                ],
              )),
        ),
      ),
    );
    state!.insert(_overlayEntry);
  }

  final key = GlobalKey();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(50),
        child: Column(
          children: [
            TextField(
              key: key,
              onChanged: (String searchText) {
                if (_debounce?.isActive ?? false) _debounce!.cancel();
                _debounce = Timer(const Duration(milliseconds: 500), () {
                  print(searchText);
                });
              },
            ),
            ElevatedButton(
              onPressed: () {
                _showOverlay(context);
              },
              child: Text('Press Me'),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if (_overlayEntry.mounted) {
            _overlayEntry.remove();
          }
        },
      ),
    );
  }
}

enter image description here

Yaseen
  • 214
  • 1
  • 11
-2

try using: https://pub.dev/packages/dropdown_search Step 1: you request all data Step 2: using the library you can searching item

  • There is no point of getting all data at once, its more like a auto complete. Where api will return the result based on text entered by user – Yaseen Sep 27 '21 at 04:58