Flutter error : 'isDocument()': is not true - flutter

I am making a chat detail page using flutter. However, I am getting this error in the stream part. I don't have much experience with flutter. I couldn't solve it for 2 days.
Error :
Exception has occurred.
_AssertionError ('package:cloud_firestore_platform_interface/src/internal/pointer.dart': Failed assertion: line 56 pos 12: 'isDocument()': is not true.)
Code :
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_chat_bubble/bubble_type.dart';
import 'package:flutter_chat_bubble/chat_bubble.dart';
import 'package:flutter_chat_bubble/clippers/chat_bubble_clipper_6.dart';
class Chat extends StatefulWidget {
Chat({super.key, required this.friendUid, required this.friendName});
final String friendUid;
final String friendName;
#override
State<Chat> createState() => _ChatState(friendUid, friendName);
}
class _ChatState extends State<Chat> {
CollectionReference chats = FirebaseFirestore.instance.collection("chats");
final friendUid;
final friendName;
final currentUserUid = FirebaseAuth.instance.currentUser!.uid;
var chatDocId;
var _controller = TextEditingController();
_ChatState(this.friendName, this.friendUid);
#override
void initState() {
chats
.where("users", isEqualTo: {friendUid: null, currentUserUid: null})
.limit(1)
.get()
.then(
(QuerySnapshot snapshotx) {
if (snapshotx.docs.isNotEmpty) {
chatDocId = snapshotx.docs.single.id;
} else {
chats.add({
"users": {currentUserUid: null, friendUid: null},
}).then((value) {
chatDocId = value;
});
}
},
)
.catchError(() {});
super.initState();
}
void sendMessage(String msg) {
if (msg == "") return;
chats.doc(chatDocId.toString()).collection("messages").add({
"createdOn": FieldValue.serverTimestamp(),
"uid": currentUserUid,
"msg": msg
}).then((value) {
_controller.text = "";
});
}
bool isSender(String friend) {
return friend == currentUserUid;
}
Alignment getAlignment(String friend) {
if (friend == currentUserUid) {
return Alignment.topRight;
}
return Alignment.topLeft;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("chat")),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: chats
.doc(chatDocId.toString())
.collection("messages") ==> the error part
.orderBy("createdOn", descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text("Error"),
);
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasData) {
return ListView(
reverse: true,
children: snapshot.data!.docs
.map((DocumentSnapshot documentSnapshot) {
Map<String, dynamic> data = documentSnapshot
.data() as Map<String, dynamic>;
return ChatBubble(
clipper: ChatBubbleClipper6(
nipSize: 0,
radius: 0,
type: isSender(data["uid"].toString())
? BubbleType.sendBubble
: BubbleType.receiverBubble),
alignment:
getAlignment(data["uid"].toString()),
margin: EdgeInsets.only(top: 20),
backGroundColor:
isSender(data["uid"].toString())
? Color(0xFF08C187)
: Color(0xffE7E7ED),
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context)
.size
.width *
0.7),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Text(
data["msg"],
style: TextStyle(
color: isSender(
data["uid"]
.toString())
? Colors.white
: Colors.black),
maxLines: 100,
overflow:
TextOverflow.ellipsis,
)
],
)
],
)),
);
}).toList());
}
return Container(
width: 300,
height: 300,
color: Colors.grey,
);
})),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: TextField(
controller: _controller,
)),
IconButton(
onPressed: () {
sendMessage(_controller.text);
},
icon: Icon(Icons.send))
],
)
],
),
));
}
}
Can you help me. Thank you.

Related

Why setState() is calling reloading listview upon item selected, how to stop it ? in flutter App

