Why can't I access the firestore values in flutter? - flutter

Using the example here.
I modified some code to output the the message onTap as follows:
The List is generated with real values from firestore.
but the output is blank. It's not empty, as I tested with another set of output.
How can I retrieve these real values to use in onTap event handler?
....
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
final dynamic message = document['message'];
return ListTile(
title: Text(
message != null ? message.toString() : '<No message retrieved>',
),
subtitle: Text('Message ${index + 1} of $messageCount'),
onTap: (){
print(message.toString());
// OUTPUT: (Nothing)
},
);
},
);
....
onTap: (){
print(message.toString().isEmpty ? 'Empty' : 'Not Empty');
//OUTPUT: Not Empty
print(message== null ? 'Null' : 'Not Null');
//OUTPUT: Not Empty
},

The reason you don't get your variable printed in onTap may lie somewhere else, e.g. in that you are possibly running other version of your app than your current code corresponds to. Try hard resetting the app and make sure you refresh the code on the emulator. Don't open the app manually from the emulator, it can load older version of your code.
I recreated exact the same code from Flutter example and it works perfectly fine (message gets printed correctly when tapping on a list tile as expected).
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final Firestore firestore = Firestore();
runApp(MaterialApp(
title: 'Firestore Example', home: MyHomePage(firestore: firestore)));
}
class MessageList extends StatelessWidget {
MessageList({this.firestore});
final Firestore firestore;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: firestore
.collection("messages")
.orderBy("created_at", descending: true)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
final dynamic message = document['message'];
return ListTile(
trailing: IconButton(
onPressed: () => document.reference.delete(),
icon: Icon(Icons.delete),
),
title: Text(
message != null ? message.toString() : '<No message retrieved>',
),
subtitle: Text('Message ${index + 1} of $messageCount'),
onTap: () {
print(message.toString());
// THIS ACTUALLY WORKS !!
},
);
},
);
},
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({this.firestore});
final Firestore firestore;
CollectionReference get messages => firestore.collection('messages');
Future<void> _addMessage() async {
await messages.add(<String, dynamic>{
'message': 'Hello world!',
'created_at': FieldValue.serverTimestamp(),
});
}
Future<void> _runTransaction() async {
firestore.runTransaction((Transaction transaction) async {
final allDocs = await firestore.collection("messages").getDocuments();
final toBeRetrieved =
allDocs.documents.sublist(allDocs.documents.length ~/ 2);
final toBeDeleted =
allDocs.documents.sublist(0, allDocs.documents.length ~/ 2);
await Future.forEach(toBeDeleted, (DocumentSnapshot snapshot) async {
await transaction.delete(snapshot.reference);
});
await Future.forEach(toBeRetrieved, (DocumentSnapshot snapshot) async {
await transaction.update(snapshot.reference, {
"message": "Updated from Transaction",
"created_at": FieldValue.serverTimestamp()
});
});
});
await Future.forEach(List.generate(2, (index) => index), (item) async {
await firestore.runTransaction((Transaction transaction) async {
await Future.forEach(List.generate(10, (index) => index), (item) async {
await transaction.set(firestore.collection("messages").document(), {
"message": "Created from Transaction $item",
"created_at": FieldValue.serverTimestamp()
});
});
});
});
}
Future<void> _runBatchWrite() async {
final batchWrite = firestore.batch();
final querySnapshot = await firestore
.collection("messages")
.orderBy("created_at")
.limit(12)
.getDocuments();
querySnapshot.documents
.sublist(0, querySnapshot.documents.length - 3)
.forEach((DocumentSnapshot doc) {
batchWrite.updateData(doc.reference, {
"message": "Batched message",
"created_at": FieldValue.serverTimestamp()
});
});
batchWrite.setData(firestore.collection("messages").document(), {
"message": "Batched message created",
"created_at": FieldValue.serverTimestamp()
});
batchWrite.delete(
querySnapshot.documents[querySnapshot.documents.length - 2].reference);
batchWrite.delete(querySnapshot.documents.last.reference);
await batchWrite.commit();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Firestore Example'),
actions: <Widget>[
FlatButton(
onPressed: _runTransaction,
child: Text("Run Transaction"),
),
FlatButton(
onPressed: _runBatchWrite,
child: Text("Batch Write"),
)
],
),
body: MessageList(firestore: firestore),
floatingActionButton: FloatingActionButton(
onPressed: _addMessage,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

TLDR: Updating the database values worked.
This must be the most weirdest thing I have seen.
But instead of the Text itself, I tried printing the length of it and it has same length as the text it had in ListTile.
Then I checked the firebase database. My findings:
Data was visible
Plain text - English
Selecting the data and copy pasting it on a notepad pasted Nothing
Retyping the data made the app to work as expected.
I didn't think of it before, but how the data was initially added to database might have been the issue.

Related

Riverpood does not update the status until I enter the screen

I have a general configuration screen, with a button that syncs the data
(...)
appBar: AppBar(
actions: [
Row(
children: [
const Text(ConfigurationsStringsUI.updateGeneral),
IconButton(
icon: const Icon(Icons.sync),
onPressed: () {
ref.read(listProductController.notifier).syncProducts();
ref.read(listEmployedController.notifier).syncEmployees();
},
),
],
)
],
),
(...)
In the case of products, it has a specific screen that is responsible for managing them, basically a CRUD. When I press the sync button, the idea is to connect to supabase and update the data. While this is happening display a loadign. The problem is that the loading does not appear.
products_page.dart
GetIt sl = GetIt.instance;
class CRUDProduct extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: [
IconButton(
onPressed: () {
ref.read(listProductController.notifier).syncProducts();
},
icon: const Icon(Icons.update),
)
],
),
floatingActionButton: ref.watch(isVisibleFabProducts)
? FloatingActionButton(
onPressed: () {
showDialog(
context: scaffoldKey.currentContext!,
builder: (context) => AddProductDialog(),
);
},
child: const Icon(Icons.fastfood),
)
: null,
body: ref.watch(listProductController).when(
data: (products) {
if (products.isEmpty) {
return const Center(
child: Text(ProductStringsUI.emptyList),
);
} else {
return NotificationListener<UserScrollNotification>(
onNotification: (notification) {
if (notification.direction == ScrollDirection.forward) {
ref.read(isVisibleFabProducts.notifier).state = true;
}
if (notification.direction == ScrollDirection.reverse) {
ref.read(isVisibleFabProducts.notifier).state = false;
}
return true;
},
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
return ItemProductList(product: products[index]);
},
separatorBuilder: (_, __) => const Divider(
color: Colors.grey,
),
itemCount: products.length),
);
}
},
error: (error, stackTrace) {
return const Center(
child: Text(ProductStringsUI.errorList),
);
},
loading: () {
return const Center(child: CircularProgressIndicator());
},
));
}
}
Product provider:
final listProductController =
StateNotifierProvider<ProductController, AsyncValue<List<LocalProduct>>>(
(ref) => ProductController(ref));
product_controller.dart
class ProductController extends StateNotifier<AsyncValue<List<LocalProduct>>> {
ProductController(this._ref) : super(const AsyncValue.loading()) {
getProducts();
}
final Ref _ref;
Future<void> getProducts() async {
try {
final employees = await sl.get<ListProductUseCase>().getProducts();
if (mounted) {
state = AsyncValue.data(employees);
}
} catch (e) {
state = AsyncValue.error(e, StackTrace.current);
}
}
Future<void> syncProducts() async {
try {
_ref.read(listCategoryController.notifier).state =
const AsyncValue.loading();
_ref.read(listEmployedController.notifier).state =
const AsyncValue.loading();
state = const AsyncValue.loading();
await _ref.read(listCategoryController.notifier).syncCategory();
final employees = await sl.get<SyncProductUseCase>().syncProducts();
state.whenData((value) {
if (mounted) {
state = AsyncValue.data([...value, ...employees]);
}
});
_ref.invalidate(listProductController);
} catch (e) {
state = AsyncValue.error(e, StackTrace.current);
}
}
}
In the case of products, it has a specific screen that is responsible for managing them, basically a CRUD. When I press the sync button, the idea is to connect to supabase and update the data. While this is happening display a loadign. The problem is that the loading does not appear. There are two scenarios:
1-I open the app, I press the sync button on the configuration screen, I enter the screen in charge of managing the products, I see the loaded products, and at the moment it updates me with the new data, when I should see the loading and then the new ones data.
In this scenario is where my biggest doubt about the strange behavior is.
2-I open the app, I enter the screen in charge of managing the products, I go to the configuration screen, I press sync, and in that case if I go to enter if the loading appears
The same goes for employees.
When you have an async provider in Riverpod, you should tell it to load:
Future<void> addTopic(String name) async {
state = const AsyncValue.loading(); // Here
state = await AsyncValue.guard(() async { // And then you guard the value
// Inside the brackets do all the logic of the function
final currentId = ref.watch(currentSubjectProvider);
final topicRepo = ref.watch(topicRepoProvider);
await topicRepo.addTopic(currentId!, name);
return _getTopics();
});
}
This example of mine, is a real project I am working on, and this is loading as expected, but I should mention that I am using the Riverpod generator, so if the generator did something, I am unaware of it.
If you set the state to loading and guard the value, all listerners of that provider should be loading correctly.

