Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter search bar with autocomplete

I'm looking for a search bar in flutter docs but can't find it, is there a widget for the search bar with autocomplete in appbar. For example, I have a search icon on my appbar. When one press it show's the search box, when you type it should show autocomplete from the dropdown with listtile. I managed to implement this but it's not easy to use because I need a dropdown to show suggestion autocomplete, then use the suggestion for a new route if selected.

Here the search action

here the example image what I want to achieve

like image 514
azra abdullah Avatar asked Apr 26 '18 20:04

azra abdullah


3 Answers

You can use Stack to achieve the autocomplete dropdown box effect. Example below has 2 Containers - both hold ListView as child objects. One holds search results, other has some random text as content for the body. ListView (search result) is placed inside an Align Object and alignment property is set to Alignment.topCenter. This ensures that List appears at the top, just below the AppBar.

Updated the Post (accepted answer) mentioned in the comments for a complete a demo.

As explained above:

    @override
      Widget build(BuildContext context) {
        return new Scaffold(
            key: key,
            appBar: buildBar(context),
            body: new Stack(
              children: <Widget>[
                new Container(
                  height: 300.0,
                  padding: EdgeInsets.all(10.0),
                  child: new DefaultTabController(length: 5, child: mainTabView),
                ),
                displaySearchResults(),
              ],
            ));
      }


      Widget displaySearchResults() {
        if (_IsSearching) {
          return new Align(
              alignment: Alignment.topCenter,
              //heightFactor: 0.0,
              child: searchList());
        } else {
          return new Align(alignment: Alignment.topCenter, child: new Container());
        }
      }

Complete demo

class SearchList extends StatefulWidget {
  SearchList({Key key, this.name}) : super(key: key);

  final String name;

  @override
  _SearchListState createState() => new _SearchListState();
}

class _SearchListState extends State<SearchList> {
  Widget appBarTitle = new Text(
    "",
    style: new TextStyle(color: Colors.white),
  );
  Icon actionIcon = new Icon(
    Icons.search,
    color: Colors.white,
  );
  final key = new GlobalKey<ScaffoldState>();
  final TextEditingController _searchQuery = new TextEditingController();
  List<SearchResult> _list;
  bool _IsSearching;
  String _searchText = "";
  String selectedSearchValue = "";

  _SearchListState() {
    _searchQuery.addListener(() {
      if (_searchQuery.text.isEmpty) {
        setState(() {
          _IsSearching = false;
          _searchText = "";
        });
      } else {
        setState(() {
          _IsSearching = true;
          _searchText = _searchQuery.text;
        });
      }
    });
  }

  @override
  void initState() {
    super.initState();
    _IsSearching = false;
    createSearchResultList();
  }

  void createSearchResultList() {
    _list = <SearchResult>[
      new SearchResult(name: 'Google'),
      new SearchResult(name: 'IOS'),
      new SearchResult(name: 'IOS2'),
      new SearchResult(name: 'Android'),
      new SearchResult(name: 'Dart'),
      new SearchResult(name: 'Flutter'),
      new SearchResult(name: 'Python'),
      new SearchResult(name: 'React'),
      new SearchResult(name: 'Xamarin'),
      new SearchResult(name: 'Kotlin'),
      new SearchResult(name: 'Java'),
      new SearchResult(name: 'RxAndroid'),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        key: key,
        appBar: buildBar(context),
        body: new Stack(
          children: <Widget>[
            new Container(
              height: 300.0,
              padding: EdgeInsets.all(10.0),
              child: new Container(
                child: ListView(
                  children: <Widget>[
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                    new Text("Hello World!"),
                  ],
                ),
              ),
            ),
            displaySearchResults(),
          ],
        ));
  }

  Widget displaySearchResults() {
    if (_IsSearching) {
      return new Align(
          alignment: Alignment.topCenter,
          child: searchList());
    } else {
      return new Align(alignment: Alignment.topCenter, child: new Container());
    }
  }

