Retrieve Data from Realtime Database in Flutter - flutter

I want to retrieve data from a realtime database for a flutter application. My data is built like this.
I need to loop through this data to display it on the application (ecommerce app, obviously). I have tried and failed in many ways. Currently when trying to get the data I see "Instance of '_Future'" as the message.
class Cart extends StatefulWidget {
Cart({Key? key}) : super(key: key);
#override
State<Cart> createState() => _CartState();
}
class _CartState extends State<Cart> {
DatabaseReference ref = FirebaseDatabase.instance.ref();
Object? products;
List productList = [];
String displayText = 'Results go here!';
snapshot() async {
final snapshot = await ref.child('Products').get();
productList = [];
if (snapshot.exists) {
productList.add(snapshot.value);
products = (snapshot.value);
print(snapshot);
print(snapshot.value);
} else {
print('No Data Available');
}
}
#override
void initState() {
super.initState();
snapshot();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const PreferredSize(
preferredSize: Size.fromHeight(60), child: MyAppBar()),
body: Column(
children: [
ElevatedButton(
onPressed: () async {
// await ref.set({"name": "Tyler"});
snapshot();
},
child: Text("Add Data"),
),
Text("${snapshot()}", style: TextStyle(color: Colors.white))
],
)
);
}
}
I also have this data class built from other posts I have seen. I have to admit, I am not entirely sure how to use it.
import 'dart:convert';
class ProductData {
final int productID;
final String productCategory;
final String productDesc;
final String productName;
final String productPrice;
final String productSize;
final bool productInStock;
final String productImage1;
final String productGender;
final String productImage2;
ProductData(
{required this.productID,
required this.productCategory,
required this.productDesc,
required this.productName,
required this.productPrice,
required this.productSize,
required this.productInStock,
required this.productImage1,
required this.productGender,
required this.productImage2});
ProductData copyWith(
{int? productID,
String? productCategory,
String? productDesc,
String? productName,
String? productPrice,
String? productSize,
bool? productInStock,
String? productImage1,
String? productGender,
String? productImage2}) {
return ProductData(
productID: productID ?? this.productID,
productCategory: productCategory ?? this.productCategory,
productDesc: productDesc ?? this.productDesc,
productName: productName ?? this.productName,
productPrice: productPrice ?? this.productPrice,
productSize: productSize ?? this.productSize,
productInStock: productInStock ?? this.productInStock,
productImage1: productImage1 ?? this.productImage1,
productGender: productGender ?? this.productGender,
productImage2: productImage2 ?? this.productImage2,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'productID': productID,
'productCategory': productCategory,
'productDesc': productDesc,
'productName': productName,
'productPrice': productPrice,
'productSize': productSize,
'productInStock': productInStock,
'productImage1': productImage1,
'productGender': productGender,
'productImage2': productImage2,
};
}
factory ProductData.fromMap(Map<String, dynamic> map) {
return ProductData(
productID: map['productID'] as int,
productCategory: map['productCategory'] as String,
productDesc: map['productDesc'] as String,
productName: map['productName'] as String,
productPrice: map['productPrice'] as String,
productSize: map['productSize'] as String,
productInStock: map['productInStock'] as bool,
productImage1: map['productImage1'] as String,
productGender: map['productGender'] as String,
productImage2: map['productImage2'] as String,
);
}
String toJson() => json.encode(toMap());
factory ProductData.fromJson(String source) =>
ProductData.fromMap(json.decode(source) as Map<String, dynamic>);
#override
String toString() {
return 'ProductData(productID: $productID, productCategory: $productCategory, productDesc: $productDesc, productName: $productName, productPrice: $productPrice, productSize: $productSize, productInStock: $productInStock, productImage11: $productImage1, productGender: $productGender, productImage2: $productImage2)';
}
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ProductData &&
other.productID == productID &&
other.productCategory == productCategory &&
other.productDesc == productDesc &&
other.productName == productName &&
other.productPrice == productPrice &&
other.productSize == productSize &&
other.productInStock == productInStock &&
other.productImage1 == productImage1 &&
other.productGender == productGender &&
other.productImage2 == productImage2;
}
#override
int get hashCode {
return productID.hashCode ^
productCategory.hashCode ^
productDesc.hashCode ^
productName.hashCode ^
productPrice.hashCode ^
productSize.hashCode ^
productInStock.hashCode ^
productImage1.hashCode ^
productGender.hashCode ^
productImage2.hashCode;
}
}