Problem with Future<dynamic> is not a subtype of type List<Routes> in Flutter

I have problem with async-await. (I am not very good at programming, but learning by creating random apps...)
Problem: Using dio to get data from Node.js json-server, but I cant transform data from
Future to List. Error: type 'Future' is not a subtype of type 'List' at line 13. List<Routes> routes = _getData();
I have read a lot of discussions here on stackoverflow and many other websites, but I just can't make it work. :( So here I am asking with specific code.
Needed code:
Code where error appears (route_list_screen.dart)
import 'package:app/api/api.dart';
import 'package:flutter/material.dart';
import 'package:app/models/routes.dart';
class RouteList extends StatefulWidget {
const RouteList({Key? key}) : super(key: key);
#override
State<RouteList> createState() => _RouteListState();
}
List<Routes> routes = _getData();
class _RouteListState extends State<RouteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Text'),
automaticallyImplyLeading: true,
centerTitle: true,
),
body: ListView.separated(
itemCount: routes.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(routes[index].number),
subtitle: Text(routes[index].routeType),
trailing: const Text('??/??'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RouteSelected(
passedRoutes: routes[index],
),
),
);
},
);
},
separatorBuilder: (context, index) {
return const Divider();
},
),
);
}
}
_getData() async {
Future<dynamic> futureOfRoutes = getRouteList(856);
List<dynamic> routes = await futureOfRoutes;
return routes;
}
Connecting to server (api.dart)
import 'package:app/models/routes.dart';
const _url = 'http://10.0.2.2:3000/routes';
getRouteList(int driverId) async {
Response response;
var dio = Dio(BaseOptions(
responseType: ResponseType.plain,
));
response = await dio.get(_url, queryParameters: {"driver_id": driverId});
final data = routesFromJson(response.data);
return data;
}
List with param Routes = Routes is model from app.quicktype.io
_getData() returns a future, you can't direct assign it on List<Routes> where it is Future<dynamic>.
You can use initState
class _RouteListState extends State<RouteList> {
List<Routes>? routes;
_loadData() async {
routes = await _getData();
setState(() {});
}
#override
void initState() {
super.initState();
_loadData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: routes == null
? Text("On Future ....")
: ListView.separated(
itemCount: routes?.length??0,
itemBuilder: (context, index) {
return ListTile(
title: Text(routes![index].number),
subtitle: Text(routes![index].routeType),
trailing: const Text('??/??'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RouteSelected(
passedRoutes: routes![index],
),
),
);
},
);
},
separatorBuilder: (context, index) {
return const Divider();
},
),
);
}
}
Also check FutureBuilder