My app reads data from PostgreSQL and displays on the screen in a listview. While selecting an item from listview app is getting refreshed and items are appending to existing list. My intention is to read data only once from the DB, display in list view, select single/multiple and proceed with processing. Any suggestions would be appriciated.
import 'package:e2/Models/MasterPositions.dart';
import 'package:flutter/material.dart';
import 'package:e2/Models/model_positions.dart';
class MasterControl extends StatefulWidget {
const MasterControl({super.key});
#override
State<MasterControl> createState() => _MasterControlState();
}
class _MasterControlState extends State<MasterControl> {
List<MasterPositions> selectedContacts = [];
List<MasterPositions> fetchedPositions = [];
List<MasterPositions> positions = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Master Control"),
centerTitle: true,
automaticallyImplyLeading: false,
),
body: SafeArea(
child: Column(
children: [
Expanded(
child: FutureBuilder<List<dynamic>>(
future: ModelsPositions().fetchPositionsData(),
builder: (context, snapshot) {
List<dynamic> positionsRaw = snapshot.data ?? [];
for (var pos in positionsRaw) {
positions.add(MasterPositions(
custID: pos[0],
custName: pos[1],
mtm: double.tryParse(pos[2]) ?? 0.0,
availableCash: double.tryParse(pos[3]) ?? 0.0,
));
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return const Center(
child: Text(
'Error while loading Master Positions screen'));
} else {
return buildPositions(positions);
}
}
},
)),
],
),
),
);
}
Widget buildPositions(List<dynamic> positions) {
return ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: positions.length,
itemBuilder: (context, index) {
final pos = positions[index];
final custID = pos.custID;
final custName = pos.custName;
final mtm = pos.mtm;
final availableCash = pos.availableCash;
final isSelected = pos.isSelected;
return ListTile(
horizontalTitleGap: -5,
title: Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(custID),
const SizedBox(height: 5),
Text(custName)
],
),
),
Flexible(
fit: FlexFit.tight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('MTM : $mtm',
softWrap: false,
style: const TextStyle(fontFamily: 'Roboto')),
const SizedBox(height: 10),
Text(
'Available : $availableCash',
softWrap: false,
),
const SizedBox(
height: 10,
),
],
),
),
],
),
),
leading: isSelected
? Icon(
Icons.check_circle,
color: Colors.green[700],
)
: const Icon(
Icons.check_circle_outline,
color: Colors.grey,
),
onTap: () {
setState(() {
positions[index].isSelected = !positions[index].isSelected;
if (positions[index].isSelected == true) {
selectedContacts.add(MasterPositions(
custID: custID,
custName: custName,
mtm: mtm,
availableCash: availableCash));
} else if (positions[index].isSelected == false) {
selectedContacts.removeWhere(
(element) => element.custID == positions[index].custID);
}
});
},
);
});
}
}
I often use something like this:
if (snapshot.hasData) {
if (snapshot.data!.isNotEmpty) {
List<dynamic> positionsRaw = snapshot.data ?? [];
for (var pos in positionsRaw) {
positions.add(MasterPositions(
custID: pos[0],
custName: pos[1],
mtm: double.tryParse(pos[2]) ?? 0.0,
availableCash: double.tryParse(pos[3]) ?? 0.0,
));
return buildPositions(positions);
} else {
// return something
}
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
And so you can guarantee that your widget will be built with data, and only be rebuilt when your new data it's loaded

Flutter : Auto refresh to update the data

I am developing a cart page which contains + and - buttons, on pressing it , the value in the backend changes, but it doesn't automatically change in the frontend.
Cartpage.dart
class CartUI extends StatefulWidget {
const CartUI({Key? key}) : super(key: key);
#override
State<CartUI> createState() => _CartUIState();
}
class _CartUIState extends State<CartUI> {
#override
Widget build(BuildContext context) {
final user = Provider.of<Userr?>(context, listen: false);
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(children: [
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Lottie.asset('assets/animations/delivery.json'),
);
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (BuildContext context, int index) {
final DocumentSnapshot documentSnapshot =
snapshot.data!.docs[index];
return Container(
height: 120,
width: 300,
child: Row(
children: [
Column(
children: [
SizedBox(
width: 200,
child: Text(
documentSnapshot['name'],
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Text(
documentSnapshot['quantity'].toString(),
style: const TextStyle(
fontSize: 15,
),
),
Text(
'Rs.${documentSnapshot['price'].toString()}',
style: const TextStyle(
color: Colors.black87,
fontSize: 15,
),
),
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
const SizedBox(
width: 40,
),
ElevatedButton(
onPressed: () {
if (documentSnapshot['value'] != 0.0) {
setState(() {
String id = documentSnapshot['docid'];
final user = Provider.of<Userr?>(context, listen: false);
var postDocRef = FirebaseFirestore.instance.collection('myOrders').doc(user?.uid).collection('items').doc();
Provider.of<Calculations>(context, listen: false).updatecartdata(
context,
{
'value': documentSnapshot['value'] - 0.5,
'price': documentSnapshot['price'] - (documentSnapshot['ogprice'] / 2),
},id
);
}
);
}
if (documentSnapshot['value'] ==
0.5) {
String id =
documentSnapshot['docid'];
Provider.of<ManageData>(
context,
listen: false)
.deleteData(context, id);
}
},
child: const Text('-'),
),
const SizedBox(width: 20),
Text(documentSnapshot['value']
.toString()),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
String id =
documentSnapshot['docid'];
final user =
Provider.of<Userr?>(context,
listen: false);
var postDocRef =
FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.doc();
Provider.of<Calculations>(context, listen: false).updatecartdata(
context,
{
'value': documentSnapshot['value'] + 0.5,
'price': documentSnapshot['price'] + (documentSnapshot['ogprice'] / 2),
},id
);
},
child: const Text('+'),
),
]),
],
),
],
),
);
});
}
},
),
),
_BillDetailView(),
]),
],
),
),
);
}
}
class _BillDetailView extends StatelessWidget {
#override
Widget build(BuildContext context) {
final textStyle =
Theme
.of(context)
.textTheme
.bodyText1!
.copyWith(fontSize: 16.0);
return Container(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Bill Details',
style:
Theme
.of(context)
.textTheme
.headline6!
.copyWith(fontSize: 17.0),
),
SizedBox(
height: 5,
),
FutureBuilder(
future: Provider.of<Calculations>(context, listen: false)
.getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${snapshot.data}', style: textStyle),
],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),
],
),
);
}
}
Calculation.dart
Future<dynamic> getTotalCost(BuildContext context) async {
final user = Provider.of<Userr?>(context, listen: false);
double totalCost = 0.0;
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.get();
for (var doc in snapshot.docs) {
totalCost += doc["price"];
}
print(totalCost.toString());
return totalCost.toString();
}
The value in the front end changes but last updated(Added or subtracted) value is not reflecting.After hot reload it changes, but not automatically.
How to change this code to automatically update the item total in the front end.
You need to use StreamBuilder instead of FutureBuilder to get live updates.
Changes your Calculation.dart to:
Stream<dynamic> getTotalCost(BuildContext context) async {
final user = Provider.of<Userr?>(context, listen: false);
double totalCost = 0.0;
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.snapshots();
for (var doc in snapshot.docs) {
totalCost += doc["price"];
}
print(totalCost.toString());
return totalCost.toString();
}
And change FutureBuilder to StreamBuilder:
StreamBuilder(
stream: Provider.of<Calculations>(context, listen: false)
.getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${snapshot.data}', style: textStyle),
],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),

