添加数据时,Firebase 列表视图未实时显示

Posted

技术标签:

【中文标题】添加数据时,Firebase 列表视图未实时显示【英文标题】:Firebase List view is not displayed in real-time while adding data 【发布时间】:2021-08-13 03:07:54 【问题描述】:

从电话中选择联系人后,我将联系人信息存储在 Firebase 实时数据库中。 当我添加使用 ListView 和 DataSnapshot 显示的数据时,数据不会实时刷新,直到我重新启动 Activity。 ListView 加载并显示所有数据没有任何问题。我该如何解决这个问题?

我的屏幕是这样的:

屏幕代码:

import 'package:epicare/CaregiverClass.dart';
import 'package:epicare/Homepage.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_switch/flutter_switch.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:contact_picker/contact_picker.dart';

//Check contacts permission
Future<PermissionStatus> _getPermission() async 
  final PermissionStatus permission = await Permission.contacts.status;
  if (permission != PermissionStatus.granted &&
      permission != PermissionStatus.denied) 
    final Map<Permission, PermissionStatus> permissionStatus =
        await [Permission.contacts].request();
    return permissionStatus[Permission.contacts] ??
        PermissionStatus.undetermined;
   else 
    return permission;
  


class CaregiverScreen extends StatefulWidget 
  @override
  _CaregiverScreenState createState() => _CaregiverScreenState();