Null check operator used on null value

I need to grab a list of all messages in the database where the level is less than or equal to the user's current level. To do this I am attempting to use a nested streambuilder. First one pulls the user info, then I use the user level to make the second query. My problem is I get an error.
Null check operator used on null value
I do not understand this error. I checked firestore to see that there is data in the collection, and I also made sure to put the proper rules in place. I've tried several variations of this code and this is the only one so far that at least doesn't give me an error in the emulator. My emulator shows the appbar and the body is blank.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../screens/welcome_screen.dart';
class MessagesScreen extends StatefulWidget {
static const String id = 'messages_screen';
#override
_MessagesScreenState createState() => _MessagesScreenState();
}
class _MessagesScreenState extends State<MessagesScreen> {
final _firestore = FirebaseFirestore.instance;
final _auth = FirebaseAuth.instance;
User loggedInUser;
int _userCurrentLevel;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() {
try {
final user = _auth.currentUser;
if (user != null) {
loggedInUser = user;
}
} catch (e) {
print(e);
}
}
getMessages(AsyncSnapshot<QuerySnapshot> snapshot2) {
return snapshot2.data.docs
.map((doc) => new ListTile(title: Text(doc['from']), subtitle: Text(doc['text'])))
.toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Darker Slate'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.chat),
tooltip: 'Messages',
onPressed: () {},
),
IconButton(
icon: const Icon(Icons.exit_to_app),
tooltip: 'Log Out',
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, WelcomeScreen.id);
},
),
],
),
body: StreamBuilder<DocumentSnapshot>(
stream: _firestore.collection('users')
.doc(_auth.currentUser.uid)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red[900],
),
);
}
_userCurrentLevel = snapshot.data['userlevel'];
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages')
.where('level', isLessThanOrEqualTo: _userCurrentLevel).snapshots(),
builder: (context, snapshot2) {
if (!snapshot2.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red[900],
),
);
}
return Column(
children: [
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot2.data.docs.length,
itemBuilder: (_, index) {
return new ListView(children: getMessages(snapshot2));
}),
],
);
}
);
}),
);
}
}

