【问题标题】:Flutter search through data displayed in listviewFlutter 搜索列表视图中显示的数据
【发布时间】:2021-03-11 06:16:01
【问题描述】:

我有一个 Flutter 应用程序,它可以获取在线用户列表,并在列表视图中显示数据。我试图实现一个搜索功能来搜索数据,但是每次我尝试在文本字段中输入任何内容时,整个页面都会刷新并执行 api 调用以再次获取数据。这是获取和显示数据的代码

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Prospect List"),
        centerTitle: true,
      ),
      body: prospectData(),
    );
  }

  Widget prospectData() {
    return FutureBuilder(
        future: _fetchData(),
        builder: (BuildContext context, AsyncSnapshot<ProspectList> snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.none:
            case ConnectionState.waiting:
            case ConnectionState.active:
              return Center(
                child: CircularProgressIndicator(),
              );
            case ConnectionState.done:
              if (snapshot.hasError)
                return Text("There was an error: ${snapshot.error}");
              prospectList = snapshot.data;
              return ListView.builder(
                itemCount: prospectList.data.length + 1,
                itemBuilder: (context, i) {
                  if (prospectList.data.length > 0) {
                    return i == 0 ? _searchBar() : _prospectData(i - 1);
                  } else {
                    return Center(child: CircularProgressIndicator());
                  }
                },
              );
            default:
              return null;
          }
        });
  }

  _prospectData(i) {
    final name =
        prospectList.data[i].firstname + " " + prospectList.data[i].lastname;
    final phone = prospectList.data[i].phone;
    final email = prospectList.data[i].email;
    return ListTile(
      title: Text(
        name,
        style: TextStyle(fontSize: 18),
      ),
      subtitle: Text(
        phone,
        style: TextStyle(fontSize: 16),
      ),
      onTap: () => Navigator.push(
          context,
          MaterialPageRoute(
              builder: (context) => CustomerInfo(
                    name: name,
                    phone: phone,
                    email: email,
                  ))),
    );
  }

这是我尝试实现但无法正常工作的搜索方法

 _searchBar() {
    return Container(
      child: Padding(
          padding: EdgeInsets.all(8.0),
          child: TextField(
              decoration: InputDecoration(hintText: 'Search...'),
              onChanged: (text) {
                text = text.toLowerCase();
                setState(() {
                  _prospectDisplay = prospectList.data.where((post) {
                    var postTitle = post.firstname.toLowerCase();
                    return postTitle.contains(text);
                  }).toList();
                });
              })),
    );
  }

编辑 我找到了我正在寻找的解决方案Here