  ListView searchList() {
    List<SearchResult> results = _buildSearchList();
    return ListView.builder(
      itemCount: _buildSearchList().isEmpty == null ? 0 : results.length,
      itemBuilder: (context, int index) {
        return Container(
          decoration: new BoxDecoration(
              color: Colors.grey[100],
            border: new Border(
              bottom: new BorderSide(
                  color: Colors.grey,
                width: 0.5
              )
          )
          ),

          child: ListTile(
            onTap: (){},
            title: Text(results.elementAt(index).name,
                style: new TextStyle(fontSize: 18.0)),
          ),
        );
      },
    );
  }

  List<SearchResult> _buildList() {
    return _list.map((result) => new SearchResult(name: result.name)).toList();
  }

  List<SearchResult> _buildSearchList() {
    if (_searchText.isEmpty) {
      return _list.map((result) => new SearchResult(name: result.name)).toList();
    } else {
      List<SearchResult> _searchList = List();
      for (int i = 0; i < _list.length; i++) {
        SearchResult result = _list.elementAt(i);
        if ((result.name).toLowerCase().contains(_searchText.toLowerCase())) {
          _searchList.add(result);
        }
      }
      return _searchList
          .map((result) => new SearchResult(name: result.name))
          .toList();
    }
  }

  Widget buildBar(BuildContext context) {
    return new AppBar(
      centerTitle: true,
      title: appBarTitle,
      actions: <Widget>[
        new IconButton(
          icon: actionIcon,
          onPressed: () {
            _displayTextField();
          },
        ),

        // new IconButton(icon: new Icon(Icons.more), onPressed: _IsSearching ? _showDialog(context, _buildSearchList()) : _showDialog(context,_buildList()))
      ],
    );
  }

  String selectedPopupRoute = "My Home";
  final List<String> popupRoutes = <String>[
    "My Home",
    "Favorite Room 1",
    "Favorite Room 2"
  ];

  void _displayTextField() {
    setState(() {
      if (this.actionIcon.icon == Icons.search) {
        this.actionIcon = new Icon(
          Icons.close,
          color: Colors.white,
        );
        this.appBarTitle = new TextField(
          autofocus: true,
          controller: _searchQuery,
          style: new TextStyle(
            color: Colors.white,
          ),
        );

        _handleSearchStart();
      } else {
        _handleSearchEnd();
      }
    });
  }

  void _handleSearchStart() {
    setState(() {
      _IsSearching = true;
    });
  }

  void _handleSearchEnd() {
    setState(() {
      this.actionIcon = new Icon(
        Icons.search,
        color: Colors.white,
      );
      this.appBarTitle = new Text(
        "",
        style: new TextStyle(color: Colors.white),
      );
      _IsSearching = false;
      _searchQuery.clear();
    });
  }
}
like image 102
CautionToWind Avatar answered Nov 09 '22 23:11

CautionToWind


this plugin will helpful for you,loader_search_bar

Flutter widget integrating search field feature into app bar, allowing to receive query change callbacks and automatically load new data set into ListView. It replaces standard AppBar widget and needs to be placed underneath Scaffold element in the widget tree to work properly.

enter image description here

Getting started To start using SearchBar insert it in place of an AppBar element in the Scaffold widget. Regardless of the use case, defaultBar named argument has to be specified, which basically is a widget that will be displayed whenever SearchBar is not in activated state:

@override
Widget build(BuildContext context) {
   return Scaffold(
 appBar: SearchBar(
   defaultBar: AppBar(
     leading: IconButton(
       icon: Icon(Icons.menu),
       onPressed: _openDrawer,
     ),
     title: Text('Default app bar title'),
   ),
   ...
 ),
 body: _body,
 drawer: _drawer,
   );
}

Optional attributes

  • searchHint - hint string being displayed until user inputs any text,
  • initialQuery - query value displayed for the first time in search field,
  • iconified - boolean value indicating way of representing non-activated SearchBar: true if widget should be showed as an action item in defaultBar, false if widget should be merged with defaultBar (only leading icon of the default widget and search input field are displayed in such case),
  • autofocus - boolean value determining if search text field should get focus whenever it becomes visible,
  • autoActive - ,
  • attrs - SearchBarAttrs class instance allowing to specify part of exact values used during widget building (e.g. search bar colors, text size, border radius),
  • controller - SearchBarController object that provides a way of interacing with current state of the widget,
  • searchItem - defining how to build and position search item widget in app bar,
  • overlayStyle - status bar overlay brightness applied when widget is activated.