Flutter Cloud Firestore issue initializing futures

I am trying to get my first cloud firestore app working, using the firestore_flutter master example for cloud firestore as a template. In the example app main.dart has all the firestore and other init and access logic, and consequently runs the Futre void() main() method as in the example main.dart section copy below. It does not have the
====================
import...
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final FirebaseApp app = await FirebaseApp.configure(
name: 'test',
options: const FirebaseOptions(
googleAppID: '1:79601577497:ios:5f2bcc6ba8cecddd',
gcmSenderID: '79601577497',
apiKey: 'AIzaSyArgmRGfB5kiQT6CunAOmKRVKEsxKmy6YI-G72PVU',
projectID: 'flutter-firestore',
),
);
final Firestore firestore = Firestore(app: app);
runApp(MaterialApp(title: 'Firestore Example', home: MyHomePage(firestore: firestore)));
}
class MyHomePage extends StatelessWidget {
MyHomePage({this.firestore});
final Firestore firestore;
CollectionReference get messages => firestore.collection('messages');
====================
But in my app I want to load the data in the 3rd page after authenticating users with firebase_auth, but the main Future function never gets called (only 1 main right?)
Here is my code that does not work because main() never gets called. What do I need to do to make the code below work? PS: I tried making it a called function, moving it, but I am not able to make main run in my page source below.
It doesn't have the next line from teh example in it, because it didn't help either.
runApp(MaterialApp(title: 'Firestore Example', home: TopicList(firestore: firestore)));}
Thanks for any help in advance.
====================
import ...
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final FirebaseApp app = await FirebaseApp.configure(
name: 'test',
options: const FirebaseOptions(
googleAppID: 'AIzaSyBPq8j3NT5DMmVgXLEN3Z91QJK32ZhrH90',
gcmSenderID: '36424891892',
apiKey: 'AIzaSyArgmRGfB5kiQT6CunAOmKRVKEsxKmy6YI',
projectID: 'usay-94b3a',
),
);
final Firestore firestore = Firestore(app: app);
}
class MessageList extends StatelessWidget {
MessageList({this.firestore});
final Firestore firestore;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection("message").orderBy("timestamp", descending: true).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
final dynamic message = document['message'];
return ListTile(
trailing: IconButton(
onPressed: () => document.reference.delete(),
icon: Icon(Icons.delete),
),
title: Text(
message != null ? message.toString() : '<No message retrieved>',
),
subtitle: Text('Message ${index + 1} of $messageCount'),
);
},
);
},
);
}
}
class TopicList extends StatelessWidget {
TopicList({this.firestore});
final Firestore firestore;
CollectionReference get messages => firestore.collection('message');
Future<void> _addMessage() async {
await messages.add(<String, dynamic>{
'messageTitle': 'Hello world!',
'messageDescription': 'Hello world!',
'timestamp': FieldValue.serverTimestamp(),
});
}
Future<void> _runTransaction() async {
firestore.runTransaction((Transaction transaction) async {
final allDocs = await firestore.collection("message").getDocuments();
final toBeRetrieved = allDocs.documents.sublist(allDocs.documents.length ~/ 2);
final toBeDeleted = allDocs.documents.sublist(0, allDocs.documents.length ~/ 2);
await Future.forEach(toBeDeleted, (DocumentSnapshot snapshot) async {
await transaction.delete(snapshot.reference);
});
await Future.forEach(toBeRetrieved, (DocumentSnapshot snapshot) async {
await transaction.update(snapshot.reference, {
// "messageTitle": FieldValue.messageTitle,
// "messageDescription": FieldValue.messageDescription",
"timestamp": FieldValue.serverTimestamp()
});
});
});
await Future.forEach(List.generate(2, (index) => index), (item) async {
await firestore.runTransaction((Transaction transaction) async {
await Future.forEach(List.generate(10, (index) => index), (item) async {
await transaction.set(firestore.collection("message").document(), {
"messageTitle": "Created from Transaction $item",
"messageDescription": "Created from Transaction $item",
"timestamp": FieldValue.serverTimestamp()
});
});
});
});
}
Future<void> _runBatchWrite() async {
final batchWrite = firestore.batch();
final querySnapshot = await firestore.collection("message").orderBy("timestamp").limit(12).getDocuments();
querySnapshot.documents.sublist(0, querySnapshot.documents.length - 3).forEach((DocumentSnapshot doc) {
batchWrite.updateData(doc.reference, {
"messageTitle": "Batched messageTitle",
"messageDescription": "Batched messageDescription",
"timestamp": FieldValue.serverTimestamp()
});
});
batchWrite.setData(firestore.collection("message").document(), {
"messageTitle": "Batched message created",
"messageDescription": "Batched message created",
"timestamp": FieldValue.serverTimestamp()
});
batchWrite.delete(querySnapshot.documents[querySnapshot.documents.length - 2].reference);
batchWrite.delete(querySnapshot.documents.last.reference);
await batchWrite.commit();
}
#override
Widget build(BuildContext context) {
return FittedBox(
fit: BoxFit.scaleDown,
child: Scaffold(
appBar: AppBar(
title: const Text('Firestore Example'),
actions: <Widget>[
FlatButton(
onPressed: _runTransaction,
child: Text("Run Transaction"),
),
FlatButton(
onPressed: _runBatchWrite,
child: Text("Batch Write"),
)
],
),
body: MessageList(firestore: firestore),
floatingActionButton: FloatingActionButton(
onPressed: _addMessage,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
),
);
}
}
===============

Why doesn't the FutureBuilder return data queried from Sqlite database?

I need to query the to-dos stored in sqlite database as a list. I used a FutureBuilder to retrieve the list, but it doesn't seem to return the value. It shows an error like "NoSuchMethodError: The getter 'length' was called on null."
This is my database provider:
class MemoDbProvider{
Database db;
MemoDbProvider(){
init();
}
void init() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path,"memos.db");
db = await openDatabase(
path,
version: 1,
onCreate: (Database newDb,int version){
newDb.execute("""
CREATE TABLE Memos(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
content TEXT)"""
);
});
}
Future<int> addItem(MemoModel item){
return db.insert("Memos", item.toMap(),
conflictAlgorithm: ConflictAlgorithm.ignore,
);
}
Future<List<MemoModel>> fetchMemos() async{
final maps = await db.query("Memos");
return List.generate(maps.length, (i) {
return MemoModel(
id: maps[i]['id'],
title: maps[i]['title'],
content: maps[i]['content'],
);
});
}
}
This is the MemoList Widget:
class MemoList extends StatefulWidget{
createState(){
return MemoListState();
}
}
class MemoListState extends State<MemoList>{
MemoDbProvider dbProvider = MemoDbProvider();
Widget build(context){
return Scaffold(
appBar: AppBar(
title: Text('Memo'),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: (){
Navigator.pushNamed(context, '/addMemo');
},
child: Icon(Icons.add),
backgroundColor: Colors.black,
),
body: FutureBuilder(
future: dbProvider.fetchMemos(),
builder: (context,AsyncSnapshot<List<MemoModel>> snapshot){
if(!snapshot.hasData){
print('error');
}
print(snapshot.data);
return ListView.builder(
itemCount : snapshot.data.length,
itemBuilder: (context,int index){
return buildMemo(snapshot.data[index]);
},
);
},
),
);
}
Widget buildMemo(MemoModel memo){
return Column(
children: [
ListTile(
onTap: (){},
title: Text(memo.title),
subtitle: Text(memo.content),
),
Divider(
height: 8.0,
),
],
);
}
}
When i press the FloatingActionButton(/addMemo) it works and print the memo list in terminal. When i go back to MemoList i can properly see the list. Why isn't it rendering at the startup?
Since i am new to flutter it would be great if i get a fine explanation on the issue in my code.
The problem is that you are creating object of db but not initialising it, because of that you are getting this error.
To solve this error, you can create object in method because it is async, so you can not even create at class level.
Create db variable as following.
final db = await database;