【问题标题】:How can I implement search in this Flutter app如何在这个 Flutter 应用中实现搜索
【发布时间】:2023-03-14 10:25:01
【问题描述】:

我正在尝试向这个 Flutter 应用添加搜索功能,因为它从中提取数据的 json 文件有 7000 个结果。

主要是我试图搜索“ctry”和“peopnameincountry”。这是从https://www.youtube.com/watch?v=EwHMSxSWIvQ 撕下来的

按原样..它在获取 json 列表时效果很好,点击显示详细信息页面也可以。

我只需要在主页上实现搜索,这样我就不必滚动浏览成千上万的结果。

感谢任何帮助..谢谢大家。

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

void main() => runApp(new UnReached());

class UnReached extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Unreached'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {

  Future<List<User>> _getUsers() async {

    var data = await http.get("https://cmfiflutterapp.s3-ap-southeast-2.amazonaws.com/UnreachedPeoplesGroup.json");

    var jsonData = json.decode(data.body);

    List<User> users = [];

    for(var u in jsonData){

      User user = User(u["ctry"], u["peopnameincountry"], u["population"], u["primarylanguagename"], u["biblestatus"],  u["primaryreligion"],  u["continent"]);

      users.add(user);

    }

    print(users.length);

    return users;

  }



  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        child: FutureBuilder(
          future: _getUsers(),
          builder: (BuildContext context, AsyncSnapshot snapshot){
            print(snapshot.data);
            if(snapshot.data == null){
              return Container(
                  child: Center(
                      child: Text("Loading...")
                  )
              );
            } else {
              return ListView.builder(
                itemCount: snapshot.data.length,
                itemBuilder: (BuildContext context, int index) {
                  return ListTile(
                    leading: Icon(Icons.arrow_forward_ios),
//                    leading: CircleAvatar(
//                      backgroundImage: NetworkImage(
//                          snapshot.data[index].picture
//                      ),
//                    ),
                    title: Text(snapshot.data[index].peopnameincountry),
                    subtitle: Text(snapshot.data[index].ctry),
                    onTap: (){

                      Navigator.push(context,
                          new MaterialPageRoute(builder: (context) => DetailPage(snapshot.data[index]))
                      );

                    },
                  );
                },
              );
            }
          },
        ),
      ),
    );
  }
}