Since the data is loaded from Firebase asynchronously, its get() method returns a Future. That's also why you had to declare your snapshot() function as async, which means that you also return a Future.
On its own the rendering code doesn't know anything about Futures, so it renders it by calling its toString() method, which leads to the output you see:
Instance of '_Future'
What you want instead is to wait for the future to resolve, which is just a fancy way of saying that you want to wait for the data to load. An easy way to do that is to use a FutureBuilder, which handles the asynchronous nature of a Future and all possible states it can be in.
That'd look something like:
snapshot() async {
final snapshot = await ref.child('Products').get();
productList = [];
if (snapshot.exists) {
productList.add(snapshot.value);
products = (snapshot.value);
} else {
print('No Data Available');
}
return productList;
}
body: Column(
children: [
ElevatedButton(
onPressed: () async {
snapshot();
},
child: Text("Add Data"),
),
FutureBuilder(
future: snapshot(),
builder: (BuildContext context, AsyncSnapshot asyncSnapshot) {
if (snapshot.hasData) {
var productList = asyncSnapshot.data! as List;
return Text(productList.length.toString());
} else if (snapshot.hasError) {
return Text('Error: ${asyncSnapshot.error}');
} else {
return CircularProgressIndicator(),
}
}
)
],
)

Related

How to get firebase realtime database data in to a list formate in flutter

I am trying to retrieve data from Firebase Realtime Database into a list in Flutter using a model.I don't get snapshot.data how to get data.value. I have read several other posts about using Firebase with Flutter but have not found a clear answer.
Model class screen:
import 'package:firebase_database/firebase_database.dart';
class DataModel {
final String id;
final String name;
final String price;
final String qty;
DataModel(
{required this.id,
required this.name,
required this.price,
required this.qty});
DataModel.fromSnapshot(DataSnapshot snapshot)
: id = snapshot.key.toString(),
name = (snapshot.value as Map<String, dynamic>?)?['productName'] ?? '',
price =
(snapshot.value as Map<String, dynamic>?)?['productPrice'] ?? '',
qty = (snapshot.value as Map<String, dynamic>?)?['qty'] ?? '';
toJson() {
return {
"productName": name,
"productPrice": price,
"qty": qty,
};
}
}
Database service with Firebase query:
import 'package:firebase_database/firebase_database.dart';
import 'package:money_management/data_json_model.dart';
class DatabaseService {
static List<DataModel> getData() {
Query needsSnapshot =
FirebaseDatabase.instance.ref("Money Management").orderByKey();
// print(needsSnapshot); // to debug and see if data is returned
List<DataModel> needs = [];
Map<dynamic, dynamic> values = needsSnapshot.onValue as Map;
values.forEach((key, values) {
needs.add(DataModel.fromSnapshot(values));
});
return needs;
}
}
ListView Page:
import 'package:flutter/material.dart';
import 'package:money_management/data_json_model.dart';
import 'database_service.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
State<ListScreen> createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
List<DataModel> _needs = [];
#override
void initState() {
super.initState();
_setupNeeds();
}
_setupNeeds() async {
List<DataModel> needs = DatabaseService.getData();
setState(() {
_needs = needs;
});
}
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () => _setupNeeds(),
child: ListView.builder(
itemCount: _needs.length,
itemBuilder: (BuildContext context, int index) {
DataModel need = _needs[index];
return Column(
children: [
Text(need.id),
Text(need.name),
Text(need.price),
Text(need.qty),
],
);
}),
);
}
}
Try make the method getData() asynchronous and call get() of FirebaseDatabase instead:
class DatabaseService {
static Future<List<dynamic>> getData() async {
final snapshot = await FirebaseDatabase.instance
.ref("Money Management")
.orderByKey()
.get();
// print(snapshot); // to debug and see if data is returned
List<DataModel> needs = [];
Map<dynamic, dynamic> values = snapshot.value as Map;
values.forEach((key, values) {
needs.add(DataModel.fromSnapshot(values));
});
return needs;
}
}
you can receive data as Map<String,dynamic> in your DataModel like this:
class DataModel {
late String id;
late String name;
late String price;
late String qty;
DataModel({
required this.id,
required this.name,
required this.price,
required this.qty
});
DataModel.fromSnapshot(DataSnapshot snapshot){
Map<String, dynamic> myData= Map<String,dynamic>.from(snapshot.value as
Map);
id = snapshot.key.toString();
name = myData["productName"].toString() ?? '';
price =myData["productPrice"].toString() ?? '';
qty = myData["qty"].toString() ?? '';
}
Map<String,dynamic> toJson() {
return {
"productName": name,
"productPrice": price,
"qty": qty,
};
}
}

