Flutter compare num with dynamic from Firebase - firebase

I want to compare the App's version manually through Firebase Firestore. In Firestore, I have a collection(system) with a document(update) with a field: newest_v_app = number 7. I want to access this field on a page in my app and want to compare it to a number. If the number in firestore is higher than the number in the app, I want a bool to set to true.
The Code I tried (to explain what I mean):
UpdatePage.dart
class UpdatePage extends StatefulWidget {
#override
_UpdatePageState createState() => _UpdatePageState();
}
class _UpdatePageState extends State<UpdatePage> {
bool update_available = false;
num current_version = 7;
dynamic newest_version_from_firebase = 7;
Future<dynamic> _getUpdateAvailable() async {
final DocumentReference document = FirebaseFirestore.instance.collection('system').doc('update');
print('Success GetUpdate 1');
await document.get().then<dynamic>((DocumentSnapshot snapshot) async {
setState(() {
newest_version_from_firebase = snapshot.data;
});
});
print('Success GetUpdate 2');
compareUpdate(context);
print('Success GetUpdate 3');
testUpdateComparer();
}
void compareUpdate(BuildContext context) {
if (newest_version_from_firebase > current_version) {
setState(() {
update_available = true;
});
} else {
setState(() {
update_available = false;
});
}
}
void testUpdateComparer() {
if (update_available == true) {
print('Success AvailableBool');
} else {
print('No AvailableBool');
}
}
#override
void initState() {
super.initState();
print('Success Init');
_getUpdateAvailable();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
return Scaffold(
);
}
}
Console & Error:
I/flutter (15814): Success Init
I/flutter (15814): Success GetUpdate 1
W/DynamiteModule(15814): Local module descriptor class for providerinstaller not found.
I/DynamiteModule(15814): Considering local module providerinstaller:0 and remote module providerinstaller:0
W/ProviderInstaller(15814): Failed to load providerinstaller module: No acceptable module found. Local version is 0 and remote version is 0.
I/flutter (15814): Success GetUpdate 2
I/flutter (15814): No AvailableBool
E/flutter (15814): [ERROR:flutter/lib/ui/ui_dart_state.cc(213)] Unhandled Exception: NoSuchMethodError: Closure call with mismatched arguments: function '>'
E/flutter (15814): Receiver: Closure: () => Map<String, dynamic>? from Function 'data':.
E/flutter (15814): Tried calling: >(7)
E/flutter (15814): Found: >() => Map<String, dynamic>?
E/flutter (15814): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:63:5)
E/flutter (15814): #1 _UpdatePageState.compareUpdate (package:myapp/UpdatePage/UpdatePage.dart:44:38)
E/flutter (15814): #2 _UpdatePageState._getUpdateAvailable (package:myapp/UpdatePage/UpdatePage.dart:38:5)
E/flutter (15814): <asynchronous suspension>
E/flutter (15814):
Flutter 2.3.0-17.0.pre.19 • channel master • https://github.com/flutter/flutter.git
Framework • revision bcf05f4587 (4 months ago) • 2021-05-23 02:19:02 -0400
Engine • revision 8cd4cf0a67
Tools • Dart 2.14.0 (build 2.14.0-143.0.dev)

The snapshot.data returns a Map<String, dynamic> from firestore and you're trying to assign it to the variable newest_version_from_firebase that you're trying to use as a number, which is not possible.
snaposhot.data returns a Document in which your have your version number probably stored as a key value pair, like: {"versionNumber": 7}
to access it your should do, for example:
newest_version_from_firebase = snapshot.data["versionNumber"] as int;

Solved it:
Changed num to int
late DocumentSnapshot snapshot;
int current_version = 7;
int newest_version_from_firebase = 7;
void getUpdateAvailable() async {
final data = await FirebaseFirestore.instance.collection("system").doc("update").get() as DocumentSnapshot;
snapshot = data as DocumentSnapshot;
setState(() {
newest_version_from_firebase = snapshot["newest_version"] as int;
});
print(newest_version_from_firebase.toString());
compareUpdate(context);
}

Related

Flutter auth firebase getx setstate() called after dispose

