I am trying to list some data from a firestore database. While pulling the data I got this exception:
======== Exception caught by provider ==============================================================
The following assertion was thrown:
An exception was throw by _MapStream<QuerySnapshot<Map<String, dynamic>>, List<UserModel>?> listened by
StreamProvider<List<UserModel>?>, but no `catchError` was provided.
Exception:
type 'int' is not a subtype of type 'double?'
====================================================================================================
These are my database functions:
// users list from snapshot
List<UserModel>? _usersListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return UserModel(
uid: doc.get('uid'),
username: doc.get('username') ?? '',
email: doc.get('email') ?? '',
type: doc.get('type') ?? '',
balance: doc.get('balance'),
usertag: doc.get('usertag') ?? '',
connected: doc.get('connected') ?? '',
);
}).toList();
}
// get users stream
Stream<List<UserModel>?> get usersList {
User? user = _auth.currentUser;
UserModel? userModel = _userFromFirebaseUser(user);
final CollectionReference userRef = FirebaseFirestore.instance.collection("users");
final List? connectedList = userModel?.connected;
final Query inList = userRef.where('uid', whereIn: connectedList);
return inList.snapshots().map(_usersListFromSnapshot);
}
This is my user.dart:
class UserModel {
String? uid;
String? username;
String? email;
String? type;
double? balance;
String? usertag;
List<dynamic>? connected;
UserModel({this.uid, this.username, this.email, this.type, this.balance, this.usertag, this.connected});
// receive data from the server
factory UserModel.fromMap(map) {
return UserModel(
uid: map['uid'],
username: map['username'],
email: map['email'],
type: map['type'],
balance: map['balance'],
usertag: map['usertag'],
connected: map['connected']
);
}
// send data to the server
Map<String, dynamic> toMap() {
return {
'uid': uid,
'username': username,
'email': email,
'type': type,
'balance': balance,
'usertag': usertag,
'connected': connected,
};
}
}
This is my Listbuilder:
class ConnectedScreen extends StatefulWidget {
const ConnectedScreen({Key? key}) : super(key: key);
@override
_ConnectedScreenState createState() => _ConnectedScreenState();
}
class _ConnectedScreenState extends State<ConnectedScreen> {
User? user = FirebaseAuth.instance.currentUser;
UserModel userModel = UserModel();
@override
void initState() {
super.initState();
FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get()
.then((value) {
userModel = UserModel.fromMap(value.data());
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: customAppBar("Connect Users", "back", ""),
body: Padding(
padding: EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: StreamProvider<List<UserModel>?>.value(
value: AuthService().usersList,
initialData: [],
child: ConnectedListBuilder(),
),
),
),
);
}
}
class ConnectedListBuilder extends StatefulWidget {
const ConnectedListBuilder({Key? key}) : super(key: key);
@override
_ConnectedListBuilderState createState() => _ConnectedListBuilderState();
}
class _ConnectedListBuilderState extends State<ConnectedListBuilder> {
@override
Widget build(BuildContext context) {
final connectedUsers = Provider.of<List<UserModel>?>(context);
if (connectedUsers!.isNotEmpty) {
return Padding(
padding: EdgeInsets.only(top: 8.0),
child: ListView.builder(
itemCount: connectedUsers.length,
itemBuilder: (context, index) {
return ConnectedUserTile(userModel: connectedUsers[index]);
},
),
);
} else if (connectedUsers.isEmpty) {
return Text("List is empty");
}
return Center(
child: CircularProgressIndicator(),
);
}
}
class ConnectedUserTile extends StatelessWidget {
final UserModel? userModel;
const ConnectedUserTile({Key? key, required this.userModel}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(8.0),
child: Card(
margin: EdgeInsets.fromLTRB(20, 6, 20, 0),
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage('https://picsum.photos/100'),
radius: 25,
),
title: Text(
"${userModel!.username}",
style: TextStyle(
color: Colors.black54,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
subtitle: Text(
"${userModel!.usertag}",
style: TextStyle(
color: Colors.black38,
fontSize: 14,
),
),
),
),
);
}
}
What's interesting is that this exact code works for another ListView, so I don't think it's an error with the database functions, but I´m out of ideas how to try fixing it. I also didn't find much helpful information on the internet about this, so I would be very thankful for any help.
CodePudding user response:
Looks like the issue is in parsing the balance field. In the instances that it works, the balance was probably formatted as a double, e.g. "1.23" and so when it's converted to a map, it's converted as a double
, which is a subtype of double?
.
When it's not working, it's most likely dealing with an integer balance, like "25" - The value stored in the map will then have a type of int
, which is not a subtype of double?
, because int
is not a subtype of double
.
To fix this, you can simply ensure that you're always dealing with a double, by calling toDouble()
on the value.
// receive data from the server
factory UserModel.fromMap(map) {
return UserModel(
uid: map['uid'],
username: map['username'],
email: map['email'],
type: map['type'],
balance: map['balance'].toDouble(), // <- Change this line
usertag: map['usertag'],
connected: map['connected']
);
}