【问题标题】:Flutter: Searching Through ListFlutter:通过列表搜索
【发布时间】:2019-08-13 00:29:29
【问题描述】:

在学习 Flutter 框架/Dart 的过程中,我创建了一个示例项目。

我有一个名为person.dart 的课程,内容如下:

class Person {
  String personFirstName;
  String personLastName;

  Person(
    {this.personFirstName, this.personLastName}
  );
}

接下来,我有一个“builder”类person_builder.dart,我在其中创建人员样本数据:

导入'package:adv_search/model/person.dart';

class PersonDataBuilder {
  List getPeople() {
    return [
      Person(
          personFirstName: "John",
          personLastName: "Smith"
      ),
      Person(
          personFirstName: "Alex",
          personLastName: "Johnson"
      ),
      Person(
          personFirstName: "Jane",
          personLastName: "Doe"
      ),
      Person(
          personFirstName: "Eric",
          personLastName: "Johnson"
      ),
      Person(
          personFirstName: "Michael",
          personLastName: "Eastwood"
      ),
      Person(
          personFirstName: "Benjamin",
          personLastName: "Woods"
      ),
      Person(
          personFirstName: "Abraham",
          personLastName: "Atwood"
      ),
      Person(
          personFirstName: "Anna",
          personLastName: "Clack"
      ),
      Person(
          personFirstName: "Clark",
          personLastName: "Phonye"
      ),
      Person(
          personFirstName: "Kerry",
          personLastName: "Mirk"
      ),
      Person(
          personFirstName: "Eliza",
          personLastName: "Wu"
      ),
      Person(
          personFirstName: "Jackey",
          personLastName: "Lee"
      ),
      Person(
          personFirstName: "Kristin",
          personLastName: "Munson"
      ),
      Person(
          personFirstName: "Oliver",
          personLastName: "Watson"
      ),

    ];
  }
}

我已经在顶部导航栏中添加了搜索功能...点击搜索图标后,搜索字段打开(在顶部导航中),允许我提供搜索输入。我有一个带有监听器的控制器,可以很好地捕获用户输入,如我的main.dart 文件中所示:

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:adv_search/model/person.dart';
import 'package:adv_search/data/person_builder.dart';

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

class AdvancedSearch extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'List of People',
      home: new ListPersonPage(title: 'List of People'),
    );
  }
}

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

  final String title;

  @override
  _ListPersonPageState createState() => _ListPersonPageState();
}

class _ListPersonPageState extends State<ListPersonPage> {
  List people;
  TextEditingController controller = new TextEditingController();
  String filter;

  Widget appBarTitle = new Text("List of People");
  Icon actionIcon = new Icon(Icons.search);

  @override
  void initState() {
    PersonDataBuilder pdb = new PersonDataBuilder();
    people = pdb.getPeople();
    controller.addListener(() {
      setState(() {
        filter = controller.text;
      });
    });
    super.initState();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final appTopAppBar = AppBar(
      elevation: 0.1,
      title: appBarTitle,
      actions: <Widget>[
        new IconButton(
          icon: actionIcon,
          onPressed: () {

            setState(() {
              if (this.actionIcon.icon == Icons.search) {
                this.actionIcon = new Icon(Icons.close);
                this.appBarTitle = new TextField(
                  style: new TextStyle(
                    color: Colors.white,
                  ),
                  decoration: new InputDecoration(
                      prefixIcon: new Icon(Icons.search, color: Colors.white),
                      hintText: "Search...",
                      hintStyle: new TextStyle(color: Colors.white)),
                  controller: controller,
                );
              } else {
                this.actionIcon = new Icon(Icons.search);
                this.appBarTitle = new Text("List of People");
              }
            });

          },
        ),
      ],
    );

    ListTile personListTile(Person person) => ListTile(
      title: Text(
        person.personFirstName + " " + person.personLastName,
        style: TextStyle(color: Colors.black45, fontWeight: FontWeight.bold),
      ),);

    Card personCard(Person person) => Card(
      child: Container(
        decoration: BoxDecoration(color: Colors.grey[300]),
        child: personListTile(person),
      ),
    );

    final appBody = Container(
      child: ListView.builder(
        scrollDirection: Axis.vertical,
        shrinkWrap: true,
        itemCount: people.length,
        itemBuilder: (BuildContext context, int index) {
          //return filter == null || filter == "" ? personCard(people[index]) : people[index].contains(filter) ? personCard(people[index]) : new Container();
          return filter == null || filter == "" ? personCard(people[index]) : new Container();
        },
      ),
    );

    return Scaffold(
      appBar: appTopAppBar,
      body: appBody,
    );
  }
}