The getter 'length' was called on null. Receiver: null Tried calling: length FutureBuilder with List

I keep getting the error mentioned above during runtime of my flutter app. Basically what I am trying to achieve is to fetch data from an api and display it in a form of a SliverList by using a FutureBuilder.
This was working perfectly fine until I changed my code for the list from FutureBuilder<List> to FutureBuilder<List> to make use of the class EntertainerEvent which has all the fields I need for display from the json file.
How can I resolve this because it seems like the contructor or the application itslef is not picking up the data when I make use of a custom class.
This is the code for the EntertainerEvent class:
class EntertainerEvent {
final int eventId;
final int entertainerId;
final int eventTypeId;
final int categoryId;
final String eventName;
final String description;
final String imagePoster;
final String location;
final DateTime startDate;
final DateTime endDate;
final double entreeFee;
const EntertainerEvent({required this.eventId, required this.entertainerId, required this.eventTypeId,
required this.categoryId, required this.eventName, required this.description, required this.imagePoster,
required this.location, required this.startDate, required this.endDate, required this.entreeFee});
factory EntertainerEvent.fromJson(Map<String, dynamic> event) {
return EntertainerEvent(
eventId: event['EventID'],
entertainerId: event['EntertainerID'],
eventTypeId: event['EventTypeID'],
categoryId: event['CategoryID'],
eventName: event['EventName'],
description: event['Description'],
imagePoster: event['ImagePoster'],
location: event['Location'],
startDate: event['StartDate'],
endDate: event['EndDate'],
entreeFee: event['EntryFee'],
);
}
}
Below is the code for fetching data from the api:
Future<List<EntertainerEvent>> fetchEvents() async {
var result = await http.get(Uri.parse(apiUrl));
if (result.statusCode == 200) {
var content = result.body;
var arr = json.decode(content) as List;
return arr.map((eve) => new EntertainerEvent.fromJson(eve)).toList();
} else {
print('Not loaded');
throw Exception('Unable to fetch data from the Rest API');
}
}
late Future<List<EntertainerEvent>> _fetchEvents;
#override
void initState() {
_fetchEvents = fetchEvents();
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<EntertainerEvent>>(
future: _fetchEvents,
builder: (BuildContext context, AsyncSnapshot snapshot) {
var childCount = 0;
if (snapshot.connectionState != ConnectionState.done) {
childCount = 1;
} else {
childCount = snapshot.data.length;
}
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
if (snapshot.hasData) {
List<EntertainerEvent> someData = snapshot.data;
print('data here');
//Do some stuff
}
}, childCount: childCount),
);
});
}
I do not know what exactly is it that I am missing because this code works if I use the type dynamic instead of the custom class EntertainerEvent.
Thank you all in advance!
Wrap it with hasData:
if(snapshot.hasData){
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
List<EntertainerEvent> someData = snapshot.data;
print('data here');
//Do some stuff
}, childCount: childCount),
);}
return CircularProgressIndicator();