I'm trying to create a flutter app with getx. Particularly in the authentication section, I have the following error:
E/flutter ( 8992): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: setState() called after dispose(): _LogButtonState#3e8d6(lifecycle state: defunct, not mounted)
E/flutter ( 8992): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter ( 8992): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
E/flutter ( 8992): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
E/flutter ( 8992): #0 State.setState. (package:flutter/src/widgets/framework.dart:1085:9)
E/flutter ( 8992): #1 State.setState (package:flutter/src/widgets/framework.dart:1120:6)
E/flutter ( 8992): #2 _LogButtonState.build. (package:sneakychat/Screens/Authentication/login.dart:231:9)
E/flutter ( 8992):
E/flutter ( 8992):
The code for the authentication controller is the following:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.dart';
import '../Globals/globals.dart';
class AuthController extends GetxController {
final FirebaseAuth _auth = firebaseAuth;
late Rx<User?> _firebaseUser;
late Rx<GoogleSignInAccount?> _googleSignInAccount;
//UserModel get user => _firebaseUser.value;
late GoogleSignIn googleSignIn = GoogleSignIn();
//var a = googleSignIn.currentUser;
#override
onReady() {
super.onReady();
print("##########Im Reasdy###########");
googleSignIn = GoogleSignIn();
// Load current user
_firebaseUser = Rx<User?>(_auth.currentUser);
_googleSignInAccount = Rx<GoogleSignInAccount?>(googleSignIn.currentUser);
// Bind Streams for listeners
_firebaseUser.bindStream(_auth.userChanges());
_googleSignInAccount.bindStream(googleSignIn.onCurrentUserChanged);
// Call workers to update auth state
ever(_firebaseUser, _manageAuthState);
ever(_googleSignInAccount, _manageAuthStateGoogle);
}
// Manage the auth state
_manageAuthState(User? user) {
print("Firebase auth state active :D");
if (user == null) {
Get.offAllNamed("/LogIn");
} else {
Get.offAllNamed("/Home");
}
}
// Manage the auth state regarding google's user state
_manageAuthStateGoogle(GoogleSignInAccount? googleSignInAccount) {
print("Google auth state active");
if (googleSignInAccount == null) {
Get.offAllNamed("/LogIn");
} else {
Get.offAllNamed("/Home");
}
}
// Sign with google account
Future<void> signupGoogle() async {
final GoogleSignIn googleSignIn = GoogleSignIn();
try {
final GoogleSignInAccount? googleSignInAccount =
await googleSignIn.signIn();
_googleSignInAccount.value = googleSignInAccount;
if (googleSignInAccount != null) {
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential authCredential = GoogleAuthProvider.credential(
idToken: googleSignInAuthentication.idToken,
accessToken: googleSignInAuthentication.accessToken);
// Bind Google account with Firebase account
UserCredential resultUser = await _auth.signInWithCredential(
authCredential);
print(resultUser.credential);
// Set a listener for the user
_firebaseUser.value = resultUser.user;
print(resultUser.user);
// Store user data if its new
/*User user = User(
name: username,
email: email,
uid: cred.user!.uid,
profilePhoto: downloadUrl,
);
await firestore
.collection('users')
.doc(cred.user!.uid)
.set(user.toJson());*/
Get.offAllNamed("/Home");
}
} catch (exc) {
print(exc);
Get.snackbar(
'Something went wrong',
"We couldn't connect with google's server",
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.amber,
colorText: Colors.white
);
}
}
Future<void> logoutGoogle() async {
print("Sign out!");
await googleSignIn.signOut();
await _auth.signOut();
print(googleSignIn.currentUser?.email);
print("#################################");
print(_firebaseUser.value?.uid);
print("#################################");
//Get.offAllNamed("/LogIn");
}
signIn() {}
}
You can stop listening to the animation in the dispose() callback or cancel the timer.
Another solution is to check the mounted property of the state class of your widget before calling setState().
For Example:
if (mounted) {
setState(() {
// Your code here
});
}
Dispose the worker on onClose().