【问题讨论】:

    标签: search flutter


    【解决方案1】:
    Try adding these function in your code:
    import 'package:flutter/material.dart';
    import 'dart:core';
    
    class HomeScreen1 extends StatefulWidget {
      @override
      HomeScreenState createState() => HomeScreenState();
    }
    
    class HomeScreenState extends State<HomeScreen1> {
      var searchController = new TextEditingController();
      String search;
      List<String> _filterList;
      String _query = "";
      bool _firstSearch = true;
    
      @override
      void initState() {
        super.initState();
      }
    
      HomeScreenState() {
        searchController.addListener(() {
          if (searchController.text.isEmpty) {
            setState(() {
              _firstSearch = true;
              _query = "";
            });
          } else {
            setState(() {
              _firstSearch = false;
              _query = searchController.text;
            });
          }
        });
      }
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: new Container(
            margin: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0),
            child: new Column(
              children: <Widget>[
                _createSearchView(),
                new Expanded(
                  child: _firstSearch ? _createListView() : _performSearch(),
                ),
              ],
            ),
          ),
        );
      }
    
      Widget _createSearchView() {
        return new Container(
          decoration: BoxDecoration(border: Border.all(width: 1.0)),
          child: new TextField(
            controller: searchController,
            decoration: InputDecoration(
              icon: Icon(Icons.search),
              hintText: "Search",
              hintStyle: new TextStyle(color: Colors.grey[300]),
            ),
            //textAlign: TextAlign.center,
          ),
        );
      }
    
      Widget _createListView() {
        return FutureBuilder(
              future: _getUsers(),
              builder: (BuildContext context, AsyncSnapshot snapshot){
                print(snapshot.data);
                if(snapshot.data == null){
                  return Container(
                      child: Center(
                          child: Text("Loading...")
                      )
                  );
                } else {
                  return ListView.builder(
                    itemCount: snapshot.data.length,
                    itemBuilder: (BuildContext context, int index) {
                      return ListTile(
                        leading: Icon(Icons.arrow_forward_ios),
    //                    leading: CircleAvatar(
    //                      backgroundImage: NetworkImage(
    //                          snapshot.data[index].picture
    //                      ),
    //                    ),
                        title: Text(snapshot.data[index].peopnameincountry),
                        subtitle: Text(snapshot.data[index].ctry),
                        onTap: (){
    
                          Navigator.push(context,
                              new MaterialPageRoute(builder: (context) => DetailPage(snapshot.data[index]))
                          );
    
                        },
                      );
                    },
                  );
                }
              },
            ),
      }
    
    
      Widget _performSearch() {
        return FutureBuilder<List>(builder: (context, snapshot) {
          _filterList = new List<String>();
          for (int i = 0; i < snapshot.data.length; i++) {
            var item = snapshot.data[i];
            if ((item.toString().toLowerCase()).contains(_query.toLowerCase())) {
    
              _filterList.add(item.toString());
            }
          }
          return _createFilteredListView();
        });
      }
    
      Widget _createFilteredListView() {
        return ListView.builder(
            itemCount: _filterList.length,
            itemBuilder: (BuildContext context, int index) {
              return new Card(
                color: Colors.white,
                elevation: 5.0,
                child: new Container(
                  margin: EdgeInsets.all(15.0),
                  child: new Text("${_filterList[index]}"),
                ),
              );
            });
      }
    }
    

    【讨论】:

      【解决方案2】:

      首先感谢您抽出宝贵时间。 我收到错误:

      Another exception was thrown: NoSuchMethodError: The getter 'length' was called on null.
      

      我认为来自 _performSearch Widget。请查看下面的完整代码,让我知道我可以修复什么。谢谢!!!

      其他一切正常,但当我尝试进行搜索时出现错误。

      import 'package:flutter/material.dart';
      import 'dart:core';
      import 'package:http/http.dart' as http;
      import 'dart:convert';
      import 'dart:async';
      
      
      class IslandWaves extends StatefulWidget {
        @override
        HomeScreenState createState() => HomeScreenState();
      }
      
      class HomeScreenState extends State<IslandWaves> {
      
        var searchController = new TextEditingController();
        String search;
        List<String> _filterList;
        String _query = "";
        bool _firstSearch = true;
      
        @override
        void initState() {
          super.initState();
        }
      
        HomeScreenState() {
          searchController.addListener(() {
            if (searchController.text.isEmpty) {
              setState(() {
                _firstSearch = true;
                _query = "";
              });
            } else {
              setState(() {
                _firstSearch = false;
                _query = searchController.text;
              });
            }
          });
        }
      
        Future<List<User>> _getUsers() async {
      
          var data = await http.get("https://cmfiflutterapp.s3-ap-southeast-2.amazonaws.com/UnreachedPeoplesGroup.json");
      
          var jsonData = json.decode(data.body);
      
          List<User> users = [];
          for(var u in jsonData){
      
            User user = User(u["ctry"], u["peopnameincountry"], u["population"], u["primarylanguagename"], u["biblestatus"],  u["primaryreligion"],  u["continent"]);
      
            users.add(user);
      
          }
      
          print(users.length);
      
          return users;
      
        }
      
      
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(
              title: Text('Unreached'),
            ),
            body: new Container(
              margin: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0),
              child: new Column(
                children: <Widget>[
                  _createSearchView(),
                  new Expanded(
                    child: _firstSearch ? _createListView() : _performSearch(),
                  ),
                ],
              ),
            ),
          );
        }
      
        Widget _createSearchView() {
          return new Container(
            decoration: BoxDecoration(border: Border.all(width: 1.0)),
            child: new TextField(
              controller: searchController,
              decoration: InputDecoration(
                icon: Icon(Icons.search),
                hintText: "Search",
                hintStyle: new TextStyle(color: Colors.grey[300]),
              ),
              //textAlign: TextAlign.center,
            ),
          );
        }
      
        Widget _createListView() {
          return FutureBuilder(
            future: _getUsers(),
            builder: (BuildContext context, AsyncSnapshot snapshot){
              print(snapshot.data);
              if(snapshot.data == null){
                return Container(
                    child: Center(
                        child: Text("Loading...")
                    )
                );
              } else {
                return ListView.builder(
                  itemCount: snapshot.data.length,
                  itemBuilder: (BuildContext context, int index) {
                    return ListTile(
                      leading: Icon(Icons.arrow_forward_ios),
                      title: Text(snapshot.data[index].peopnameincountry),
                      subtitle: Text(snapshot.data[index].ctry),
                      onTap: (){
      
                        Navigator.push(context,
                            new MaterialPageRoute(builder: (context) => DetailPage(snapshot.data[index]))
                        );
                      },
                    );
                  },
                );
              }
            },
          );
        }
      
        Widget _performSearch() {
          return FutureBuilder<List>(builder: (context, snapshot) {
            _filterList = new List<String>();
            for (int i = 0; i < snapshot.data.length; i++) {
              var item = snapshot.data[i];
              if ((item.toString().toLowerCase()).contains(_query.toLowerCase())) {
      
                _filterList.add(item.toString());
              }
            }
            return _createFilteredListView();
          });
        }
      
        Widget _createFilteredListView() {
          return ListView.builder(
              itemCount: _filterList.length,
              itemBuilder: (BuildContext context, int index) {
                return new Card(
                  color: Colors.white,
                  elevation: 5.0,
                  child: new Container(
                    margin: EdgeInsets.all(15.0),
                    child: new Text("${_filterList[index]}"),
                  ),
                );
              });
        }
      }
      
      
      

      【讨论】:

      • 你在哪里调用_getusers方法?
      【解决方案3】:

      FutureBuilder 小部件的概念是在收到它后立即构建,但与此同时,快照根本不包含任何数据。所以当你打电话时:

      for (int i = 0; i &lt; snapshot.data.length; i++) {

      由于尚未收到数据,您至少在开始时会在 null 上调用 length

      解决办法是创建一个开关,当状态完成时调用`snapshot.data:

          switch (snapshot.connectionState) {
            case ConnectionState.none:
              return DefaultWidget();         // For instance a CircularProgress
      
            case ConnectionState.active:
              return DefaultWidget();         // For instance a CircularProgress
      
            case ConnectionState.waiting:
              return DefaultWidget();         // For instance a CircularProgress
      
            case ConnectionState.done:
      
              if (snapshot.hasError)
                return ErrorWidget('Error: ${snapshot.error}'); //For example a Text Widget 
      
               // Your code here:
              _filterList = new List<String>();
            for (int i = 0; i < snapshot.data.length; i++) {
              var item = snapshot.data[i];
              if ((item.toString().toLowerCase()).contains(_query.toLowerCase())) {
      
                _filterList.add(item.toString());
              }
            }
            return _createFilteredListView();
          }
          return null; // unreachable
      

      更多关于here

      【讨论】:

      • 您好 Ferdi,我按照您的建议添加了一个开关。不再崩溃,但它只是挂在这里加载...:case ConnectionState.waiting : return FadingText( 'Loading...' );
      • 嗯...根据您自己答案中的代码,我想说这是因为在您的小部件_performSearch 中没有未来可以连接,添加参数future 就像您为你的小部件_createListView
      【解决方案4】:

      抱歉,伙计们...我最终采用了一种稍微不同的方法,我认为它的响应速度比 FutureBuilder 方法稍快。也许这只是我的互联网。不确定。

      import 'package:flutter/material.dart';
      import 'dart:core';
      import 'package:http/http.dart' as http;
      import 'dart:convert';
      import 'dart:async';
      import 'package:progress_indicators/progress_indicators.dart';
      
      class IslandWaves extends StatefulWidget {
        @override
        HomeScreenState createState() => HomeScreenState();
      }
      
      class HomeScreenState extends State<IslandWaves> {
        List<User> _list = [];
        List<User> _search = [];
        var loading = false;
        Future<Null> fetchData() async {
          setState(() {
            loading = true;
          });
          _list.clear();
          final response = await http.get(
              "https://cmfiflutterapp.s3-ap-southeast-2.amazonaws.com/UnreachedPeoplesGroup.json");
          if (response.statusCode == 200) {
            final data = jsonDecode(response.body);
            setState(() {
              for (Map i in data) {
                _list.add(User.formJson(i));
                loading = false;
              }
            });
          }
        }
      
        TextEditingController controller = new TextEditingController();
      
        onSearch(String text) async {
          _search.clear();
          if (text.isEmpty) {
            setState(() {});
            return;
          }
      
          _list.forEach((f) {
            if (f.ctry.contains(text) ||
                f.peopnameincountry.toString().contains(text)) _search.add(f);
          });
      
          setState(() {});
        }
      
        @override
        void initState() {
          // TODO: implement initState
          super.initState();
          fetchData();
        }
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(),
            body: Container(
              child: Column(
                children: <Widget>[
                  Container(
                    padding: EdgeInsets.all(10.0),
                    color: Colors.blue,
                    child: Card(
                      child: ListTile(
                        leading: Icon(Icons.search),
                        title: TextField(
                          controller: controller,
                          onChanged: onSearch,
                          decoration: InputDecoration(
                              hintText: "Search", border: InputBorder.none),
                        ),
                        trailing: IconButton(
                          onPressed: () {
                            controller.clear();
                            onSearch('');
                          },
                          icon: Icon(Icons.cancel),
                        ),
                      ),
                    ),
                  ),
                  loading
                      ? Center(
                          heightFactor: 20.0,
                          child: FadingText('Loading...'),
                        )
                      : Expanded(
                          child: _search.length != 0 || controller.text.isNotEmpty
                              ? ListView.builder(
                                  itemCount: _search.length,
                                  itemBuilder: (context, i) {
                                    final b = _search[i];
                                    return GestureDetector(
                                      onTap: () {
                                        Navigator.push(
                                            context,
                                            new MaterialPageRoute(
                                                builder: (context) =>
                                                    DetailPage(_search[i])));
                                        debugPrint('TopNav');
                                      },
                                      child: Container(
                                          padding: EdgeInsets.all(10.0),
                                          child: Column(
                                            crossAxisAlignment:
                                                CrossAxisAlignment.start,
                                            children: <Widget>[
                                              Text(
                                                b.ctry,
                                                style: TextStyle(
                                                    fontWeight: FontWeight.bold,
                                                    fontSize: 18.0),
                                              ),
                                              SizedBox(
                                                height: 4.0,
                                              ),
                                              Text(b.peopnameincountry),
                                            ],
                                          )),
                                    );
                                  },
                                )
                              : ListView.builder(
                                  itemCount: _list.length,
                                  itemBuilder: (context, i) {
                                    final a = _list[i];
                                    return GestureDetector(
                                      onTap: () {
                                        Navigator.push(
                                            context,
                                            new MaterialPageRoute(
                                                builder: (context) =>
                                                    DetailPage(_list[i])));
                                        debugPrint('BottomNav');
                                      },
                                      child: Container(
                                          padding: EdgeInsets.all(10.0),
                                          child: Column(
                                            crossAxisAlignment:
                                                CrossAxisAlignment.start,
                                            children: <Widget>[
                                              Text(
                                                a.ctry,
                                                style: TextStyle(
                                                    fontWeight: FontWeight.bold,
                                                    fontSize: 18.0),
                                              ),
                                              SizedBox(
                                                height: 4.0,
                                              ),
                                              Text(a.peopnameincountry),
                                            ],
                                        )
                                      ),
                                  );
                              },
                         ),
                    ),
                ],
              ),
            ),
          );
        }
      }
      
      class DetailPage extends StatelessWidget{....etc.}
      

      【讨论】:

      • 只是为了让您知道,这并不是真正的 SO 应该如何工作,而且您不能通过用其他问题回答您自己的问题来同时提出 3 个问题。如果你有问题就问吧,但如果你通过修复它来面对一个新问题,那就是一个新问题=>打开另一个问题。
      • 你似乎是新人,所以没关系,但考虑到人们进入你的主题来寻找答案......他们将面临新的问题——'即使它对你有用,这个想法是解决问题,而不是绕过它,这可能无法扩展到其他人;)
      • 抱歉,Ferdi,我还在学习中,感谢您的建议和帮助。非常感谢。
      猜你喜欢
      • 2020-03-13
      • 2021-03-29
      • 2019-10-10
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 2019-02-13
      • 1970-01-01
      • 2020-06-25
      相关资源
      最近更新 更多