Exception: type 'String' is not a subtype of type 'int' in type cast

I am trying to list the users in flutter but i am getting the error that string is not a subtype of type int in type cast. I can't detect what part of the code is the causing the problem.
Model
class User {
final int id;
final String email;
bool auth;
User({this.id, this.email,});
factory User.fromJSON(Map<String, dynamic> jsonMap) {
return User(
id: jsonMap['id'] as int,
email: jsonMap['email'] as String,
);}
Map toMap() {
var map = new Map<String, dynamic>();
map["id"] = id;
map["email"] = email;
return map;}
#override
String toString() {
var map = this.toMap();
map["auth"] = this.auth;
return map.toString();
}}
Actual Part
Future<List<User>> fetchUsers(http.Client client) async {
final response = await http.get("http://a6df36670036.ngrok.io/api/users/?format=json");
return compute(parseUsers, response.body);
}
List<User> parseUsers(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<User>((json) => User.fromJSON(json)).toList();
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
),
body: FutureBuilder<List<User>>(
future: fetchUsers(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? UsersList(users: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
);
}
}
class UsersList extends StatelessWidget {
final List<User> users;
UsersList({Key key, this.users}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return Text(users[index].email);
},
);
}
}
I think it is caused from id part of the model but i am not sure if it is actually id or not.
Is anybody able to help me?
The id from your API is a String so you need to change the id in your model to String
class User {
final String id;
final String email;
bool auth;
User({this.id, this.email,});
factory User.fromJSON(Map<String, dynamic> jsonMap) {
return User(
id: jsonMap['id'] as String,
email: jsonMap['email'] as String,
);}
Map toMap() {
var map = new Map<String, dynamic>();
map["id"] = id;
map["email"] = email;
return map;}
#override
String toString() {
var map = this.toMap();
map["auth"] = this.auth;
return map.toString();
}}
Inside your User Model Class, when you try to get an int value from a JSON file, instead of doing
id: jsonMap['id'] as int,
do it like
id: int.parse(jsonMap['id'])
For your case , the code below should get you the id value as an int
return User(
id: int.parse(jsonMap['id']),
email: jsonMap['email'] as String,
);

An exception was thrown: NoSuchMethodError: The getter 'the0' was called on null