The getter 'user' was called on null on a Firebase Realtime Database + Flutter App

Today i updated the android emulator i use frecuently and for some reason im getting this error. I already update all possible dependences and packages.
I/FirebaseAuth(11346): [FirebaseAuth:] Preparing to create service connection to fallback implementation
W/System (11346): Ignoring header X-Firebase-Locale because its value was null.
E/flutter (11346): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: NoSuchMethodError: The getter 'user' was called on null.
E/flutter (11346): Receiver: null
E/flutter (11346): Tried calling: user
E/flutter (11346): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
E/flutter (11346): #1 _RegistrarseState.signupNewUser (package:mundoplay/code/registrarse/registrarse.dart:511:9)
E/flutter (11346): <asynchronous suspension>
This is part of my current code for the user to register:
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
void signupNewUser(BuildContext context) async {
showDialog(context: context,
barrierDismissible: false,
builder: (BuildContext context)
{
return barraProgreso(mensaje: "Creando su cuenta, espere...",);
});
final firebaseUser = (await _firebaseAuth
.createUserWithEmailAndPassword(
email: email.text, password: password.text)
.catchError((errMsg) {
Navigator.pop(context);
setState(() {
_error = Errors.show(errMsg.code);
});
})).user;
if (firebaseUser != null)
{
Map userDataMap = {
"nombre": nombre.text.trim(),
"apellido": apellido.text.trim(),
"email": email.text.trim(),
"password": password.text.trim(),
"celular": celular.text.trim(),
"direccion": direccion.text.trim(),
"localidad": localidad.text.trim(),
};
usersRef.child(firebaseUser.uid).set(userDataMap).then((value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', email.text);
//Navigator.push(context, new MaterialPageRoute(builder: (context) => new SeleccionarConsola()));
});
My register and login works with a form with TextEditingControllers that are set to the controller value.
I'm working with the firebase realtime database... any extra info, just ask me and i will try add it. THANKS!
According to the docs, catchError
Returns a new Future that will be completed with either the result of this future or the result of calling the onError callback.
So when you initialize your firebaseUser, you use a catchError that doesn't return nothing (i.e. implicitely returns null). You can see this in practice with a simple example:
Future<T> handleError<T>(Future<T> future) {
return future.catchError((e) {
print("An error occurred.");
// Not returning anything here!
});
}
void main() async {
// When no error occurs, it will print 1
print(await handleError(Future.value(1)));
// When an error occurs, it will print null
print(await handleError(Future.error(Error())));
}
Since you've already said that you're not connected to the internet since you're using an emulator, an error is being thrown inside the future (maybe a "no internet exception" kind of error), the future is returning null and thus the "The getter 'user' was called on null." message.
There are two ways you can avoid this:
Using the ?. operator:
final firebaseUser = (await _firebaseAuth
.createUserWithEmailAndPassword(
email: email.text, password: password.text)
.catchError((errMsg) {
Navigator.pop(context);
setState(() {
_error = Errors.show(errMsg.code);
});
}))?.user; // <- use it here!
Doing it step-by-step:
final result = await _firebaseAuth.createUserWithEmailAndPassword(
email: email.text,
password: password.text,
).catchError((e) {
Navigator.pop(context);
setState(() => _error = Errors.show(errMsg.code));
});
// Early exit
if (result == null) return;
// Only retrieve the firebase user here
final firebaseUser = result.user;

The function I wrote to fetch data in the database returns empty. There is data in the database. In flutter

======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building Notlar(dirty, state: _NotlarState#20db1):
The method 'notListesiniGetir' was called on null.
Receiver: null
Tried calling: notListesiniGetir()
Photo-3 code here:
[Future<int> kategoriSil(int kategoriID) async {
var db= await _getDatabase();
var sonuc= await db.delete("kategori", where: 'kategoriID= ?', whereArgs: \[kategoriID\]);
return sonuc;
}
Future<List<Map<String, dynamic>>> notlariGetir() async {
var db = await _getDatabase();
var sonuc= await db.rawQuery('select * from "not" inner join kategori on kategori.kategorID = "not".kategoriID;');
return sonuc;
}
Future<List<Not>> notListesiniGetir() async{
var notlarMapListesi = await notlariGetir();
var notListesi = List<Not>();
for(Map map in notlarMapListesi){
notListesi.add(Not.fromMap(map));
}
return notListesi;
}][1]
error code:======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building Notlar(dirty, state: _NotlarState#20db1):
The method 'notListesiniGetir' was called on null.
Receiver: null
Tried calling: notListesiniGetir()
The relevant error-causing widget was:
Notlar file:///C:/Flutter%20calismalari/not_sepeti/lib/main.dart:63:27
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 _NotlarState.build (package:not_sepeti/main.dart:166:30)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4802:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4685:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4857:11)
...
enter image description hereBwg.png
The problem is not in your code but in your screenshot (that you should add to your question as a code snippet).
You have to await the condition snapshot.hasData
Change from
If (snapshot.connectionState == ConnectionState.done)
To
If (snapshot.connectionState == ConnectionState.done && snapshot.hasData)
Also
var notListesi = List<Not>()
List<Not> notListesi = <Not>[ ]; // better
And more importantly remove DatabaseHelper in your init state:
DatabaseHelper databaseHelper = DatabaSeHelper();
To
databaseHelper = DatabaseHelper();
it will be working.

Error database_closed when using flutter's sqflite

I try to use sqflite to save some data like my class Movie but when i try to insert or query on the database, see this message:
[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: DatabaseException(error database_closed)
my MovieBloc class:
List<Movie> _movies = new List<Movie> ();
class MoviesBloc implements BaseBloc {
final _moviesController = StreamController<List<Movie>>();
Database moviesDB;
String _moviesPath;
Stream<List<Movie>> get moviesStream => _moviesController.stream;
List<Movie> get movies => _movies;
int getLengthMovieList() {
return _movies.length;
}
clearMovieList() {
_movies.clear();
}
Future<void> openMovieDB({String dbName:'movies.db'}) async {
var databasesPath = await getDatabasesPath();
_moviesPath = join(databasesPath, dbName);
moviesDB = await openDatabase(_moviesPath, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE movies (Id INTEGER PRIMARY KEY AUTOINCREMENT, Title TEXT NOT NULL, imdbRating TEXT, Poster TEXT, Plot TEXT, Saved INTEGER DEFAULT 0)');
});
}
Future<void> closeMovieDB() async => moviesDB.close();
void dispose() {
_moviesController.close();
}
}
my MovieProvider class:
class MovieProvider extends MoviesBloc {
String _tableName = 'movies';
Future<Movie> insert(Movie movie, {conflictAlgorithm: ConflictAlgorithm.ignore}) async {
await moviesDB.insert(_tableName, movie.toMap(), conflictAlgorithm: conflictAlgorithm);
return movie;
}
Future<bool> insertAll(List<Movie> movies) async {
await Future.wait(movies.map((movie) async {
await this.insert(movie);
}
));
return true;
}
Future<List<Movie>> paginate(int page, {int limit: 15}) async {
print('in Paginate to SQLite');
List<Map> maps = await moviesDB.query(_tableName,
columns: ['Id', 'Title', 'imdbRating', 'Poster', 'Plot', 'Saved'],
limit: limit,
offset: page == 1 ? 0 : ((page -1) * limit)
);
List<Movie> movies = new List<Movie> ();
if (maps.length > 0) {
maps.map((movie) {
movies.add(Movie.fromJson(movie));
}
);
}
return movies;
}
}
my save method to sqflite:
static Future<bool> saveAllMoviesIntoSqlite(List<Movie> movies) async {
var db = new MovieProvider();
await db.openMovieDB();
await db.insertAll(movies);
await db.closeMovieDB();
return true;
}
my load method from sqflite:
static Future<Map> getAllMoviesFromSqlite(int page) async {
var db = new MovieProvider();
await db.openMovieDB();
List<Movie> movies = new List<Movie> ();
movies = await db.paginate(page);
await db.closeMovieDB();
return {
"currentPage": page,
"movies": movies
};
}
I observe that in insertAll method from MovieProvider class for insert any movie from the movies list to sq with await and async:
Future<bool> insertAll(List<Movie> movies) async {
await Future.wait(movies.map((movie) async {
await this.insert(movie);
}
));
return true;
}
I try to close and open database sometimes sequential and other plays with close and open database ...
my log:
I/flutter ( 1617): in Paginate to SQLite
E/flutter ( 1617): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: DatabaseException(error database_closed)
E/flutter ( 1617): #0 SqfliteDatabaseMixin.checkNotClosed (package:sqflite_common/src/database_mixin.dart:282:7)
E/flutter ( 1617): #1 SqfliteDatabaseExecutorMixin._rawQuery (package:sqflite_common/src/database_mixin.dart:125:8)
E/flutter ( 1617): #2 SqfliteDatabaseExecutorMixin.query (package:sqflite_common/src/database_mixin.dart:110:12)
E/flutter ( 1617): #3 MovieProvider.paginate (package:umdb/bloc/movie_provider.dart:24:38)
E/flutter ( 1617): #4 MovieService.getAllMoviesFromSqlite (package:umdb/services/movie_service.dart:54:23)
E/flutter ( 1617): <asynchronous suspension>
E/flutter ( 1617): #5 MovieService.getSavedFromSQ (package:umdb/services/movie_service.dart:95:26)
E/flutter ( 1617): #6 MovieService.fetchSavedMovies (package:umdb/services/movie_service.dart:105:5)
E/flutter ( 1617): <asynchronous suspension>
E/flutter ( 1617): #7 MovieDetailState.build.<anonymous closure>.<anonymous closure> (package:umdb/ui_widgets/movie_detail.dart:168:42)
E/flutter ( 1617): #8 State.setState (package:flutter/src/widgets/framework.dart:1240:30)
E/flutter ( 1617): #9 MovieDetailState.build.<anonymous closure> (package:umdb/ui_widgets/movie_detail.dart:142:27)
E/flutter ( 1617): #10 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
E/flutter ( 1617): #11 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
E/flutter ( 1617): #12 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:284:5)
E/flutter ( 1617): #13 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:256:7)
E/flutter ( 1617): #14 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:158:27)
E/flutter ( 1617): #15 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:224:20)
E/flutter ( 1617): #16 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:200:22)
E/flutter ( 1617): #17 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:158:7)
E/flutter ( 1617): #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:104:7)
E/flutter ( 1617): #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:88:7)
E/flutter ( 1617): #20 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter ( 1617): #21 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 1617): #22 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter ( 1617): #23 _invoke1 (dart:ui/hooks.dart:267:10)
E/flutter ( 1617): #24 _dispatchPointerDataPacket (dart:ui/hooks.dart:176:5)
please help me to fix it
It is very hard to deal with opening/closing the database. There are indeed many cases where you might access a closed database and even worse, if you open twice the same database, you get the same instance so the first close will close the database.
Personally I recommend keeping the database open.
If you need to handle multiple databases and open/close them for short actions, make sure you have the proper mutex/lock mechanism so that you control how your database is opened/accessed/closed without any other method trying to access the same database at the same time (ok that part is not clear sorry).
Something like that (although I really don't recommend opening/closing each time):
import 'package:synchronized/synchronized.dart';
final static _lock = Lock();
static Future<bool> saveAllMoviesIntoSqlite(List<Movie> movies) async {
return _lock.synchronized(() {
var db = new MovieProvider();
await db.openMovieDB();
await db.insertAll(movies);
await db.closeMovieDB();
return true;
});
}
static Future<Map> getAllMoviesFromSqlite(int page) async {
return _lock.synchronized(() {
var db = new MovieProvider();
await db.openMovieDB();
List<Movie> movies = new List<Movie> ();
movies = await db.paginate(page);
await db.closeMovieDB();
return {
"currentPage": page,
"movies": movies
};
});
}
You need to set null for your database instance when you close your database. Otherwise it will always return the old instance.
Future close() async {
final db = await instance.database;
_database = null;
return db.close();
}
Keeping database without close, and using Singleton pattern is a good choice to solve this problem.
1- Create new file sql_lite_service.dart, this will contains SqlLiteService class with this full code:
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class SqlLiteService {
String dBName = 'my_db_name';
int dBVersion = 1;
// Singleton pattern
static final SqlLiteService _databaseService = SqlLiteService._internal();
factory SqlLiteService() => _databaseService;
SqlLiteService._internal();
static Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
Database db = await _getDB();
return db;
}
Future<Database> _getDB() async{
final path = await _getPath(); // Get a location using getDatabasesPath
return await openDatabase(
path,
onCreate: _onCreate,
version: dBVersion,
onConfigure: (db) async => await db.execute('PRAGMA foreign_keys = ON'),
);
}
// create tables
Future<void> _onCreate(Database db, int version) async {
// Run the CREATE {users} TABLE statement on the database.
await db.execute(
'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, created_at datetime default current_timestamp, updated_at datetime default current_timestamp)'
);
// todo: Add your code here ...
}
Future<String> _getPath() async {
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, dBName);
return path;
}
}
2- Now you can get Database instance from any other class by:
final Database database = await SqlLiteService().database;
// usage example
await database.rawDelete(
'DELETE FROM users WHERE id = ?',
[1]
);
Using this way will solve database_closed error...

Cannot query a document from a sub collection from Firestore collection

I am trying to query a specific document nested under collection'users/document'useruid'/collection'items'/'document'documentid'. I keep getting a 'null' value and it errors out:
try {
final user = await _auth.currentUser();
if (user != null) {
_loggedInUser = user;
_userUid = _loggedInUser.uid;
setState(() {
// showSpinner = false;
print(_userUid);
});
}
} catch (e) {
print(e);
}
}
void getItemDetail() async {
await _firestore
.collection('users')
.document(_userUid)
.collection('items')
.document(documentID)
.get()
.then((DocumentSnapshot snapshot) {
itemDetails = snapshot.data;
itemName = snapshot['item_name'];
itemNum = snapshot['item_num'.toString()];
itemDesc = snapshot['item_desc'];
itemLoc = snapshot['item_location'];
itemQty = snapshot['item_qty'.toString()];
itemUom = snapshot['item_uom'];
itemMfr = snapshot['item_mfr'];
itemStock = snapshot['out_of_stock'];
lastEditDate = snapshot['edit_date'];
createDate = snapshot['create_date'];
imageURL = snapshot['image_url'];
setState(() {
dateCreated =
new DateFormat.yMMMMd("en_US").format(createDate.toDate());
itemDetails = snapshot.data;
showSpinner = false;
stockCheck();
editDateCheck();
});
});
}
#override
void initState() {
testApp();
getCurrentUser();
getItemDetail();
// editDateCheck();
super.initState();
}
flutter error output:
[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: The method '[]' was called on null.
Receiver: null
Tried calling: []("item_name")
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 DocumentSnapshot.[] (package:cloud_firestore/src/document_snapshot.dart:29:42)
#2 _ItemDetailScreenState.getItemDetail.<anonymous closure> (package:simmanager/screens/item_detail_screen.dart:77:26)
#3 _rootRunUnary (dart:async/zone.dart:1132:38)
#4 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#5 _FutureListener.handleValue (dart:async/future_impl.dart:137:18)
#6 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:678:45)
#7 Future._propagateToListeners (dart:async/future_impl.dart:707:32)
#8 Future._completeWithValue (dart:async/future_impl.dart:522:5)
#9 _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:30:15)
#10 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:288:13)
#<…>
The documentid is passed from the navigator on previous page. I have printed (userid, documentid" all the values correctly print.
You should always check whether you're snapshot has any data or null like,
if(snapshot!=null)
Then you always should check the data inside snapshot is null or not like,
if(snapshot.data!=null)
Also, in your case you are calling you're data on null object.
I think you need to replace your snapshot['item_name'] with snapshot.data['item_name'] for all the item.
You are mistakenly calling snapshot["your_field"] instead of snapshot.data["your_field"] which is causing the issue.
Also, checking null safety while parsing the data from the network is recommended always.

Resources