颤振:使用ListView实现StreamBuilder中数据的搜索功能

颤振:使用ListView实现StreamBuilder中数据的搜索功能,listview,flutter,dart,stream-builder,Listview,Flutter,Dart,Stream Builder,在我的Flitter应用程序中,我有一个包含所有用户的屏幕。用户列表由StreamBuilder生成,它从Cloud Firestore获取数据,并在列表视图中显示用户。为了改进功能,我希望能够使用Appbar中的搜索栏搜索此用户列表 我已经尝试过了,效果很好,但我不知道如何使用StreamBuilder来实现它。作为一个颤振初学者,我将感谢任何帮助!下面是我的用户屏幕和StreamBuilder import 'package:flutter/material.dart'; import 'p

在我的Flitter应用程序中,我有一个包含所有用户的屏幕。用户列表由
StreamBuilder
生成,它从Cloud Firestore获取数据,并在
列表视图中显示用户。为了改进功能,我希望能够使用
Appbar
中的搜索栏搜索此用户列表

我已经尝试过了,效果很好,但我不知道如何使用
StreamBuilder来实现它。作为一个颤振初学者,我将感谢任何帮助!下面是我的用户屏幕和
StreamBuilder

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

class UsersScreen extends StatefulWidget {
  static const String id = 'users_screen';

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

class _UsersScreenState extends State<UsersScreen> {
  static Map<String, dynamic> userDetails = {};
  static final String environment = userDetails['environment'];
  Widget appBarTitle = Text('Manage all users');
  Icon actionIcon = Icon(Icons.search);
  final TextEditingController _controller = TextEditingController();
  String approved = 'yes';

  getData() async {
    FirebaseUser user = await FirebaseAuth.instance.currentUser();
    return await _firestore
        .collection('users')
        .document(user.uid)
        .get()
        .then((val) {
      userDetails.addAll(val.data);
    }).whenComplete(() {
      print('${userDetails['environment']}');
      setState(() {});
    });
  }

  _printLatestValue() {
    print('value from searchfield: ${_controller.text}');
  }

  @override
  void initState() {
    getData();
    _controller.addListener(_printLatestValue);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: appBarTitle,
          actions: <Widget>[
            IconButton(
              icon: actionIcon,
              onPressed: () {
                setState(() {
                  if (this.actionIcon.icon == Icons.search) {
                    this.actionIcon = Icon(Icons.close);
                    this.appBarTitle = TextField(
                      controller: _controller,
                      style: TextStyle(
                        color: Colors.white,
                      ),
                      decoration: InputDecoration(
                          prefixIcon: Icon(Icons.search, color: Colors.white),
                          hintText: "Search...",
                          hintStyle: TextStyle(color: Colors.white)),
                      onChanged: (value) {
                        //do something
                      },
                    );
                  } else {
                    this.actionIcon = Icon(Icons.search);
                    this.appBarTitle = Text('Manage all users');
                    // go back to showing all users
                  }
                });
              },
            ),
          ]),
      body: SafeArea(
        child: StreamUsersList('${userDetails['environment']}', approved),
      ),
    );
  }
}
我将其传递到
StreamUsersList
并将其添加到初始化中。在
ListView.Builder
中,我添加了一个if语句,其中包含
(snapshot.data.documents[index].data['login'].contains(searchResult))
。有关示例,请参见my
ListView.Builder的以下代码

else if (snapshot.hasData) {
            return ListView.builder(
                padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
                itemCount: snapshot.data.documents.length,
                itemBuilder: (BuildContext context, int index) {
                  DocumentSnapshot user = snapshot.data.documents[index];
                  final record3 = Record3.fromSnapshot(user);
                  String unitNr = user.data['unit'];
                  if (user.data['login'].contains(searchResult) ||
                      user.data['name'].contains(searchResult) ||
                      user.data['unit'].contains(searchResult)) {
                    return Padding(
                      padding: EdgeInsets.symmetric(
                        horizontal: 7.0,
                        vertical: 3.0,
                      ),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
//This CardCustom is just a Card with some styling
                           CardCustomUsers(
                              title: unitNr,
                              color: Colors.white,
                              weight: FontWeight.bold,
                              subTitle:
                                  '${user.data['name']}\n${user.data['login']}',
                          ),
                        ],
                      ),
                    );
                  } else {
                    return Visibility(
                      visible: false,
                      child: Text(
                        'no match',
                        style: TextStyle(fontSize: 4.0),
                      ),
                    );
                  }
                });
          } else {
            return Center(
              child: Text('Something is wrong'),
            );
          }
