Listview过滤器搜索Flutter

时间:2018-05-28 13:10:49

标签: dart flutter

  var userDetails = {};
  var i;
  List returnTicketDetails = [] ;

  body: new Column(
        children: <Widget>[
          new Container(
            color: Theme.of(context).primaryColor,
            child: new Padding(
              padding: const EdgeInsets.all(8.0),
              child: new Card(
                child: new ListTile(
                  leading: new Icon(Icons.search),
                  title: new TextField(
                    controller: controller,
                    decoration: new InputDecoration(
                        hintText: 'Search', border: InputBorder.none),
                   // onChanged: onSearchTextChanged,
                  ),
                  trailing: new IconButton(icon: new Icon(Icons.cancel), onPressed: () {
                    controller.clear();
                   // onSearchTextChanged('');
                  },),
                ),
          new Expanded(
            child: userDetails.length != 0 || controller.text.isNotEmpty
                ? new ListView.builder(
               itemCount: userDetails.length,
              itemBuilder: (context, i) {
                return new Card(

                  child: new Column
                    (mainAxisSize: MainAxisSize.min, children:
                    <Widget>[
                    new Row(children: <Widget>[
                    new Container(
                    width: 80.0,
                    height: 80.0,
                    decoration: new BoxDecoration(
                    shape: BoxShape.circle,
                    image: new DecorationImage(
                    fit: BoxFit.fill,
                    image: new NetworkImage(
                    "https:..")
                )
                )),
                    new Text(userDetails[returnTicketDetails[i]["user_id"]]["first_name"]
                    ),),
                  ,),
                    new Text(userDetails[returnTicketDetails[i]["user_id"]]["last_name"]),
                );
                },
            )
                : new ListView.builder(
               itemCount: userDetails.length,
              itemBuilder: (context, i) {
                return new Card(
                  child: new ListTile(
                    //title: new Text(userDetails[returnTicketDetails[i]["user_id"]]["first_name"]),
                  ),
                  margin: const EdgeInsets.all(0.0),
                );
    );
      }
 _getTicketDetails() async {
     final response = await http.get(
         "https..", headers: {
       HttpHeaders.AUTHORIZATION: access_token
     });
      returnTicketDetails = json.decode(response.body);
     for ( i = 0; i < (returnTicketDetails?.length ?? 0); i++) {
       final ticketresponse = await http.get(
           "https...", headers: {
         HttpHeaders.AUTHORIZATION:
         access_token
       });
       userDetails[returnTicketDetails[i]["user_id"]] =
           json.decode(ticketresponse.body);
   }
   }

This is how my interface looks like right now

我几乎所有的东西都按照我想要的方式,但是我想知道如何使搜索功能根据我的ListView的索引工作?所以,例如,如果我输入一个&#34; z&#34;根据我的情况,我不应该在列表中显示任何内容。 我还在这里更新并发布了函数_getTicketDeatils。 提前谢谢!

11 个答案:

答案 0 :(得分:18)

我已经根据需要从url获取数据替换了硬编码模型输入。

import 'dart:async';

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() => runApp(new MaterialApp(
  home: new HomePage(),
  debugShowCheckedModeBanner: false,
));

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => new _HomePageState();
}

class _HomePageState extends State<HomePage> {
  TextEditingController controller = new TextEditingController();

  // Get json result and convert it to model. Then add
  Future<Null> getUserDetails() async {
    final response = await http.get(url);
    final responseJson = json.decode(response.body);

    setState(() {
      for (Map user in responseJson) {
        _userDetails.add(UserDetails.fromJson(user));
      }
    });
  }

  @override
  void initState() {
    super.initState();

    getUserDetails();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Home'),
        elevation: 0.0,
      ),
      body: new Column(
        children: <Widget>[
          new Container(
            color: Theme.of(context).primaryColor,
            child: new Padding(
              padding: const EdgeInsets.all(8.0),
              child: new Card(
                child: new ListTile(
                  leading: new Icon(Icons.search),
                  title: new TextField(
                    controller: controller,
                    decoration: new InputDecoration(
                        hintText: 'Search', border: InputBorder.none),
                    onChanged: onSearchTextChanged,
                  ),
                  trailing: new IconButton(icon: new Icon(Icons.cancel), onPressed: () {
                    controller.clear();
                    onSearchTextChanged('');
                  },),
                ),
              ),
            ),
          ),
          new Expanded(
            child: _searchResult.length != 0 || controller.text.isNotEmpty
                ? new ListView.builder(
              itemCount: _searchResult.length,
              itemBuilder: (context, i) {
                return new Card(
                  child: new ListTile(
                    leading: new CircleAvatar(backgroundImage: new NetworkImage(_searchResult[i].profileUrl,),),
                    title: new Text(_searchResult[i].firstName + ' ' + _searchResult[i].lastName),
                  ),
                  margin: const EdgeInsets.all(0.0),
                );
              },
            )
                : new ListView.builder(
              itemCount: _userDetails.length,
              itemBuilder: (context, index) {
                return new Card(
                  child: new ListTile(
                    leading: new CircleAvatar(backgroundImage: new NetworkImage(_userDetails[index].profileUrl,),),
                    title: new Text(_userDetails[index].firstName + ' ' + _userDetails[index].lastName),
                  ),
                  margin: const EdgeInsets.all(0.0),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  onSearchTextChanged(String text) async {
    _searchResult.clear();
    if (text.isEmpty) {
      setState(() {});
      return;
    }

    _userDetails.forEach((userDetail) {
      if (userDetail.firstName.contains(text) || userDetail.lastName.contains(text))
        _searchResult.add(userDetail);
    });

    setState(() {});
  }
}

List<UserDetails> _searchResult = [];

List<UserDetails> _userDetails = [];

final String url = 'https://jsonplaceholder.typicode.com/users';
class UserDetails {
  final int id;
  final String firstName, lastName, profileUrl;

  UserDetails({this.id, this.firstName, this.lastName, this.profileUrl = 'https://i.amz.mshcdn.com/3NbrfEiECotKyhcUhgPJHbrL7zM=/950x534/filters:quality(90)/2014%2F06%2F02%2Fc0%2Fzuckheadsho.a33d0.jpg'});

  factory UserDetails.fromJson(Map<String, dynamic> json) {
    return new UserDetails(
      id: json['id'],
      firstName: json['name'],
      lastName: json['username'],
    );
  }
}

答案 1 :(得分:9)

我会向您推荐这篇search as you type媒体,该文章使用流实现了搜索,基本思路是这样

1. get your data store in a list(in initstate to use this list later in step 5)
2. search for your query in above list (ontext changed in query)
3. store the filtered result (from above search) in a new list 
4. add the filtered list to a streams (to show updated result in UI using streambuilder)
5. if searchquery is empty add the datastore list to stream (to show complete data in UI)

图片的下半部分显示了示例实现

enter image description here

如果您希望上面的文章适合您,这就是上述实现过程的输出。

enter image description here

答案 2 :(得分:3)

使用SearchDelegate

enter image description here


完整解决方案:

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String _result;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Search')),
      body: Center(
        child: Column(
          children: <Widget>[
            Text(_result ?? '', style: TextStyle(fontSize: 18)),
            RaisedButton(
              onPressed: () async {
                var result = await showSearch<String>(
                  context: context,
                  delegate: CustomDelegate(),
                );
                setState(() => _result = result);
              },
              child: Text('Search'),
            ),
          ],
        ),
      ),
    );
  }
}

class CustomDelegate<T> extends SearchDelegate<T> {
  List<String> data = nouns.take(100).toList();

  @override
  List<Widget> buildActions(BuildContext context) => [IconButton(icon: Icon(Icons.clear), onPressed: () => query = '')];

  @override
  Widget buildLeading(BuildContext context) => IconButton(icon: Icon(Icons.chevron_left), onPressed: () => close(context, null));

  @override
  Widget buildResults(BuildContext context) => Container();

  @override
  Widget buildSuggestions(BuildContext context) {
    List listToShow;
    if (query.isNotEmpty) listToShow = data.where((e) => e.contains(query) && e.startsWith(query)).toList();
    else listToShow = data;

    return ListView.builder(
      itemCount: listToShow.length,
      itemBuilder: (_, i) {
        return ListTile(
          title: Text(listToShow[i]),
          onTap: () => close(context, listToShow[i]),
        );
      },
    );
  }
}

答案 3 :(得分:2)

参考 Vinoth Kumar 的答案,请注意.contains()区分大小写的。因此,要用尽所有匹配项,可以将字符串转换为小写:

_userDetails.forEach((userDetail) {
    if (userDetail.firstName.toLowerCase().contains(text.toLowerCase()) || userDetail.lastName.toLowerCase().contains(text.toLowerCase()))
      _searchResult.add(userDetail);
 });

对我来说很完美。

答案 4 :(得分:1)

在Flutter中,我们必须使用自定义过滤器小部件进行管理,并且必须比较两个不同的对象列表。如

if (country.name.toLowerCase().contains(searchQuery) ||
          country.name.contains(searchQuery)) {
        filteredRecored.add(country);
      }

我找到了示例here

答案 5 :(得分:0)

我正在学习Flutter,正在寻找@Vinoth Kumar示例可完美完成的可搜索列表。

我已将代码拆分为不同的文件,并将HomePage主体简化为几种方法,以使其对我自己更具可维护性/可读性,我认为值得分享。

main.dart

import 'package:flutter/material.dart';
import 'homepage.dart';

void main() => runApp(new MaterialApp(
      home: new HomePage(),
      debugShowCheckedModeBanner: false,
    ));

homepage.dart

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'userDetails.dart';

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  List<UserDetails> _searchResult = [];
  List<UserDetails> _userDetails = [];
  TextEditingController controller = new TextEditingController();

  // Get json result and convert it to model. Then add
  Future<Null> getUserDetails() async {
    final response = await http.get(url);
    final responseJson = json.decode(response.body);

    setState(() {
      for (Map user in responseJson) {
        _userDetails.add(UserDetails.fromJson(user));
      }
    });
  }

  @override
  void initState() {
    super.initState();

    getUserDetails();
  }

  Widget _buildUsersList() {
    return new ListView.builder(
      itemCount: _userDetails.length,
      itemBuilder: (context, index) {
        return new Card(
          child: new ListTile(
            leading: new CircleAvatar(
              backgroundImage: new NetworkImage(
                _userDetails[index].profileUrl,
              ),
            ),
            title: new Text(_userDetails[index].firstName +
                ' ' +
                _userDetails[index].lastName),
          ),
          margin: const EdgeInsets.all(0.0),
        );
      },
    );
  }

  Widget _buildSearchResults() {
    return new ListView.builder(
      itemCount: _searchResult.length,
      itemBuilder: (context, i) {
        return new Card(
          child: new ListTile(
            leading: new CircleAvatar(
              backgroundImage: new NetworkImage(
                _searchResult[i].profileUrl,
              ),
            ),
            title: new Text(
                _searchResult[i].firstName + ' ' +_searchResult[i].lastName),
          ),
          margin: const EdgeInsets.all(0.0),
        );
      },
    );
  }

  Widget _buildSearchBox() {
    return new Padding(
      padding: const EdgeInsets.all(8.0),
      child: new Card(
        child: new ListTile(
          leading: new Icon(Icons.search),
          title: new TextField(
            controller: controller,
            decoration: new InputDecoration(
                hintText: 'Search', border: InputBorder.none),
            onChanged: onSearchTextChanged,
          ),
          trailing: new IconButton(
            icon: new Icon(Icons.cancel),
            onPressed: () {
              controller.clear();
              onSearchTextChanged('');
            },
          ),
        ),
      ),
    );
  }

  Widget _buildBody() {
    return new Column(
      children: <Widget>[
        new Container(
            color: Theme.of(context).primaryColor, child: _buildSearchBox()),
        new Expanded(
            child: _searchResult.length != 0 || controller.text.isNotEmpty
                ? _buildSearchResults()
                : _buildUsersList()),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Home'),
        elevation: 0.0,
      ),
      body: _buildBody(),
      resizeToAvoidBottomPadding: true,
    );
  }

  onSearchTextChanged(String text) async {
    _searchResult.clear();
    if (text.isEmpty) {
      setState(() {});
      return;
    }

    _userDetails.forEach((userDetail) {
      if (userDetail.firstName.contains(text) ||
          userDetail.lastName.contains(text)) _searchResult.add(userDetail);
    });

    setState(() {});
  }
}

userDetails.dart

import 'package:flutter/material.dart';

final String url = 'https://jsonplaceholder.typicode.com/users';
class UserDetails {
  final int id;
  final String firstName, lastName, profileUrl;

  UserDetails({this.id, this.firstName, this.lastName, this.profileUrl = 'https://i.amz.mshcdn.com/3NbrfEiECotKyhcUhgPJHbrL7zM=/950x534/filters:quality(90)/2014%2F06%2F02%2Fc0%2Fzuckheadsho.a33d0.jpg'});

  factory UserDetails.fromJson(Map<String, dynamic> json) {
    return new UserDetails(
      id: json['id'],
      firstName: json['name'],
      lastName: json['username'],
    );
  }
}

答案 6 :(得分:0)

这是另一种过滤列表的方法

_searchResult = _userDetails.where(
                    (userDetail) => (userDetail.firstName.contains(text) || userDetail.lastName.contains(text))
                );

答案 7 :(得分:0)

将此添加到您的pub.yaml文件中:flutter_slidable:^ 0.5.4

    import 'package:flutter/material.dart';
    import 'produit_bloc.dart';
    import 'produit_service.dart';
    import 'package:flutter_slidable/flutter_slidable.dart';

    class CRUDListView extends StatefulWidget {
      @override
      _CRUDListViewState createState() => _CRUDListViewState();
    }

    class _CRUDListViewState extends State<CRUDListView> {
      List<Produit> _produits;
      List<Produit> _searchProduct;
      TextEditingController controller = new TextEditingController();

      @override
      void initState() {
        super.initState();
        _produits = [];
        _searchProduct = [];
        _getEmployees();
      }

      _getEmployees() {
        ProduitService.getProduits().then((produits) {
          setState(() {
            _produits = produits;
          });
          print("Length ${produits.length} ");
        });
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey,
          appBar: new AppBar(
            title: new Text('SEARCH ON LISTVIEW'),
            elevation: 0.0,
          ),
          body: new Column(
            children: <Widget>[
              new Container(
                color: Theme.of(context).primaryColor,
                child: new Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: new Card(
                    child: new ListTile(
                      leading: new Icon(Icons.search),
                      title: new TextField(
                        controller: controller,
                        decoration: new InputDecoration(
                            hintText: 'Search', border: InputBorder.none),
                        onChanged: onSearchTextChanged,
                      ),
                      trailing: new IconButton(
                        icon: new Icon(Icons.cancel),
                        onPressed: () {
                          controller.clear();
                          onSearchTextChanged('');
                        },
                      ),
                    ),
                  ),
                ),
              ),
              new Expanded(
                child: _searchProduct.length != 0 || controller.text.isNotEmpty
                    ? new ListView.builder(
                        itemCount: _searchProduct.length,
                        itemBuilder: (context, i) {
                          return new Slidable(
                            actionPane: SlidableDrawerActionPane(),
                            actionExtentRatio: 0.25,
                            child: Container(
                              color: Colors.white,
                              child: ListTile(
                                leading: Text(('${_searchProduct[i].id}')),
                                title: Text(('${_searchProduct[i].article}')),
                                trailing: Text(('${_searchProduct[i].prixu}')),
                              ),
                            ),
                            actions: <Widget>[
                              IconSlideAction(
                                caption: 'Archive',
                                color: Colors.blue,
                                icon: Icons.archive,
                                onTap: () {
                                  print('archive');
                                },
                              ),
                              IconSlideAction(
                                caption: 'Share',
                                color: Colors.indigo,
                                icon: Icons.share,
                                onTap: () {
                                  print('share');
                                },
                              ),
                            ],
                            secondaryActions: <Widget>[
                              IconSlideAction(
                                caption: 'More',
                                color: Colors.black45,
                                icon: Icons.more_horiz,
                                onTap: () {
                                  print('more');
                                },
                              ),
                              IconSlideAction(
                                caption: 'Delete',
                                color: Colors.red,
                                icon: Icons.delete,
                                onTap: () {
                                  print('delete');
                                },
                              ),
                            ],
                          );
                        },
                      )
                    : new ListView.builder(
                        itemCount: _produits.length,
                        itemBuilder: (context, index) {
                          return new Slidable(
                            actionPane: SlidableDrawerActionPane(),
                            actionExtentRatio: 0.25,
                            child: Container(
                              color: Colors.white,
                              child: ListTile(
                                leading: Text(('${_produits[index].id}')),
                                title: Text(('${_produits[index].article}')),
                                trailing: Text(('${_produits[index].prixu}')),
                              ),
                            ),
                            actions: <Widget>[
                              IconSlideAction(
                                caption: 'Archive',
                                color: Colors.blue,
                                icon: Icons.archive,
                                onTap: () {
                                  print('archive');
                                },
                              ),
                              IconSlideAction(
                                caption: 'Share',
                                color: Colors.indigo,
                                icon: Icons.share,
                                onTap: () {
                                  print('share');
                                },
                              ),
                            ],
                            secondaryActions: <Widget>[
                              IconSlideAction(
                                caption: 'More',
                                color: Colors.black45,
                                icon: Icons.more_horiz,
                                onTap: () {
                                  print('more');
                                },
                              ),
                              IconSlideAction(
                                caption: 'Delete',
                                color: Colors.red,
                                icon: Icons.delete,
                                onTap: () {
                                  print('delete');
                                },
                              ),
                            ],
                          );
                        },
                      ),
              ),
            ],
          ),
        );
      }

      onSearchTextChanged(String text) async {
        _searchProduct.clear();
        if (text.isEmpty) {
          setState(() {});
          return;
        }

        _produits.forEach((produit) {
          if (produit.article.contains(text) || produit.prixu.contains(text))
            _searchProduct.add(produit);
        });

        setState(() {});
      }
    }

答案 8 :(得分:0)

有时setState((){})可能不需要重建整个屏幕

在这种情况下,您可以使用ValuelistenableBuilder小部件包装可搜索对象。

对此进行检查;

 Widget searchableUsersWidget() {
    List<Map> users = [
      {'name': 'James', 'tel': '9010'},
      {'name': 'Michael', 'tel': '9011'},
      {'name': 'Jane', 'tel': '9013'},
     
    ];
    ValueNotifier<List<Map>> filtered = ValueNotifier<List<Map>>([]);
    TextEditingController searchController = TextEditingController();
    FocusNode searchFocus = FocusNode();
    bool searching = false;
    return ValueListenableBuilder<List>(
        valueListenable: filtered,
        builder: (context, value, _) {
          return Container(
            margin: EdgeInsets.only(top: 10),
            decoration: BoxDecoration(
              color: shared.primaryBackgroundColor(),
              borderRadius:  BorderRadius.only(
                      topLeft: Radius.circular(20),
                      topRight: Radius.circular(20)),
              boxShadow: [
                      BoxShadow(
                        color: Colors.white.withOpacity(0.5),
                        spreadRadius: 4,
                        blurRadius: 6,
                        offset: Offset(0, 3), // changes position of shadow
                      ),
                    ],
            ),
            child: Column(
              children: [
                Container(
                  margin: EdgeInsets.all(8),
                  child: Card(
                    child: new ListTile(
                      leading: new Icon(Icons.search),
                      title: new TextField(
                        controller: searchController,
                        decoration: new InputDecoration(
                            hintText: 'Search', border: InputBorder.none),
                        onChanged: (text) {
                          if (text.length > 0) {
                            searching = true;
                            filtered.value = [];
                            users.forEach((user) {
                              if (user['name']
                                      .toString()
                                      .toLowerCase()
                                      .contains(text.toLowerCase()) ||
                                  user['tel'].toString().contains(text)) {
                                filtered.value.add(user);
                              }
                            });
                          } else {
                            searching = false;
                            filtered.value = [];
                          }
                        },
                      ),
                      trailing: new IconButton(
                        icon: new Icon(Icons.cancel),
                        onPressed: () {
                          searchController.clear();
                          searching = false;
                          filtered.value = [];
                          if (searchFocus.hasFocus) searchFocus.unfocus();
                        },
                      ),
                    ),
                  ),
                ),
                Expanded(
                  child: ListView.builder(
                      itemCount: searching
                          ? filtered.value.length
                          : users.length,
                      itemBuilder: (context, index) {
                        final item = searching
                            ? filtered.value[index]
                            : users[index];
                        return ListTile(
                          leading: Avatar(
                            imageUrl: '',
                            color: Colors.amber,
                          ),
                          title: Text(item['name']),
                          subtitle: Text(item['tel']),
                          onTap: () {},
                        );
                      }),
                ),
              ],
            ),
          );
        });
  }

答案 9 :(得分:0)

您可以使用内置类SearchDelegate

答案 10 :(得分:0)

这是一个带有自动完成建议的搜索视图。以及最简单的 API 搜索方式

整合源码

1:直接将 pk_search_bar 目录拖放到您的 Flutter 项目中。

2 : 创建一个类 SearchScreen 并在 build > Scaffold > child: SearchBar()

创建模型类

class CountryModel {
  String countryName;
  String countryCode;
  @override
  String toString() {
    return '$countryName $countryCode';
  }

  CountryModel(this.countryName, this.countryCode);
}

创建搜索屏幕

在这个类中,我们将调用过滤器并获取数据。然后我们将解析数据以获取用户的 CountryModel。

List<CountryModel> countryModelList = <CountryModel>[
  CountryModel('Australia', 'AU'),
  CountryModel('Egypt', 'EG'),
  CountryModel('Germany', 'DE'),
  CountryModel('India', 'IN'),
  CountryModel('Singapore', 'SG'),
  CountryModel('United States of America', 'US')
];


Future<List<CountryModel>> getCountrySearch(String search) async {
  print("Resident search = $search");
  if (search == "empty") return [];
  if (search == "error") throw Error();
  List<CountryModel> filterCountryList = [];

  widget.countryModelList.forEach((CountryModel) {
    if (CountryModel.countryName
        .toLowerCase()
        .contains(search.toLowerCase()) ||
        CountryModel.countryCode
            .toLowerCase()
            .contains(search.toLowerCase()))
      filterCountryList.add(CountryModel);
  });

  return filterCountryList;
}


  // TODO: CountrySearchBar
  Widget searchBar(BuildContext context) {
    return SearchBar<CountryModel>(
      searchBarPadding: EdgeInsets.only(left: 5, right: 5, top: 10, bottom: 5),
      headerPadding: EdgeInsets.only(left: 0, right: 0),
      listPadding: EdgeInsets.only(left: 0, right: 0),
      hintText: "Search Placeholder",
      hintStyle: TextStyle(
        color: Colors.black54,
      ),
      textStyle: TextStyle(
        color: Colors.black,
        fontWeight: FontWeight.normal,
      ),
      iconActiveColor: Colors.deepPurple,
      shrinkWrap: true,
      mainAxisSpacing: 5,
      crossAxisSpacing: 5,
      suggestions: widget.countryModelList,
      cancellationWidget: Text("Cancel"),
      minimumChars: 1,
//      placeHolder: Center(
//        child: Padding(
//          padding: const EdgeInsets.only(left: 10, right: 10),
//          child: Text(searchMessage, textAlign: TextAlign.center, style: CustomTextStyle.textSubtitle1(context).copyWith(fontSize: 14),),
//        ),
//      ),
      emptyWidget: Center(
        child: Padding(
          padding: const EdgeInsets.only(left: 10, right: 10),
          child: Text("There is no any data found"),
        ),
      ),
      onError: (error) {
        return Center(
          child: Padding(
            padding: const EdgeInsets.only(left: 10, right: 10),
            child: Text("$error", textAlign: TextAlign.center),
          ),
        );
      },
      loader: Center(
        child: LoadingIndicator(),
      ),
      onSearch: getCountrySearchWithSuggestion, /// CountrySearch  // if want to search with API then use thi ----> getCountryListFromApi
      onCancelled: () {
        Navigator.pop(context);
      },
      buildSuggestion: (CountryModel countryModel, int index) {
        return countryGenerateColumn(countryModel, index);
      },
      onItemFound: (CountryModel countryModel, int index) {
        return countryGenerateColumn(countryModel, index);
      },
    );
  }

完整的源代码

import 'package:flutter/material.dart';
import 'package:fluttersearchviewpk/Constants.dart';
import 'package:page_transition/page_transition.dart';
import 'package:fluttersearchviewpk/pk_search_bar/pk_search_bar.dart';
import 'package:fluttersearchviewpk/DataListScreen.dart';
import 'package:flutter/services.dart';


class SearchScreen extends StatefulWidget {

  final List<CountryModel> countryModelList;
  @override
  _SearchScreenState createState() => _SearchScreenState();

  const SearchScreen(this.countryModelList);
}

class _SearchScreenState extends State<SearchScreen> {

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
    return Scaffold(
      body: SafeArea(
          bottom: false,
          child: Container(
            child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 5),
                child: searchBar(context)),
          )),
    );
  }

  // TODO: CountrySearchBar
  Widget searchBar(BuildContext context) {
    return SearchBar<CountryModel>(
      searchBarPadding: EdgeInsets.only(left: 5, right: 5, top: 10, bottom: 5),
      headerPadding: EdgeInsets.only(left: 0, right: 0),
      listPadding: EdgeInsets.only(left: 0, right: 0),
      hintText: "Search Placeholder",
      hintStyle: TextStyle(
        color: Colors.black54,
      ),
      textStyle: TextStyle(
        color: Colors.black,
        fontWeight: FontWeight.normal,
      ),
      iconActiveColor: Colors.deepPurple,
      shrinkWrap: true,
      mainAxisSpacing: 5,
      crossAxisSpacing: 5,
      suggestions: widget.countryModelList,
      cancellationWidget: Text("Cancel"),
      minimumChars: 1,
//      placeHolder: Center(
//        child: Padding(
//          padding: const EdgeInsets.only(left: 10, right: 10),
//          child: Text(searchMessage, textAlign: TextAlign.center, style: CustomTextStyle.textSubtitle1(context).copyWith(fontSize: 14),),
//        ),
//      ),
      emptyWidget: Center(
        child: Padding(
          padding: const EdgeInsets.only(left: 10, right: 10),
          child: Text("There is no any data found"),
        ),
      ),
      onError: (error) {
        return Center(
          child: Padding(
            padding: const EdgeInsets.only(left: 10, right: 10),
            child: Text("$error", textAlign: TextAlign.center),
          ),
        );
      },
      loader: Center(
        child: LoadingIndicator(),
      ),
      onSearch: getCountrySearchWithSuggestion, /// CountrySearch  // if want to search with API then use thi ----> getCountryListFromApi
      onCancelled: () {
        Navigator.pop(context);
      },
      buildSuggestion: (CountryModel countryModel, int index) {
        return countryGenerateColumn(countryModel, index);
      },
      onItemFound: (CountryModel countryModel, int index) {
        return countryGenerateColumn(countryModel, index);
      },
    );
  }

  Widget countryGenerateColumn(CountryModel countryModel, int index) => InkWell(
    child: Stack(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(
              left: 5.0, top: 5.0, right: 5.0, bottom: 5.0),
          child: ConstrainedBox(
            constraints: BoxConstraints(
              minHeight: 50,
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                Container(
                  padding: EdgeInsets.fromLTRB(8.0, 5.0, 0.0, 5.0),
                  width: MediaQuery.of(context).size.width * .60,
                  color: Colors.transparent,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(countryModel.countryName,
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis
                      ),
                      Text(
                        countryModel.countryCode,
                        maxLines: 2,
                        overflow: TextOverflow.ellipsis,
                      ),
                      SizedBox(height: 8),
                      Divider(height: 0.5)
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    ),
  );
  
  Future<List<CountryModel>> getCountrySearch(String search) async {
    print("Resident search = $search");
    if (search == "empty") return [];
    if (search == "error") throw Error();
    List<CountryModel> filterCountryList = [];

    widget.countryModelList.forEach((CountryModel) {
      if (CountryModel.countryName
          .toLowerCase()
          .contains(search.toLowerCase()) ||
          CountryModel.countryCode
              .toLowerCase()
              .contains(search.toLowerCase()))
        filterCountryList.add(CountryModel);
    });

    return filterCountryList;
  }

  Future<List<CountryModel>> getCountrySearchWithSuggestion(
      String search) async {
    print("Resident search = $search");
    if (search == "empty") return [];
    if (search == "error") throw Error();
    List<CountryModel> filterCountryList = [];

    widget.countryModelList.forEach((CountryModel) {
      if (CountryModel.countryName
          .toLowerCase()
          .contains(search.toLowerCase()) ||
          CountryModel.countryCode
              .toLowerCase()
              .contains(search.toLowerCase()))
        filterCountryList.add(CountryModel);
    });

    final suggestionList =
    search.isEmpty ? widget.countryModelList : filterCountryList;

    return suggestionList;
  }
}

class LoadingIndicator extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const Center(
      child: const CircularProgressIndicator(
        valueColor: AlwaysStoppedAnimation<Color>(Colors.deepPurple),
      ),
    );
  }
}

enter image description here