首页 > 解决方案 > 如何协调 StreamBuilder 的授权和 FutureBuilder 的数据来自 api 和 listview 过滤

问题描述

首先,我是新手,我可以犯一些基本的错误。

初步假设:

我的主要课程:

  class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => GoogleSignInProvider(),
      child: MaterialApp(
        home: child: MyList()
    );
  }

还有 MyList 类(我在这个例子中简化了这个类):

class MyList extends StatefulWidget {
  MyList();

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

class _MyListState extends State<MyList> {
  TextEditingController controller = new TextEditingController();
  late Future<List<Details>> _detailsList;

  Future<List<Details>> getDetailsList() async {
    Map<String, String> headers = {};
    if (FirebaseAuth.instance.currentUser != null){
      headers["tokenID"] = FirebaseAuth.instance.currentUser?.uid;
    }

    final response = await http
        .get(Uri.parse('http://hostname/api/v1/details/list'), headers: headers);
    final responseJson = json.decode(response.body);

    List<Details> details = [];
    for (Map details in responseJson["details"]) {
      details.add(Details.fromJson(details));
    }
    return details;
  }

  @override
  void initState() {
    super.initState();
    _details = getDetailsList();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        stream: FirebaseAuth.instance.authStateChanges(),
        builder: (context, snapshot) {
          return FutureBuilder(
              future: _details,
              builder: (context, AsyncSnapshot<List<Details>> snapshot) {
                return new Scaffold(
                  body: new Column(
                    children: <Widget>[
                      new TextField(
                        controller: controller,
                        decoration: new InputDecoration(
                            hintText: 'Search', border: InputBorder.none),
                        onChanged: onSearchTextChanged,
                      ),
                      new Expanded(
                        child: _searchResult.length != 0 ||
                            controller.text.isNotEmpty
                            ? new ListView.builder(
                          padding: const EdgeInsets.only(top: 10, bottom: 50),
                          itemCount: _searchResult.length,
                          itemBuilder: (context, i) {
                            return new ListItem(_searchResult[i]);
                          },
                        )
                            : new ListView.builder(
                          padding: const EdgeInsets.only(bottom: 40),
                          itemCount: snapshot.data!.length,
                          itemBuilder: (context, index) {
                            return new ListItem(snapshot.data![index]);
                          },
                        ),
                      ),
                    ],
                  ),
                );
              }
          );
        }

    );
  }

  List<Details> _searchResult = [];
  onSearchTextChanged(String text) async {
    _searchResult.clear();
    if (text.isEmpty) {
      setState(() {});
      return;
    }

    _details.forEach((userDetail) {
      if (userDetail.name.toLowerCase().contains(text.toLowerCase()))
        _searchResult.add(userDetail);
    });

    setState(() {});
  }

在这种情况下,过滤/搜索工作正常。但是每个键入的字符都会执行 setState 和重新启动小部件,这会导致为每种类型发送 API 请求 - 我不希望这种行为。

另一方面,我想在用户登录或注销时获得一个新的 API 请求。

我怎样才能做到这一点?

标签: firebaseflutterfirebase-authentication

解决方案


回答问题的第一部分:为了防止每个键入字符的 setState,您需要为您的 TextField 实现去抖动和节流。肯定有很多教程。

对第二部分的回答:获取firebase auth流,在每次需要时根据auth对象在请求的流上执行asyncMap。asyncMap 之后的流将返回 List,并且会在每次 auth 流更新时更新。


推荐阅读