I'm trying to get data from API using Rest Service..
But I got an error saying that the getter was called on null, after doing some research, I found something, like the UI which the data I want to display was executed first than the getData function, it makes the system read the variable as null and the error occurs. Can anybody help me with this case..
Here this is a bit of my codes,
class PickUp extends StatefulWidget {
var created_name, wmsorders_id, id;
PickUp(
{Key key,
#required this.created_name,
#required this.wmsorders_id,
#required this.id})
: super(key: key);
#override
_PickUpState createState() => _PickUpState();
}
class _PickUpState extends State<PickUp> {
DetailModel detailModel;
String sender = "";
Future<String> getDetail() async {
print("id : " + widget.id);
var data = "id=" + widget.id + "";
var response_detail = await RestService()
.restRequestServiceGet(SystemParam.URL_DETAIL_UPCOMING, data);
print("response_detail : " + response_detail.body.toString());
setState(() {
detailModel = DetailModel.fromJson(jsonDecode(response_detail.body));
});
return "Success!";
}
#override
void initState() {
getDetail();
}
Widget build(BuildContext context) {
// NULL CHECKING
if (detailModel != null) {
print("sender =" +detailModel.the0.picName);
} else {
print("sender = null");
}
// I want to get picName from detail Model and using it in UI, but I got Error here
sender = detailModel.the0.picName';
print("sender = " +'$sender');
}
Here is the detailModel
// To parse this JSON data, do
//
// final detailModel = detailModelFromJson(jsonString);
import 'dart:convert';
DetailModel detailModelFromJson(String str) => DetailModel.fromJson(json.decode(str));
String detailModelToJson(DetailModel data) => json.encode(data.toJson());
class DetailModel {
The0 the0;
The0 the1;
Records records;
DetailModel({
this.the0,
this.the1,
this.records,
});
factory DetailModel.fromJson(Map<String, dynamic> json) => DetailModel(
the0: The0.fromJson(json["0"]),
the1: The0.fromJson(json["1"]),
records: Records.fromJson(json["records"]),
);
Map<String, dynamic> toJson() => {
"0": the0.toJson(),
"1": the1.toJson(),
"records": records.toJson(),
};
}
class Records {
int status;
String message;
Records({
this.status,
this.message,
});
factory Records.fromJson(Map<String, dynamic> json) => Records(
status: json["status"],
message: json["message"],
);
Map<String, dynamic> toJson() => {
"status": status,
"message": message,
};
}
class The0 {
String id;
String sku;
int sak;
String qty;
String shipstatId;
String picName;
String picTelp;
String orderMultipleId;
String orderdetId;
String coordinatorId;
The0({
this.id,
this.sku,
this.sak,
this.qty,
this.shipstatId,
this.picName,
this.picTelp,
this.orderMultipleId,
this.orderdetId,
this.coordinatorId,
});
factory The0.fromJson(Map<String, dynamic> json) => The0(
id: json["id"],
sku: json["sku"],
sak: json["sak"],
qty: json["qty"],
shipstatId: json["shipstat_id"],
picName: json["pic_name"],
picTelp: json["pic_telp"],
orderMultipleId: json["order_multiple_id"],
orderdetId: json["orderdet_id"],
coordinatorId: json["coordinator_id"],
);
Map<String, dynamic> toJson() => {
"id": id,
"sku": sku,
"sak": sak,
"qty": qty,
"shipstat_id": shipstatId,
"pic_name": picName,
"pic_telp": picTelp,
"order_multiple_id": orderMultipleId,
"orderdet_id": orderdetId,
"coordinator_id": coordinatorId,
};
}
And here is the Error,
You need to use FutureBuilder in build method and wait until response.
Remove setstate and Modify the code as below.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Test',
style: TextStyle(
color: Colors.white,
),
),
),
body: FutureBuilder<DetailModel>(
future: getDetail(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print("Here you can get data "+snapshot.data.toString());
} else {
print("Waiting mode");
return Container(
color: Colors.blue,
);
}
},
),
);
}
}

Can't access values from redux state event hough I have an instance of the class

