Flutter Text("${sqlite_tbl[0]['body']") throwing NoSuchMethodError - sqlite

In this Flutter project, I am displaying information from a local sqlite database onto a page. There are many examples on the internet using the ListView.builder and ListTile to display. However, I am not in this case and I want to be able to access and display a specific column from a table.
First I create an instance of List then make that instance be a rawQuery like so.
List<Map> sqlite_tbl = db.rawQuery('SELECT * FROM sample_table');
Then, in the build method for the page, I place some text in the appBar and in a SingleChildScrollView which is text I retrieve from the database like so..
child: new Text("${sqlite_tbl[0]['title']}",
child: new Text("${sqlite_tbl[0]['body']}",
When I access this page from the previous page, I get the red flash with NoSuchMethodError for a second or two, then the page will display with no errors. In my Run log, it will say The method '[]' was called on null. Receiver: null Tried calling: . So I assume it is the [0] where I retrieve the text, however once I remove that it will say that type argument type 'String' cannot be assigned to parameter type 'int'.
Does anybody know the correct way to retrieve this data or how to fix this issue? Please let me know if you need to see more of my code.

The method db.rawQuery will return the type Future<List<Map>>. You should change your instantiation of the list to be:
List<Map> sqlite_tbl = await db.rawQuery('SELECT * FROM sample_table');
You would then have to call setState.
A better way for you to do this is something like:
List<Map> sqlite_tbl;
bool initialized;
initState() {
super.initState();
initialized = false;
_getData();
}
_getData() {
db.rawQuery('SELECT * FROM sample_table').then((results) {
setState(() {
sqlite_tbl = results;
initialized = true;
});
}).catchError((e) { // do something });
}
And then you would build your UI depending on the value of initialized.

Related

Riverpod Flutter: Looking for best way to combine FutureProvider with StateNotifierProvider

I am working on a basic Support Ticket System. I get the Tickets from Firebase (Either as a Stream or Future).
I want to allow some Filtering Options (e.g. sort by Status and Category).
For this, I thought about using A Future Provider to get the List and a StateNotiferProvider to update the List depending on which filter is being used.
This is the code I have so far:
final ticketListStreamProvider =
RP.FutureProvider((_) => FirestoreService.getTicketList());
class TicketListNotifier extends RP.StateNotifier<List<Ticket>> {
TicketListNotifier() : super([]);
void addTicket(Ticket ticket) {
state = List.from(state)..add(ticket);
}
void removeTicket(Ticket ticket) {
state = List.from(state)..remove(ticket);
}
}
final ticketsController =
RP.StateNotifierProvider<TicketListNotifier, List<Ticket>>(
(ref) => TicketListNotifier(),
);
There are multiple issues I have with that. Firstly it doesn't work.
The StateNotifier accepts a List and not a Future<List>. I need to convert it somehow or rewrite the StateNotifier to accept the Future.
I was trying to stay close to one of the official examples.
(https://github.com/rrousselGit/riverpod/tree/master/examples/todos)
Unfortunately, they don't use data from an outside source like firebase to do it.
What's the best approach to get resolve this issue with which combination of providers?
Thanks
You can fetch your ticketlist in your TicketListNotifier and set its state with your ticket list.
class TicketListNotifier extends RP.StateNotifier<List<Ticket>> {
TicketListNotifier() : super([]);
Future<void> fetchTicketList() async {
FirestoreService.getTicketList().when(
//success
// state = fetched_data_from_firestore
// error
// error handle
)
}
}
final ticketsController =
RP.StateNotifierProvider<TicketListNotifier, List<Ticket>>(
(ref) => TicketListNotifier(),
);
Call this method where you want to fetch it /*maybe in your widget's initState method
ref.read(ticketsController.notifier).fetchTicketList();
Now ref.read(ticketsController); will return your ticket list
Since you have the ticket list in your TicketListNotifier's state you can use your add/remove method like this:
ref.read(ticketsController.notifier).addTicket(someTicket);

Flutter/Dart/Firebase - Updated nested map values

I am trying to record which on my users has purchased which ticket in my app. I am using firebase to store my data about my users and giveaways. When a purchase is complete, I am trying to update the relevant giveaway and assign each ticket to a user using their id.
Firstly, I am not sure if my data schema is the most appropriate for what I'm trying to achieve so open to suggestions for editing as I'm still quite new to the flutter world.
Second, here is how my data is currently structured:
Here is how I have structured my code. Here is my SingleBasketItem model:
class SingleBasketItem {
final String id;
final String uid;
final OurGiveAways giveaway;
final int ticketNumber;
SingleBasketItem(this.id, this.uid, this.giveaway, this.ticketNumber);
}
Here is my Shopping Basket model, I have added an Elevated Button to my shopping basket page which, when tapped, will execute the placeOrder() function:
class ShoppingBasket extends ChangeNotifier {
Map<String, SingleBasketItem> _items = {};
Map<String, SingleBasketItem> get items {
return {..._items};
}
void addItem(String id, String uid, OurGiveAways giveaway, int ticketNumber) {
_items.putIfAbsent(
id,
() => SingleBasketItem(id, uid, giveaway, ticketNumber),
);
notifyListeners();
}
void placeOrder(BuildContext context, OurUser user) {
for (var i = 0; i < _items.length; i++) {
FirebaseFirestore.instance
.collection('giveaways')
.doc(_items.values.toList()[i].giveaway.giveawayId)
.update(
{
'individual_ticket_sales'[_items.values.toList()[i].ticketNumber]:
user.uid,
},
);
}
}
}
Below is an image of the results:
By analysing the results it looks like my code is creating a new field with a title of the 1st index character of individual_ticket_sales ("n" because ive bought ticket 1), how can I set the nested "1" (or whatever ticket I choose) to my user id rather than creating a new field? Thanks.
I would recommend to refactor your database structure because the first problem you will hit with that one is that firestore for now does not support updating a specific index for an array field value. You can get more info about that here.
You could get the whole value individual_ticket_sales update it and save it again as whole but it would be just a matter of time when you would hit the problem that multiple users want to update the same value on almost the same time and one of the changes get's lost. Even the usage of transaction would not be 100% safe because of the size of the object and potential multiple changes.
Is it possible for you to store each ticketId as a firestore document in a firestore collection like this:
FirebaseFirestore.instance
.collection('giveaways')
.doc(_items.values.toList()[i].giveaway.giveawayId)
.collection('individual_ticket_sales')
.doc(i)
.update(user.uid);

Flutter listen to one document change using provider

I have seen examples of listening to document changes in streambuilder, but is it possible to use it in providers? I want to listen to changes in the document in userinfo collection.
Here is my code:
in databaseservice.dart
Stream <DocumentSnapshot> get info{
return userinfo.doc(uid).snapshots();
}
In main
return MultiProvider(
providers: [
StreamProvider<DocumentSnapshot>.value(
value: DatabaseService().info
), // other providers
In wrapper where I need to see the change:
final info = Provider.of<DocumentSnapshot>(context).data();
However, I'll first get error:
The method 'data' was called on null.
Receiver: null
Tried calling: data()
And later, the info variable is giving me null instead of a map.
I want to let users input their name and age after their first signup, but not when they sign in. So my idea is that when users sign up, there will be a new document in the collection, "userinfo", which sets their age and name as null at first.
Then the wrapper checks if the name is null. If null, it will turn to the gather information page. If it has a value, it will turn to the home page.
Could anyone tell me where I am doing wrong with my document snapshot thing, or have a better idea to implement this?

How to get data from firebase query based on value from another firebase query in FutureBuilder in Flutter?

I am new to flutter and I am sure there is a simple way of doing this. Let me first give you a background. I have 2 tables(collections). The first one store a mapping. Therefore it returns a key based on an id which will be used to query the second table and retrieve the data from firebase.
I have written 2 data models and 2 functions which return Future<> data. They are as follows-
Future<SpecificDevice> getSpecificDevice(String deviceId) {
Future<SpecificDevice> obj =_database.reference().child("deviceMapping").orderByChild("deviceId").equalTo(deviceId).once().then((snapshot) {
SpecificDevice specificDevice = new SpecificDevice(deviceId, "XXXX", new List<String> ());
if(snapshot.value.isNotEmpty){
print(snapshot.value);
snapshot.value.forEach((key,values) {
if(values["deviceId"] == deviceId) {
specificDevice.deviceKey = values["deviceDesc"];
specificDevice.vendorList = List.from(values["vendorList"]);
}
});
}
return specificDevice;
});
return obj;
}
This function gets the mapping deviceId -> deviceKey.
This is the key of record stored in another table. Following is the function for it.
Future<Device> getDeviceDescription(String deviceKey) {
Future<Device> device = _database.reference().child("deviceDescription").once().then((snapshot) {
Device deviceObj = new Device("YYYY", "YYYY", "YYY", "YYYY", "YYYY");
if(snapshot.value.isNotEmpty){
print(snapshot.value);
//Future<SpecificDevice> obj = getSpecificDevice(deviceId);
//obj.then((value) {
snapshot.value.forEach((key,values) {
if(key == deviceKey) { // compare with value.deviceKey instead
print(values["deviceDescription"]); // I get the correct data here.
deviceObj.manual = values["deviceManual"];
deviceObj.deviceType = values["deviceType"];
deviceObj.description = values["deviceDescription"];
deviceObj.brand = values["deviceBrand"];
deviceObj.picture = values["devicePicture"];
}
// });
});
}
return deviceObj;
});
return device;
}
Now both of these functions work. I want to make it work one after the other. In the above function, if I uncomment the lines of code, the data is retrieved properly in the inner function but it returns initial default values set because the values get returned before setting the obj of SpecificDevice.
Here is where I am getting the error. I am calling the second function in FutureBuilder<> code with the above lines uncommented and taking input param as deviceId.
return FutureBuilder<Device>(
future: getDeviceDescription(deviceId),
builder:(BuildContext context,AsyncSnapshot snapshot){... // using snapshot.data in its child.
Here in snapshot.data. would give me YYYY. But it should get me the value from the database.
I am stuck with this for a while. Any help in fixing this? or if what I am trying to do is clear then please suggest me a better way to approach this. Thanks in advance!
The answer is rather simple:
first and foremost - you forgot to use async / await keywords, which will guarantee synchronous data retrieval from the database. Always use them, if you are connecting to any network service
to make one command work after another - use .then((value) {}). It will get data from the first function (which you pass using return) and use it in the second function.
Solved the problem by changing the calling function to -
return FutureBuilder<Device>(
future: getSpecificDevice(deviceId).then((value){
return getDeviceDescription(value.deviceKey);
}),
builder:(BuildContext context,AsyncSnapshot snapshot){

Create and update database with input from PageView.builder in flutter

I have a PageView.builder that has a textFormfield and a title. The builder iterates through the elements of the list and once on the last one, there is a submit button that I would like to send the Key : Value pairs to firestore. when I use for each, it only creates the content of the last item of the list multiple times.
here is my create and update function:
Future updateDatabase(String remarkText) async {
return await databaseCollection.document().setData({
questionItems[index].title : questionItems[index].remarkText
});
}
and this is how I call it in my button
onPressed: () async {
questionItems.forEach((question) async {
await updateDatabase(remarkText);
});
},
How can I loop through them to send data for the previous items as well? Please help.
I think it's iterating over all items, but updating always the same item in Firestore. The variable index should be changed in each iteration somehow. Otherwise each iteration will set value on the same questionItems[index].
I hope it will help!

Resources