【问题讨论】:

    标签: flutter dart search


    【解决方案1】:

    每次更新主小部件的状态时,小部件都会重新构建。这会导致您的 Widget prospectData() 再次运行,获取数据并重新初始化您的 prospectList

    我会为您的 Widget Tree 建议一种不同的结构,由两个 Widget 组成:

    SearchPage > ProspectList

    SearchPage 是一个 StatelessWidget,负责获取前景列表。

    ProspectList 是一个 StatefulWidget(或者,在我的示例中,是一个 HookWidget),负责显示和过滤获取的潜在客户列表。

    完整源代码:

    import 'package:faker/faker.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_hooks/flutter_hooks.dart';
    import 'package:freezed_annotation/freezed_annotation.dart';
    
    part 'main.freezed.dart';
    
    void main() {
      runApp(
        MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Search Demo',
          home: SearchPage(),
        ),
      );
    }
    
    class SearchPage extends StatelessWidget {
      Future<List<Customer>> _fetchData() async {
        await Future.delayed(Duration(seconds: 2));
        return dummyData;
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Prospects List"),
            centerTitle: true,
          ),
          body: FutureBuilder(
              future: _fetchData(),
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.done) {
                  return (snapshot.hasError)
                      ? Text("There was an error: ${snapshot.error}")
                      : ProspectList(prospects: snapshot.data);
                }
                return Center(child: CircularProgressIndicator());
              }),
        );
      }
    }
    
    class ProspectList extends HookWidget {
      final List<Customer> prospects;
    
      const ProspectList({Key key, this.prospects}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final searchTerm = useState('');
        return ListView(
          children: [
            Padding(
              padding: EdgeInsets.all(8.0),
              child: TextField(
                decoration: InputDecoration(hintText: 'Search...'),
                onChanged: (value) => searchTerm.value = value,
              ),
            ),
            ...prospects
                .where((prospect) =>
                    searchTerm.value.isEmpty ||
                    prospect.name.toLowerCase().contains(searchTerm.value))
                .map(
                  (prospect) => ListTile(
                    title: Text(prospect.name, style: TextStyle(fontSize: 18)),
                    subtitle: Text(prospect.phone, style: TextStyle(fontSize: 16)),
                    onTap: () => {},
                  ),
                )
                .toList(),
          ],
        );
      }
    }
    
    final faker = Faker();
    final dummyData = List.generate(
        100,
        (index) => Customer(
              firstname: faker.person.firstName(),
              lastname: faker.person.lastName(),
              phone: '+1${faker.randomGenerator.integer(999999999)}',
              email: faker.internet.email(),
            ));
    
    @freezed
    abstract class Customer implements _$Customer {
      const factory Customer({
        String firstname,
        String lastname,
        String phone,
        String email,
      }) = _Customer;
    
      const Customer._();
    
      String get name => '$firstname $lastname';
    }
    

    依赖关系

    在这个示例中,我使用了以下依赖项:

    • flutter_hooks,代替StatefulWidgets
    • freezed,定义我的Customer 模型。注意main.freezed.dart只是build_runnerfreezed生成的文件。
    • faker,生成一堆随机的Customers

    运行示例时遇到问题?

    一步一步:

    1. 将以下依赖项添加到您的pubspec.yaml
    name: stackoverflow
    description: A new Flutter project.
    
    publish_to: 'none'
    
    version: 1.0.0+1
    
    environment:
      sdk: ">=2.7.0 <3.0.0"
    
    dependencies:
      flutter:
        sdk: flutter
      freezed_annotation: ^0.12.0
      faker: ^1.3.0
      flutter_hooks: ^0.16.0
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      freezed: ^0.12.7
      build_runner: ^1.11.5
    
    flutter:
    
      uses-material-design: true
      # assets:
      #   - images/a_dot_burr.jpeg
      #   - images/a_dot_ham.jpeg
    
    1. 在项目的根目录中运行 build_runner:
    flutter pub run build_runner watch --delete-conflicting-outputs
    

    这将生成带有freezed的类

    1. 运行您的项目

    【讨论】:

    • 我很难尝试使用这个例子。
    • 对不起。检查我回答的最后一部分,如果您有任何问题,请告诉我。
    • 我收到此错误The parameter firstname of Customer is non-nullable but is neither required nor marked with @Default
    • 抱歉,freezed (0.14) 的最新版本已经处于空安全状态。请改用以前的版本,如我的 pubspec.yaml 中所定义。或者,如果您已经迁移到 null 安全,请告诉我。
    • 我降级了依赖并运行它。它运行正常。谢谢老哥
    【解决方案2】:

    所以,这是您实现 TextField 的方式的错误。

    尝试将 onChanged 更改为 onSubmitted,一切都会按预期进行。

    onChanged 每次按下任何键盘键时都会调用。

    onSubmitted仅在用户按下提交按钮时,导致只有 1 次 API 调用。

    【讨论】:

    • 我改了,还是搜索不到
    • 你把_searchBar放在代码什么地方了??
    • 这里是 itemBuilder: (context, i) {if (prospectList.data.length &gt; 0) {return i == 0 ? _searchBar() : _prospectData(i - 1); 在 ListTile 之前
    • 使用 onSubmitted,尝试同时更改 TextField 中的 textInputAction: TextInputAction.done
    【解决方案3】:

    这里的问题在于FutureBuilder。为了避免不必要的刷新,您需要先声明您的Future,然后在您的initState 中调用未来:

    Future myFuture;
    @override
      void initState() {
        super.initState();
        myFuture = _fetchData();
      }
    
    ...
    FutureBuilder(
            future: myFuture,
    ...
    

    您可以在此视频中找到有关您的问题的更多详细信息:https://www.youtube.com/watch?v=LYN46233cws&t=644s&ab_channel=DevGrub

    【讨论】:

      猜你喜欢
      • 2018-12-13
      • 1970-01-01
      • 1970-01-01
      • 2023-03-04
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 2013-01-15
      • 1970-01-01
      相关资源
      最近更新 更多