3

在我的 Flutter 应用程序中,我有一个包含所有用户的屏幕。用户列表由 a 生成StreamBuilder,它从 Cloud Firestore 获取数据并在 a 中显示用户ListView。为了改进功能,我希望能够使用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),
      ),
    );
  }
}
class StreamUsersList extends StatelessWidget {
  final String environmentName;
  final String approved;
  StreamUsersList(this.environmentName, this.approved);
  static String dropdownSelected2 = '';

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
        stream: Firestore.instance
            .collection('users')
            .where('environment', isEqualTo: environmentName)
            .where('approved', isEqualTo: approved)
            .snapshots(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(
              child: CircularProgressIndicator(
                backgroundColor: Colors.lightBlueAccent,
              ),
            );
          } else if (snapshot.connectionState == ConnectionState.done &&
              !snapshot.hasData) {
            return Center(
              child: Text('No users found'),
            );
          } 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];
                  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: user.data['unit'],
                            weight: FontWeight.bold,
                            subTitle:
                                '${user.data['name']} - ${user.data['login']}',
                        ),
                      ],
                    ),
                  );
                });
          } else {
            return Center(
              child: Text('Something is wrong'),
            );
          }
        });
  }
}

已编辑

我设法以更简单的方式实现了搜索功能,而无需更改我的大部分代码。对于其他初学者,我已包含以下代码:

在我的里面我_UsersScreenState添加了String searchResult = '';我的其他变量。然后我将 的 更改onChangedTextField

onChanged: (String value) {
                        setState(() {
                          searchResult = value;
                        });
                      },```

我将它传递给StreamUsersList并在初始化中添加它。在ListView.Builder我添加了一个 if 语句(snapshot.data.documents[index].data['login'].contains(searchResult))。请参阅下面的我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'),
            );
          }
4

1 回答 1

4

您可以采取以下方法。

  1. 您会在快照中收到完整的数据。
  2. 具有小部件的层次结构,例如: StreamBuilder( ValueListenableBuilder( ListView.Builder ) )
  3. 创建 ValueNotifier 并将其提供给 ValueListenable 构建器。
  4. 使用搜索视图更改 ValueNotifier 的值。
  5. 当 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';
  }
}
于 2019-12-18T18:25:30.270 回答