Flutter text message is not being displayed on the foreground but is visible on cloud firestore

I have been working on the chat functionality of an app. However, I have found that the message is been saved on the backend but does not display on the screen. Any help will be beneficial. I would like to know where I went wrong and see if the chat functionality can function properly. I would like for the sender of the text message to see the message and have the ability to communicate with the receiver of the message seemlessly. Below is the code snippet:
class MessageCenter extends StatefulWidget {
final String garageUid;
final String garageName;
const MessageCenter(
{super.key, required this.garageUid, required this.garageName});
#override
State<MessageCenter> createState() =>
_MessageCenterState(garageName, garageUid);
}
class _MessageCenterState extends State<MessageCenter> {
CollectionReference chats = FirebaseFirestore.instance.collection('chats');
final String garageUid;
final String garageName;
final currentUserId = FirebaseAuth.instance.currentUser!.uid;
var chatDocId;
final _textController = TextEditingController();
_MessageCenterState(this.garageUid, this.garageName);
#override
void initState() {
chats
.where('users', isEqualTo: {garageUid: null, currentUserId: null})
.limit(1)
.get()
.then(
(QuerySnapshot querySnapshot) {
if (querySnapshot.docs.isNotEmpty) {
chatDocId = querySnapshot.docs.single.id;
} else {
chats.add({
'users': {currentUserId: null, garageUid: null}
}).then((value) => {chatDocId = value});
}
},
)
.catchError((error) {});
super.initState();
}
void sendMessage(String msg) {
if (msg == '') return;
chats.doc(chatDocId).collection('messages').add({
'createdOn': FieldValue.serverTimestamp(),
'uid': currentUserId,
'msg': msg
}).then((value) {
_textController.text = '';
});
}
bool isSender(String user) {
return user == currentUserId;
}
Alignment getAlignment(user) {
if (user == currentUserId) {
return Alignment.topRight;
}
return Alignment.topLeft;
}
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('chats')
.doc(chatDocId)
.collection('messages')
.orderBy('createdOn', descending: true)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('Something went wrong'),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
var data;
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
previousPageTitle: "back",
middle: Text(garageUid),
trailing: CupertinoButton(
child: const Icon(CupertinoIcons.phone),
onPressed: () {},
),
),
child: SafeArea(
child: Column(
children: [
Expanded(
child: ListView(
reverse: true,
children:
snapshot.data!.docs.map((DocumentSnapshot document) {
data = document.data()!;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ChatBubble(
clipper: ChatBubbleClipper6(
nipSize: 0,
radius: 0,
type: isSender(data['uid'].toString())
? BubbleType.sendBubble
: BubbleType.receiverBubble,
),
alignment: getAlignment(data['uid'].toString()),
margin: const EdgeInsets.only(top: 20),
backGroundColor: isSender(data['uid'].toString())
? const Color(0xFF08C187)
: const Color(0xffE7E7ED),
child: Container(
constraints: BoxConstraints(
maxWidth:
MediaQuery.of(context).size.width * 0.7,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
data['msg'],
style: TextStyle(
color:
isSender(data['uid'].toString())
? Colors.white
: Colors.black),
maxLines: 100,
overflow: TextOverflow.ellipsis,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
data['createdOn'] == null
? DateTime.now().toString()
: data['createdOn']
.toDate()
.toString(),
style: TextStyle(
fontSize: 10,
color:
isSender(data['uid'].toString())
? Colors.white
: Colors.black),
),
],
),
],
),
),
),
);
}).toList(),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: CupertinoTextField(
controller: _textController,
)),
CupertinoButton(
child: const Icon(Icons.send_sharp),
onPressed: () => sendMessage(_textController.text))
],
),
],
),
),
);
}
return Container();
},
);
}
}