class _CaregiverScreenState extends State<CaregiverScreen> 
  //Firebase
  FirebaseAuth firebaseAuth = FirebaseAuth.instance;
  final ref = FirebaseDatabase.instance.reference();
  User cuser = FirebaseAuth.instance.currentUser;
  final fb = FirebaseDatabase.instance.reference().child("User_data");
  List <CaregiverList> list = List();

  // Adding data into Firebase
  void addData(String name, String number) 
    print("Saving data to firebase");
    ref.child('User_data').child(cuser.uid).child("caregivers").push().set('Caregiver_Name': name, 'Caregiver_Number': number);
  

  // Get location
  Position _currentPosition;
  String _currentAddress;
  final Geolocator geolocator = Geolocator()..forceandroidLocationManager;
  @override
  void initState() 
    // Firebase
    fb.child(cuser.uid).child("caregivers").once().then((DataSnapshot snapshot)
    
      var data = snapshot.value;
      list.clear();
      data.forEach((key,value)
        print(value['Caregiver_Name']);
        CaregiverList contact_list = new CaregiverList(
          name: value['Caregiver_Name'],
          phone_number: value['Caregiver_Number'],
          isActive: true,
          key: key,
        );
        list.add(contact_list);
        );
      setState(() 

      );
    
    );
    super.initState();
  


  
  final ContactPicker _contactPicker = new ContactPicker();

  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  
  @override
  Widget build(BuildContext context) 
    //Size size = MediaQuery.of(context).size;
    return Scaffold(
      key: _scaffoldKey,
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: const Color(0xffE5E0A1),
        elevation: 0,
        centerTitle: true,
        title: Text(
          "Add Caregiver",
          style: TextStyle(
            fontSize: 15.0,
            color: Colors.black,
            fontFamily: 'Montserrat',
            fontWeight: FontWeight.normal,
          ),
        ),
        leading: IconButton(
          icon: Icon(
            Icons.arrow_back,
            color: Colors.black,
          ),
          onPressed: () 
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) 
                  return Homepage();
                ,
              ),
            );
          ,
        ),
      ),
      body: SingleChildScrollView(
        child: Container(
          padding: EdgeInsets.symmetric(horizontal: 10, vertical: 40),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  MaterialButton(
                    onPressed: () async 
                      final PermissionStatus permissionStatus =
                          await _getPermission();
                      if (permissionStatus == PermissionStatus.granted) 
                        //We can now access our contacts here
                        Contact contact = await _contactPicker.selectContact();
                        print("Contact name: $contact.fullName");
                        print("Contact number: $contact.phoneNumber");
                        List<String> num =
                            contact.phoneNumber.toString().split('(Work)');
                        print(num);
                        print(num[0]);
                        final pattern = RegExp('\\s+');
                        num[0].replaceAll(pattern, '');
                        num[0].replaceAll(new RegExp('-'),'');
                        addData(contact.fullName.toString(),num[0]);
                   
                       else 
                        //If permissions have been denied show standard cupertino alert dialog
                        showDialog(
                            context: context,
                            builder: (BuildContext context) =>
                                CupertinoAlertDialog(
                                  title: Text('Permissions error'),
                                  content: Text('Please enable contacts access '
                                      'permission in system settings'),
                                  actions: <Widget>[
                                    CupertinoDialogAction(
                                      child: Text('OK'),
                                      onPressed: () =>
                                          Navigator.of(context).pop(),
                                    )
                                  ],
                                ));
                      
                    ,
                    color: const Color(0xffd4d411),
                    textColor: Colors.white,
                    child: Icon(
                      Icons.add,
                      size: 32,
                    ),
                    padding: EdgeInsets.all(3),
                    shape: CircleBorder(),
                  ),
                  Text(
                    'Add a Caregiver',
                    style: TextStyle(
                        fontFamily: 'Montserrat',
                        fontSize: 13,
                        color: const Color(0xff000000),
                        height: 1.5384615384615385,
                        fontWeight: FontWeight.w600),
                    textHeightBehavior:
                        TextHeightBehavior(applyHeightToFirstAscent: false),
                    textAlign: TextAlign.left,
                  )
                ],
              ),
              list.length == 0 ? Center(child: Text("Please add Caregivers"))
              : ListView.separated(
                  physics: NeverScrollableScrollPhysics(),
                  shrinkWrap: true,
                  primary: false,
                  itemBuilder: (context, index) 
                    return _caregiverWidget(list[index].name,list[index].phone_number,list[index].isActive,list[index].key
                       
                    );
                  ,
                  separatorBuilder: (_, __) => Container(),
                  itemCount: list.length),
              // _contact == null ? Container() : CaregiversList(_contact.fullName),
            ],
          ),
        ),
      ),
    );
  

  Widget _caregiverWidget( String name, String phone_number, bool isActive, String key
     
  ) 
    
    print(name);
    var c = name.split(' ');
    print(c[0]);
    var caregiver = c[0];
    var output = getInitials(string: caregiver, limitTo: 1);
    print(output);
    return GestureDetector(
      onLongPress: (),
      onTap: (),
      child: Card(
        elevation: 0,
        child: Container(
          padding: EdgeInsets.symmetric(vertical: 10, horizontal: 27),
          child: Row(
            //crossAxisAlignment: CrossAxisAlignment.center,
            //mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              CircleAvatar(
                radius: 24,
                backgroundColor: const Color(0xffd4d411),
                child: CircleAvatar(
                  radius: 22,
                  backgroundColor: Colors.white,
                  child: Text(
                    output,
                    style: TextStyle(
                      fontFamily: 'Segoe UI',
                      fontSize: 20,
                      color: const Color(0xff000000),
                    ),
                  ),
                ),
              ),
              SizedBox(width: 19),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    caregiver,
                    style: TextStyle(
                        fontFamily: 'Montserrat',
                        fontSize: 13,
                        color: const Color(0xff000000),
                        height: 1.5384615384615385,
                        fontWeight: FontWeight.w600),
                    textHeightBehavior:
                        TextHeightBehavior(applyHeightToFirstAscent: false),
                    textAlign: TextAlign.left,
                  ),
                  isActive
                      ? Text(
                          "Activated",
                          style: TextStyle(
                              fontFamily: 'Montserrat',
                              fontSize: 10,
                              color: const Color(0x80232425),
                              fontWeight: FontWeight.w500),
                          textAlign: TextAlign.left,
                        )
                      : Text(
                          "Disabled",
                          style: TextStyle(
                              fontFamily: 'Montserrat',
                              fontSize: 10,
                              color: const Color(0x80232425),
                              fontWeight: FontWeight.w500),
                          textAlign: TextAlign.left,
                        ),
                ],
              ),
              SizedBox(width: 143),
              FlutterSwitch(
                width: 40.0,
                height: 20.0,
                value: isActive,
                toggleSize: 15,
                borderRadius: 40.0,
                padding: 2.0,
                showOnOff: false,
                activeColor: const Color(0xffd4d411),
                activeToggleColor: Colors.white,
                inactiveColor: const Color(0xffDDDBAF),
                inactiveToggleColor: Colors.white,
                onToggle: (value) 
                  print(value);
                  setState(() 
                    isActive = value;
                    // _careList.removeAt(index);
                    // _careList.insert(index, _caregiver);
                  );
                ,
              ),
            ],
          ),
        ),
      ),
    );
  

  String getInitials(String string, int limitTo) 
    var buffer = StringBuffer();
    var split = string.split(' ');
    for (var i = 0; i < (limitTo ?? split.length); i++) 
      buffer.write(split[i][0]);
    
    return buffer.toString();
  