else if(snapshot.hasData){
返回ListView.builder(
填充:边缘组。对称(水平:10.0,垂直:20.0),
itemCount:snapshot.data.documents.length,
itemBuilder:(构建上下文,int索引){
DocumentSnapshot user=snapshot.data.documents[index];
最终记录3=记录3.fromSnapshot(用户);
字符串unitNr=user.data['unit'];
if(user.data['login']包含(searchResult)||
user.data['name'].包含(searchResult)||
user.data['unit'].contains(searchResult)){
返回填充(
填充:EdgeInsets.symmetric(
水平:7.0,
垂直:3.0,
),
子:列(
crossAxisAlignment:crossAxisAlignment.start,
儿童:[
//这张卡片只是一张带有一些样式的卡片
信用卡用户(
标题:unitNr,
颜色:颜色,白色,
重量:fontwweight.bold,
字幕:
“${user.data['name']}\n${user.data['login']}”,
),
],
),
);
}否则{
返回可见性(
可见:假,
子:文本(
“没有对手”,
样式:TextStyle(fontSize:4.0),
),
);
}
});
}否则{
返回中心(
孩子:文本(‘有什么不对劲’),
);
}

您可以采取以下方法

  • 您将在快照中收到完整的数据
  • 具有如下小部件的层次结构: StreamBuilder( ValueListenableBuilder( ListView.Builder ) )
  • 创建ValueNotifier并将其交给ValueListenable builder
  • 使用搜索视图更改ValueNotifier的值
  • 当ValueNotifier的值更改时,ListView.builder将重新生成,此时,如果您根据查询将过滤列表提供给ListView.builder,则它将按您希望的方式为您工作
  • 我希望这有帮助,如果有任何疑问,请让我知道

    已编辑

    您不需要ValueNotifier,而是需要StreamBuilder。因此,小部件的最终层次结构是: StreamBuilder( StreamBuilder>( ListView.Builder ) )

    我没有像你这样的环境,所以我模仿了它并创建了一个例子。你可以参考它,我希望它能给你一些想法来解决你的问题。以下是您可以参考的工作代码:

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(
          home: Scaffold(
            appBar: AppBar(),
            body: SearchWidget(),
          ),
        ));
    
    class SearchWidget extends StatelessWidget {
      SearchWidget({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return SingleChildScrollView(
          child: Column(
            children: <Widget>[
              TextField(onChanged: _filter),
              StreamBuilder<List<User>>( // StreamBuilder<QuerySnapshot> in your code.
                initialData: _dataFromQuerySnapShot, // you won't need this. (dummy data).
                // stream: Your querysnapshot stream.
                builder:
                    (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
                  return StreamBuilder<List<User>>(
                    key: ValueKey(snapshot.data),
                    initialData: snapshot.data,
                    stream: _stream,
                    builder:
                        (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
                          print(snapshot.data);
                      return ListView.builder(
                        shrinkWrap: true,
                        itemCount: snapshot.data.length,
                        itemBuilder: (BuildContext context, int index) {
                          return Text(snapshot.data[index].name);
                        },
                      );
                    },
                  );
                },
              )
            ],
          ),
        );
      }
    }
    
    StreamController<List<User>> _streamController = StreamController<List<User>>();
    Stream<List<User>> get _stream => _streamController.stream;
    _filter(String searchQuery) {
      List<User> _filteredList = _dataFromQuerySnapShot
          .where((User user) => user.name.toLowerCase().contains(searchQuery.toLowerCase()))
          .toList();
      _streamController.sink.add(_filteredList);
    }
    
    List<User> _dataFromQuerySnapShot = <User>[
      // every user has same enviornment because you are applying
      // such filter on your query snapshot.
      // same is the reason why every one is approved user.
      User('Zain Emery', 'some_enviornment', true),
      User('Dev Franco', 'some_enviornment', true),
      User('Emilia ONeill', 'some_enviornment', true),
      User('Zohaib Dale', 'some_enviornment', true),
      User('May Mcdougall', 'some_enviornment', true),
      User('LaylaRose Mitchell', 'some_enviornment', true),
      User('Beck Beasley', 'some_enviornment', true),
      User('Sadiyah Walker', 'some_enviornment', true),
      User('Mae Malone', 'some_enviornment', true),
      User('Judy Mccoy', 'some_enviornment', true),
    ];
    
    class User {
      final String name;
      final String environment;
      final bool approved;
    
      const User(this.name, this.environment, this.approved);
    
      @override
      String toString() {
        return 'name: $name environment: $environment approved: $approved';
      }
    }
    
    导入'dart:async';
    进口“包装:颤振/材料.省道”;
    void main()=>runApp(MaterialApp(
    家:脚手架(
    appBar:appBar(),
    正文:SearchWidget(),
    ),
    ));
    类SearchWidget扩展了无状态Widget{
    SearchWidget({Key}):超级(Key:Key);
    @凌驾
    小部件构建(构建上下文){
    返回SingleChildScrollView(
    子:列(
    儿童:[
    文本字段(一旦更改:_过滤器),
    StreamBuilder(//StreamBuilder在代码中。
    initialData:\u dataFromQuerySnapShot,//您不需要这个。(虚拟数据)。
    //流:您的querysnapshot流。
    建设者:
    (BuildContext上下文,异步快照){
    返回流生成器(
    key:ValueKey(快照数据),
    initialData:snapshot.data,
    溪流:_溪流,
    建设者:
    (BuildContext上下文,异步快照){
    打印(快照数据);
    返回ListView.builder(
    收缩膜:对,
    itemCount:snapshot.data.length,
    itemBuilder:(构建上下文,int索引){
    返回文本(snapshot.data[index].name);
    },
    );
    },
    );
    },
    )
    ],
    ),
    );
    }
    }
    StreamController _StreamController=StreamController();
    Stream get _Stream=>_streamController.Stream;
    _过滤器(字符串搜索查询){
    列表_filteredList=_dataFromQuerySnapShot
    .where((User)=>User.name.toLowerCase()包含(searchQuery.toLowerCase())
    .toList();
    _streamController.sink.add(_filteredList);
    }
    列表_dataFromQuerySnapShot=[
    //每个用户都有相同的环境,因为您正在申请
    //在查询快照上设置这样的过滤器。
    //这也是为什么每个人都是被批准的用户的原因。
    用户('Zain Emery','some_environment',true),
    用户('
    
    else if (snapshot.hasData) {
                return ListView.builder(
                    padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
                    itemCount: snapshot.data.documents.length,
                    itemBuilder: (BuildContext context, int index) {
                      DocumentSnapshot user = snapshot.data.documents[index];
                      final record3 = Record3.fromSnapshot(user);
                      String unitNr = user.data['unit'];
                      if (user.data['login'].contains(searchResult) ||
                          user.data['name'].contains(searchResult) ||
                          user.data['unit'].contains(searchResult)) {
                        return Padding(
                          padding: EdgeInsets.symmetric(
                            horizontal: 7.0,
                            vertical: 3.0,
                          ),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: <Widget>[
    //This CardCustom is just a Card with some styling
                               CardCustomUsers(
                                  title: unitNr,
                                  color: Colors.white,
                                  weight: FontWeight.bold,
                                  subTitle:
                                      '${user.data['name']}\n${user.data['login']}',
                              ),
                            ],
                          ),
                        );
                      } else {
                        return Visibility(
                          visible: false,
                          child: Text(
                            'no match',
                            style: TextStyle(fontSize: 4.0),
                          ),
                        );
                      }
                    });
              } else {
                return Center(
                  child: Text('Something is wrong'),
                );
              }
    
    import 'dart:async';
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(
          home: Scaffold(
            appBar: AppBar(),
            body: SearchWidget(),
          ),
        ));
    
    class SearchWidget extends StatelessWidget {
      SearchWidget({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return SingleChildScrollView(
          child: Column(
            children: <Widget>[
              TextField(onChanged: _filter),
              StreamBuilder<List<User>>( // StreamBuilder<QuerySnapshot> in your code.
                initialData: _dataFromQuerySnapShot, // you won't need this. (dummy data).
                // stream: Your querysnapshot stream.
                builder:
                    (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
                  return StreamBuilder<List<User>>(
                    key: ValueKey(snapshot.data),
                    initialData: snapshot.data,
                    stream: _stream,
                    builder:
                        (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
                          print(snapshot.data);
                      return ListView.builder(
                        shrinkWrap: true,
                        itemCount: snapshot.data.length,
                        itemBuilder: (BuildContext context, int index) {
                          return Text(snapshot.data[index].name);
                        },
                      );
                    },
                  );
                },
              )
            ],
          ),
        );
      }
    }
    
    StreamController<List<User>> _streamController = StreamController<List<User>>();
    Stream<List<User>> get _stream => _streamController.stream;
    _filter(String searchQuery) {
      List<User> _filteredList = _dataFromQuerySnapShot
          .where((User user) => user.name.toLowerCase().contains(searchQuery.toLowerCase()))
          .toList();
      _streamController.sink.add(_filteredList);
    }
    
    List<User> _dataFromQuerySnapShot = <User>[
      // every user has same enviornment because you are applying
      // such filter on your query snapshot.
      // same is the reason why every one is approved user.
      User('Zain Emery', 'some_enviornment', true),
      User('Dev Franco', 'some_enviornment', true),
      User('Emilia ONeill', 'some_enviornment', true),
      User('Zohaib Dale', 'some_enviornment', true),
      User('May Mcdougall', 'some_enviornment', true),
      User('LaylaRose Mitchell', 'some_enviornment', true),
      User('Beck Beasley', 'some_enviornment', true),
      User('Sadiyah Walker', 'some_enviornment', true),
      User('Mae Malone', 'some_enviornment', true),
      User('Judy Mccoy', 'some_enviornment', true),
    ];
    
    class User {
      final String name;
      final String environment;
      final bool approved;
    
      const User(this.name, this.environment, this.approved);
    
      @override
      String toString() {
        return 'name: $name environment: $environment approved: $approved';
      }
    }