Should i put CameraController to getxController?

I saw in the getx documents that GetxController is business logic class. When i try to seperate business logic in widget, i have an issue about some controllers such as CameraController, QRViewController,MediaPlayer,... Should i put all things in GetxController or keep this in widget?
qr_code.dart
import 'dart:developer';
import 'dart:io';
import 'package:clean_architecture_getx/controller/qr_code/qr_code_controller.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
class QrCodeScreen extends StatefulWidget {
const QrCodeScreen({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => _QrCodeScreenState();
}
class _QrCodeScreenState extends State<QrCodeScreen> {
final scanQrController = Get.put(ScanQrController());
QRViewController? qrViewController;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
#override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
qrViewController!.pauseCamera();
}
qrViewController!.resumeCamera();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(flex: 4, child: _buildQrView(context)),
Expanded(
flex: 1,
child: FittedBox(
fit: BoxFit.contain,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Obx(() {
if (scanQrController.result.value.code != null) {
return Text(
'Barcode Type: ${describeEnum(scanQrController.result
.value.format)} Data: ${scanQrController.result
.value.code}');
} else {
return const Text('Scan a code');
}
}),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await qrViewController?.toggleFlash();
setState(() {});
},
child: FutureBuilder(
future: qrViewController?.getFlashStatus(),
builder: (context, snapshot) {
return Text('Flash: ${snapshot.data}');
},
)),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await qrViewController?.flipCamera();
setState(() {});
},
child: FutureBuilder(
future: qrViewController?.getCameraInfo(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return Text(
'Camera facing ${describeEnum(
snapshot.data!)}');
} else {
return const Text('loading');
}
},
)),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await qrViewController?.pauseCamera();
},
child: const Text('pause',
style: TextStyle(fontSize: 20)),
),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await qrViewController?.resumeCamera();
},
child: const Text('resume',
style: TextStyle(fontSize: 20)),
),
)
],
),
],
),
),
)
],
),
);
}
Widget _buildQrView(BuildContext context) {
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
var scanArea = (MediaQuery
.of(context)
.size
.width < 400 ||
MediaQuery
.of(context)
.size
.height < 400)
? 150.0
: 300.0;
// To ensure the Scanner view is properly sizes after rotation
// we need to listen for Flutter SizeChanged notification and update controller
return QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
);
}
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.qrViewController = controller;
});
controller.scannedDataStream.listen((scanData) {
debugPrint('barcode: ${scanData.code}');
scanQrController.updateBarCode(scanData);
});
}
void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('no Permission')),
);
}
}
#override
void dispose() {
qrViewController?.dispose();
super.dispose();
}
}
qr_code_controller.dart
import 'package:get/get.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
class ScanQrController extends GetxController {
final result = Barcode(null, BarcodeFormat.unknown, null).obs;
void onQrViewCreated(QRViewController controller) {}
void updateBarCode(Barcode barcode) {
result.value = barcode;
}
}
Should i put QRViewController to ScanQrController (GetxController) or keep it in widget?
Put the UI in a view, and put the business logic inside a controller. Have a GetX Controller per feature. It would be more helpful if you could provide an example of your code.