但是,我遇到困难并寻求指导的地方是ListView.builder

这是我目前在ListView.builder(第 106 行)返回的内容--

return filter == null || filter == "" ? personCard(people[index]) : people[index].contains(filter) ? personCard(people[index]) : new Container();

我得到的错误是:

NoSuchMethodError:类 'Person' 没有实例方法 'contains'

接收者:“人”的实例

尝试调用: contains("John")

目前,鉴于上述错误,我无法过滤 。我想知道:

  1. 如何允许用户按名字或姓氏进行搜索,并根据用户输入使用过滤后的人员卡片刷新视图?
  2. 当我单击 搜索 图标时,输入字段没有自动聚焦...我是否以正确的方式设置搜索功能?
  3. 是否有更好/推荐的方法来创建数据(人员列表)?

编辑 1

我应该补充一点:启动应用程序后,我可以看到通过构建器类创建的所有人的列表。

编辑 2

添加了完整的ALL代码文件;重新表述了帖子的部分内容并添加了几个额外的问题。

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    错误准确地告诉您代码有什么问题。当您编写 items[index].contains(searchFilter) 时,编译器会尝试在 Person 类中查找“包含”方法。而且由于您还没有实现它,它正在引发异常。

    实现搜索的一种方法如下:

    List<Person> _personList = [] ;
    List<Person> _searchList = [] ;
    
    // populate _personList
    
    _personList.forEach((p) {
       if (p.personFirstName == searchFilter or p.personLastName == searchFilter) {
           _searchList.add(f);  
       }
    }
    

    然后在列表视图中显示 _searchList 而不是完整的 _personList。例如,如下所示:

        Widget _buildPersonListView() {
          if (!_isSearching) {
             return _listContents(_personList);
          } else {
             return _listContents(_searchList);
          }
    

    然后你定义 _listContents 如下:

    Widget _listContents(List<Person> list) {
        // implement list view
    }
    

    将您的小部件构建方法设置为:

     @override
      Widget build(BuildContext context) {
        return Scaffold(
          .....
          appBar: buildBar(context),
          body: _buildPersonView()
        );
      }
    

    最后,根据用户交互设置_isSearching。

    【讨论】:

    • 在我的返回语句return searchFilter == null || .... itemsList 类型,它从 builder 类的 getPerson 方法中获取人员列表,它返回带有名字和姓氏的 Person 对象...也就是说,在上面的示例中,_personList 应该是 items 并且是在 ListView.builder 中实现的吗? (因为我试过了,它没有锻炼)
    • 好的...所以在玩了一会儿之后,我取得了进步...您的回答给了我一个线索;为此+1!...一旦完成,我将发布完整的代码...
    【解决方案2】:

    所以这不是微不足道的......很可能是因为我是 Dart / Flutter 框架的新手......无论如何,这个答案建立在@Sukhi 的回答之上:

    首先,person_buider.dart 中的getPeople 方法需要返回List&lt;Person&gt; 类型的返回

    最后,在main.dart 文件中发生了以下变化:

    1. _ListPersonPageState State 内部,我们需要定义两个不同的列表(如@Sukhi 所述):List&lt;Person&gt; _personList = []List&lt;Person&gt; _filteredList = []

    2. initState 方法内部:

      一个。我们需要创建一个临时列表,遍历从 PeopleDataBuilder 类的 getPeople 方法检索到的列表,并将返回列表中的每个项目添加到我们创建的这个临时列表中

      b.然后,我们setState 并在其中将这个临时列表分配给_personList,然后将_personList 分配给_filteredList

      c。当我们向控制器添加 listener 时,我们会进行一些(健全性)验证,并在此基础上 setState

    3. build 方法中,我们需要(再次)创建一个临时列表(如果filter 不为空),然后将该临时列表分配给@987654337 @

    4. 有一些怪癖...例如单击搜索图标时没有出现光标等...因此为TextField添加/定义了基本属性(例如cursorColor: Colors.white,等)

    注意:创建这些临时列表很重要...否则使用搜索时会出错(我认为错误是迭代期间并发修改,如果我我没记错)

    完整的main.dart代码,供参考:

    import 'package:flutter/material.dart';
    import 'package:flutter/foundation.dart';
    import 'package:adv_search/model/person.dart';
    import 'package:adv_search/data/person_builder.dart';
    
    void main() => runApp(new AdvancedSearch());
    
    class AdvancedSearch extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'List of People',
          home: new ListPersonPage(title: 'List of People'),
        );
      }
    }
    
    class ListPersonPage extends StatefulWidget {
      ListPersonPage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _ListPersonPageState createState() => _ListPersonPageState();
    }
    
    class _ListPersonPageState extends State<ListPersonPage> {
      List<Person> _personList = [];
      List<Person> _filteredList = [];
      TextEditingController controller = new TextEditingController();
      String filter = "";
    
      Widget appBarTitle = new Text("List of People");
      Icon actionIcon = new Icon(Icons.search);
    
      @override
      void dispose() {
        controller.dispose();
        super.dispose();
      }
    
      @override
      void initState() {
        PersonDataBuilder pdb = new PersonDataBuilder();
        List<Person> tmpList = new List<Person>();
        for(int i=0; i < pdb.getPeople().length; i++) {
          tmpList.add(pdb.getPeople()[i]);
        }
        setState(() {
          _personList = tmpList;
          _filteredList = _personList;
        });
        controller.addListener(() {
          if(controller.text.isEmpty) {
            setState(() {
              filter = "";
              _filteredList = _personList;
            });
          } else {
            setState(() {
              filter = controller.text;
            });
          }
        });
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
    
        final appTopAppBar = AppBar(
          elevation: 0.1,
          title: appBarTitle,
          actions: <Widget>[
            new IconButton(
              icon: actionIcon,
              onPressed: () {
                setState(() {
                  if (this.actionIcon.icon == Icons.search) {
                    this.actionIcon = new Icon(Icons.close);
                    this.appBarTitle = new TextField(
                      controller: controller,
                      decoration: new InputDecoration(
                        prefixIcon: new Icon(Icons.search, color: Colors.white),
                        hintText: "Search...",
                        hintStyle: new TextStyle(color: Colors.white),
                      ),
                      style: new TextStyle(
                        color: Colors.white,
                      ),
                      autofocus: true,
                      cursorColor: Colors.white,
                    );
                  } else {
                    this.actionIcon = new Icon(Icons.search);
                    this.appBarTitle = new Text("List of People");
                    _filteredList = _personList;
                    controller.clear();
                  }
                });
              },
            ),
          ],
        );
    
        ListTile personListTile(Person person) => ListTile(
          title: Text(
            person.personFirstName + " " + person.personLastName,
            style: TextStyle(color: Colors.black45, fontWeight: FontWeight.bold),
          ),);
    
        Card personCard(Person person) => Card(
          child: Container(
            decoration: BoxDecoration(color: Colors.grey[300]),
            child: personListTile(person),
          ),
        );
    
        if((filter.isNotEmpty)) {
          List<Person> tmpList = new List<Person>();
          for(int i = 0; i < _filteredList.length; i++) {
            if(_filteredList[i].personFirstName.toLowerCase().contains(filter.toLowerCase())) {
              tmpList.add(_filteredList[i]);
            }
          }
          _filteredList = tmpList;
        }
    
        final appBody = Container(
          child: ListView.builder(
            scrollDirection: Axis.vertical,
            shrinkWrap: true,
            itemCount: _personList == null ? 0 : _filteredList.length,
            itemBuilder: (BuildContext context, int index) {
              return personCard(_filteredList[index]);
            },
          ),
        );
    
        return Scaffold(
          appBar: appTopAppBar,
          body: appBody,
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-04-10
      • 2017-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多