Query callbacks

To get notified about user input specify onQueryChanged and/or onQuerySubmitted callback functions that receive current query string as an argument:

appBar: SearchBar(
   ...
   onQueryChanged: (query) => _handleQueryChanged(context, query),
   onQuerySubmitted: (query) => _handleQuerySubmitted(context, query),
),

QuerySetLoader

By passing QuerySetLoader object as an argument one can additionally benefit from search results being automatically built as ListView widget whenever search query changes:

appBar: SearchBar(
  ...
   loader: QuerySetLoader<Item>(
   querySetCall: _getItemListForQuery,
   itemBuilder: _buildItemWidget,
   loadOnEachChange: true,
   animateChanges: true,
  ),
),

List<Item> _getItemListForQuery(String query) { ... }

Widget _buildItemWidget(Item item) { ... }
  • querySetCall - function transforming search query into list of items being then rendered in ListView (required),
  • itemBuilder - function creating Widget object for received item, called during ListView building for each element of the results set (required),
  • loadOnEachChange - boolean value indicating whether querySetCall should be triggered on each query change; if false query set is loaded once user submits query,
  • animateChanges - determines whether ListView's insert and remove operations should be animated.

SearchItem

Specifying this parameter allows to customize how search item should be built and positioned in app bar. It can be either action or menu widget. No matter which of these two is picked, two constructor arguments can be passed:

  • builder - function receiving current BuildContext and returning Widget for action or PopupMenuItem for menu item,
  • gravity - can be one of SearchItemGravity values: start, end or exactly. If no arguments are passed, SearchBar will create default item which is search action icon with start gravity.

SearchItem.action

enter image description here

appBar: SearchBar(
  // ...
  searchItem: SearchItem.action(
    builder: (_) => Padding(
      padding: EdgeInsets.all(12.0),
      child: Icon(
        Icons.find_in_page,
        color: Colors.indigoAccent,
      ),
    ),
    gravity: SearchItemGravity.exactly(1),
  ),
)

SearchItem.menu

enter image description here

appBar: SearchBar(
  // ...
  searchItem: SearchItem.menu(
    builder: (_) => PopupMenuItem(
      child: Text("Search  🔍"),
      value: "search",
    ),
    gravity: SearchItemGravity.end,
  ),
)

Also, bear in mind that SearchBar will prevent built item widget from receiving tap events and will begin search action rather than that.

hope it will help you.

like image 45
null Avatar answered Nov 09 '22 22:11

null


It is actually very simple. you can refer above answers for details. Let's follow these steps:

  • Create a list of items we want to have in the autofill menu, lets name it autoList
  • Create one more emptyList named filteredList
  • Add all the values of autoList to filterList
void initState() {
filteredList.addAll(autoList);
}
  • Create a custom search bar widget with a TextField in it

  • we will be getting a 'value' i.e. the text entered from this Textfield: eg. TextFiled(onchange(value){})

  • Assuming that we have strings in our autoList, write:

filteredList.removeWhere((i) => i.contains(value.toString())==false); 

The complete TextField widget will look like:

TextField(
     onChanged: (value) {
     setState(() {
     filteredList.clear(); //for the next time that we search we want the list to be unfilterted                            
     filteredList.addAll(autoList); //getting list to original state

//removing items that do not contain the entered Text                                                            
     filteredList.removeWhere((i) => i.contains(value.toString())==false); 

//following is just a bool parameter to keep track of lists
     searched=!searched;
     });


    },
  controller: editingController,
  decoration: InputDecoration(
  border: InputBorder.none,
  labelText: "Search for the filtered list",
  prefixIcon: Icon(Icons.search),
   ),
    ),

Now, along the search bar, we just have to display filteredList with ListViewBuilder. done :)

like image 2
Shubhra Deshpande Avatar answered Nov 09 '22 21:11

Shubhra Deshpande