Expected a value of type 'String', but got one of type 'Null' StreamBuilder<QuerySnapshot<Object?>>

I trying create a contact list to be displayed on my screen as a user edit of delete his details. I got this error and UI doesn't show anything. The error is : Expected a value of type 'String', but got one of type 'Null'
here is where error happens:
---dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
class contactlist extends StatefulWidget {
const contactlist({ Key? key }) : super(key: key);
#override
State<contactlist> createState() => _contactlistState();
}
class _contactlistState extends State<contactlist> {
final Stream<QuerySnapshot>_myUserContacts =
FirebaseFirestore.instance.collection('userContact').snapshots();
#override
Widget build(BuildContext context) {
TextEditingController _nameFieldcntroler = TextEditingController();
TextEditingController _phoneNumFieldcntroler = TextEditingController();
TextEditingController _EmailFieldcntroler = TextEditingController();
TextEditingController _AgeFieldcntroler = TextEditingController();
void _delete(docId){
FirebaseFirestore.instance
.collection("userContact")
.doc(docId)
.delete()
.then((value) => print("deleted"));
}
void _update(data){
var collection = FirebaseFirestore.instance.collection("userContact");
_nameFieldcntroler.text = data["names"];
_phoneNumFieldcntroler.text = data["phoneNumber"];
_EmailFieldcntroler.text = data["email"];
_AgeFieldcntroler.text = data["age"];
showDialog(context: context,
builder: (_) => AlertDialog(
title: Text("Update"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller:_nameFieldcntroler,
),
TextField(
controller:_phoneNumFieldcntroler,
),
TextField(
controller:_EmailFieldcntroler,
),
TextField(
controller:_AgeFieldcntroler,
),
TextButton(
onPressed: (){
collection.doc(data["doc_Id"])
.update({
"names": _nameFieldcntroler.text,
"phoneNumber": _phoneNumFieldcntroler.text,
"email": _EmailFieldcntroler.text,
"age": _AgeFieldcntroler.text,
});
Navigator.pop(context);
},
child: Text("Update")),
]),
)
);
}
return StreamBuilder(
stream: _myUserContacts,
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot<Object?>> snapshot){
if (snapshot.hasError){
return const Text("Something went wrong");
}
if(snapshot.connectionState == ConnectionState.waiting){
return const Center(child: CircularProgressIndicator());
}
if(snapshot.hasData){
return Row(
children: [
Expanded(
child: SizedBox(
height: (MediaQuery.of(context).size.height),
width: (MediaQuery.of(context).size.width),
child: ListView(
children: snapshot.data!.docs
.map((DocumentSnapshot documentSnapshot) {
Map<String, dynamic> data = documentSnapshot.data()! as Map<
String, dynamic>;
return Column(
children: [
Card(
child: Column(
children:[
ListTile(
title: Text(data['names']),
subtitle:Text(data['phoneNumber']),
),
ButtonTheme(
child: ButtonBar(
children:[
OutlineButton.icon(
onPressed:(){
_update(data);
},
icon: Icon(Icons.edit),
label: Text("Edit"),
),
OutlineButton.icon(
onPressed:(){
_delete(data["doc_Id"]);
},
icon: Icon(Icons.remove),
label: Text("Delete"),
)
],
),
),
],
)
)
],
);
}).toList(),
),
)
)
],
);
}else{
return(Text("No data"));
}
},
);
}
}
---dart
---dart
The following TypeErrorImpl was thrown building StreamBuilder<QuerySnapshot<Object?>>(dirty, dependencies: [MediaQuery], state: _StreamBuilderBaseState<QuerySnapshot<Object?>, AsyncSnapshot<QuerySnapshot<Object?>>>#1efa0):
Expected a value of type 'String', but got one of type 'Null'
The relevant error-causing widget was
StreamBuilder<QuerySnapshot<Object?>>
---dart