【问题讨论】:

这里需要使用StreamBuilder进行实时更新。 你能告诉我如何使用它吗? 【参考方案1】:

问题在于这部分:

fb.child(cuser.uid).child("caregivers").once()..

只获得一次价值。

要查看实时更新,您应该使用listener,如下所示:

CollectionReference reference = Firestore.instance.collection('caregivers');
reference.snapshots().listen((querySnapshot) 
  querySnapshot.documentChanges.forEach((change) 
    // Do something with change
  );
);

StreamBuilder 喜欢这里:

StreamBuilder<QuerySnapshot>(
  stream: Firestore.instance.collection('caregivers').snapshots(),
  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
    if (!snapshot.hasData) return new Text('Loading...');
    return new ListView(
      children: snapshot.data.documents.map((DocumentSnapshot document) 
        return new ListTile(
          title: new Text(document['name']),
        );
      ).toList(),
    );
  ,
);

对于 RTDB,StreamBuilder 如下所示:

StreamBuilder(
      stream: _database.reference().child('caregivers').onValue,
      builder: (context, event) 
        if (event.hasData &&
            !event.hasError &&
            event.data.snapshot.value != null) 
          DataSnapshot snapshot = event.data.snapshot;
       

)

听者会是这样的:

_database.reference().child('caregivers').onValue.listen((event)

)

onValue 每次都会为您提供整个列表,因此您需要在再次添加每个项目之前清空它:

 var data = event.snapshot.value;
 list= List(); //Clear the list here

 data.forEach((key, value) 
          print(value['Caregiver_Name']);
          
          CaregiverList contact_list = new CaregiverList(
            name: value['Caregiver_Name'],
            phone_number: value['Caregiver_Number'],
            isActive: true,
            key: key,
          );
          list.add(contact_list);
        );

【讨论】:

我使用的是实时数据库而不是firestore 你能解释一下使用实时数据库吗? 我应该使用 Stream Builder 并删除 Listview.builder,对不起,我还是 Flutter 的新手,这就是为什么我很难理解我应该在哪里更改代码 我应该在监听器里面写什么? 我会推荐使用 StreamBuilder。替换这部分代码list.length == 0 ? Center(child: Text("Please add Caregivers")) : ListView.separated( physics: NeverScrollableScrollPhysics(), shrinkWrap: true, primary: false, itemBuilder: (context, index) return _caregiverWidget(list[index].name,list[index].phone_number,list[index].isActive,list[index].key...

以上是关于添加数据时,Firebase 列表视图未实时显示的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Firebase 实时数据库的数据在控制台中显示为空?

Firebase Android ListView 未显示

Firebase Android ListView 未显示

FireBase 实时数据库:回收站视图未填充

Firebase 实时数据库未更新 // 数据未添加到 Firebase 实时数据库

将 Firebase 实时数据库与 SwiftUI 列表一起使用 - 清除列表