I have some state that is added during some middleware. This state is used to build ListTiles for a ListView. I cannot access the properties of this instance when I map over the instance.
I can see the info in the debugger: https://imgur.com/a/YTpjBou
But I cannot access the property because it returns null. I am unsure if this is because the future has not completed by the time it renders or what.
Here is the build for the home_widget
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:nasp_portal_app/model/model.dart';
import 'main_drawer.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Image.asset('lib/images/Logo.png', height: 35),
),
drawer: DrawerOnly(),
body: StoreConnector<AppState, _ViewModel>(
converter: (Store<AppState> store) => _ViewModel.create(store),
builder: (BuildContext context, _ViewModel viewModel) => Column(
children: <Widget>[Expanded(child: ItemListWidget(viewModel))],
),
),
);
}
}
class ItemListWidget extends StatelessWidget {
final _ViewModel model;
ItemListWidget(this.model);
#override
Widget build(BuildContext context) {
return ListView(
children: model.tournaments.map((Tournament tournament) {
return ListTile(
title: Text(tournament.tournName ?? 'Test'),
leading: IconButton(
icon: Icon(Icons.home),
onPressed: () => print('go to tourney'),
));
}).toList(),
);
}
}
class _ViewModel {
final List<Tournament> tournaments;
_ViewModel({this.tournaments});
factory _ViewModel.create(Store<AppState> store) {
print(store.state.tournaments.length);
return _ViewModel(tournaments: store.state.tournaments);
}
}
Here is the class definition of a Tournament
class Tournament {
final String tournName;
final String tournState;
final String tournCity;
final double distanceMiles;
final int startDate;
final int endDate;
final int tID;
Tournament({
#required this.tournName,
#required this.tournState,
#required this.tournCity,
#required this.distanceMiles,
#required this.startDate,
#required this.endDate,
#required this.tID,
});
Tournament copyWith({
String tournName,
String tournState,
String tournCity,
double distanceMiles,
int startDate,
int endDate,
int tID,
}) {
return Tournament(
tournName: tournName ?? this.tournName,
tournState: tournState ?? this.tournState,
tournCity: tournCity ?? this.tournCity,
distanceMiles: distanceMiles ?? this.distanceMiles,
startDate: startDate ?? this.startDate,
endDate: endDate ?? this.endDate,
tID: tID ?? this.tID,
);
}
}
This is my redux middleware handling the async task
class NearTournamentsMiddleware extends MiddlewareClass<AppState> {
#override
void call(Store<AppState> store, dynamic action, NextDispatcher next) {
if (action is NearTournamentsAction) {
checkNearTournaments(next);
}
next(action);
}
void checkNearTournaments(NextDispatcher next) async {
final tournaments = await _tournamentsInRange();
for (final tournament in tournaments) {
next(AddTournamentsAction(
tournament['TournName'],
tournament['TID'],
tournament['TournState'],
tournament['TournCity'],
tournament['Distance_Miles'],
tournament['Start_Date'],
tournament['End_Date']));
}
}
_tournamentsInRange() async {
Map currentLocation = <String, double>{};
var location = Location();
try {
currentLocation = await location.getLocation();
final response = await _checkLocalTournaments(
currentLocation["latitude"], currentLocation["longitude"]);
final decoded = jsonDecode(response.body);
return decoded;
} on PlatformException {
currentLocation = null;
}
}
Future<http.Response> _checkLocalTournaments(lat, lng) async {
var url = 'https://napi.com';
var body = json.encode({
'miles': '-1', // -1 for test api
'lat': lat,
'lng': lng
});
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
final response = await http.post(url, body: body, headers: headers);
return response;
}
}
These are my current reducers
import 'package:nasp_portal_app/model/model.dart';
import 'package:nasp_portal_app/redux/actions.dart';
AppState appStateReducer(AppState state, action) {
return AppState(tournaments: tournamentReducer(state.tournaments, action));
}
List<Tournament> tournamentReducer(List<Tournament> state, action) {
if (action is AddTournamentsAction) {
return []
..addAll(state)
..add(Tournament(
tournName: action.tournName,
tournState: action.tournState,
tournCity: action.tournCity,
distanceMiles: action.distanceMiles,
startDate: action.startDate,
endDate: action.endDate,
tID: action.tID));
}
return state;
}
How can I properly access the values in the map in my screenshot? I know I have an instanced based on the debugger but cannot get its properties.
My issue was with the redux action that I was using called AddTournamentsAction
I was not using this to refer to the class variables in its constructor like so:
class AddTournamentsAction {
final String tournName;
final String tournState;
final String tournCity;
final double distanceMiles;
final int startDate;
final int endDate;
final int tID;
AddTournamentsAction(
tournName,
tournState,
tournCity,
distanceMiles,
startDate,
endDate,
tID,
);
}
To fix this I simply had to add the this keyword:
class AddTournamentsAction {
final String tournName;
final String tournState;
final String tournCity;
final double distanceMiles;
final int startDate;
final int endDate;
final int tID;
AddTournamentsAction(
this.tournName,
this.tournState,
this.tournCity,
this.distanceMiles,
this.startDate,
this.endDate,
this.tID,
);
}