How to display variable from json return in text - dart

String empName;
Future<List> getUserData() async{
final response = await http.post("http://172.16.161.34:8080/ebs/cfs/android_test_app/accessfile.php?q=getUserData",body:{
"emp_id": widget.empId,
});
var dataUser = jsonDecode(response.body);
empName = dataUser[0]['name'];
return null;
}
How to display the variable "empName" in line 2 to line 70 "child: Text('')"
Full code on Pastebin

Try this way.. make pojo class for response data like this way..
class UserData {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
UserData({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory UserData.fromJson(Map<String, dynamic> json) {
return new UserData(
albumId: json['albumId'],
id: json['id'],
title: json['title'],
url: json['url'],
thumbnailUrl: json['thumbnailUrl']);
}
}
make method for api call..
Future<UserData> fetchData() async {
var result = await get('https://jsonplaceholder.typicode.com/photos');
if (result.statusCode == 200) {
return UserData.fromJson(json.decode(result.body));
} else {
// If that response was not OK, throw an error.
throw Exception('Failed to load post');
}
}
after that make global object that fetch data..
Future<UserData> userDataList;
on Button click ..
userDataList = fetchData();
after that you want to print data..
userDataList.then((userData){
print(userData.title);
});

First of all you getUserData() function never returns anything. It seems like you only need the name so this function could look like this:
Future<String> getUserData() async{
final response = await http.post("http://172.16.161.34:8080/ebs/cfs/android_test_app/accessfile.php?q=getUserData",body:{
"emp_id": widget.empId,
});
var dataUser = jsonDecode(response.body);
return dataUser[0]['name'];
}
Then to set the empName variable you should use setState().
So change your afterFirstLayout() method to this:
#override
void afterFirstLayout(BuildContext context) async {
// Calling the same function "after layout" to resolve the issue.
getUserData().then( (userName) {
setState(() {
empName = userName;
});
});
}
Also you seem to want to reload the name once you press the IconButton.
So you might want to override your code with this:
IconButton(icon: Icon(Icons.shopping_cart),
onPressed:() {
getUserData().then( (userName) {
setState(() {
empName = userName;
});
});
},
),

Related

type 'Null' is not a subtype of type 'Future<DataState<List<CustomerEntity>>>

I'm working on a test CRUD application using Bloc for state management and sqflite database. I was asked to implement BDD testing for the app but I have no idea about BDD testing. From what I've learned so far I tryed to implement a simple scenario for the start which is landing on the home screen, but I'm getting this error when I run the test.
Also I don't know exactly how to mock my database to test the four main Create, Read, Update, and Delete functionalities.
I'm using getIt for dependency injection, Mocktail, and bdd_widget_test.
It is the scenario that I wrote in the .feature file:
Feature: Add Feature
Scenario: Landing on the home screen
Given the app is running
Then I see enabled elevated button
And it's the logic for the test.dart file where I get the mentioned exception:
class MockGetAllCustomers extends Mock implements GetAllCustomersUsecase {}
class MockAddCustomer extends Mock implements AddCustomerUsecase {}
class MockUpdateCustomer extends Mock implements UpdateCustomerUsecase {}
class MockDeleteCustomer extends Mock implements DeleteCustomerUsecase {}
class MockDbHelper extends Mock implements DBHelper {}
void main() {
final GetIt getIt = GetIt.instance;
late CustomersBloc bloc;
late MockGetAllCustomers mockGetAllCustomers;
late MockAddCustomer mockAddCustomer;
late MockUpdateCustomer mockUpdateCustomer;
late MockDeleteCustomer mockDeleteCustomer;
late MockDbHelper dbHelper;
late Database database;
CustomerEntity customer = CustomerEntity(
firstName: 'firstName',
lastName: 'lastName',
dateOfBirth: 'dateOfBirth',
phoneNumber: 'phoneNumber',
email: 'email',
bankAccountNumber: 'bankAccountNumber');
setUpAll(() async {
// Initialize FFI
sqfliteFfiInit();
database = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath);
await database.execute(
'CREATE TABLE $customersTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colFirstName TEXT, $colLastName TEXT, $colDateOfBirth TEXT, $colPhoneNumber TEXT, $colEmail TEXT, $colAccountNum TEXT)');
dbHelper = MockDbHelper();
dbHelper.database = database;
databaseFactory = databaseFactoryFfi;
});
setUp(() {
mockGetAllCustomers = MockGetAllCustomers();
mockAddCustomer = MockAddCustomer();
mockUpdateCustomer = MockUpdateCustomer();
mockDeleteCustomer = MockDeleteCustomer();
bloc = CustomersBloc(mockGetAllCustomers, mockAddCustomer,
mockUpdateCustomer, mockDeleteCustomer);
getIt.registerFactory(() => bloc);
});
group('''Add Feature''', () {
testWidgets('''Landing on the home screen''', (tester) async {
await theAppIsRunning(tester);
await iSeeEnabledElevatedButton(tester);
});
});
}
It's a part of the exception message I'm receiving:
type 'Null' is not a subtype of type 'Future<DataState<List<CustomerEntity>>>'
package:mc_crud_test/features/customer_feature/domain/usecases/get_all_customers_usecase.dart 11:43 MockGetAllCustomers.execute
package:mc_crud_test/features/customer_feature/presentation/bloc/bloc/customers_bloc.dart 43:60 new CustomersBloc.<fn>
package:bloc/src/bloc.dart 226:26 Bloc.on.<fn>.handleEvent
package:bloc/src/bloc.dart 235:9 Bloc.on.<fn>
dart:async
And it's my database class:
String customersTable = 'customers_table';
String colId = 'id';
String colFirstName = 'firstName';
String colLastName = 'lastName';
String colDateOfBirth = 'dateOfBirth';
String colPhoneNumber = 'phoneNumber';
String colEmail = 'email';
String colAccountNum = 'bankAccountNumber';
//
class DBHelper {
//
Database database;
DBHelper({required this.database});
//Database initialization and creation
static Future<Database> initDatabase() async {
//
final dbPath = await sql.getDatabasesPath();
return await sql.openDatabase(
path.join(dbPath, 'customers.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE $customersTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colFirstName TEXT, $colLastName TEXT, $colDateOfBirth TEXT, $colPhoneNumber TEXT, $colEmail TEXT, $colAccountNum TEXT)');
},
version: 1,
);
}
//
//find a customer by firstName, lastName, dateOfBirth, and email
Future<bool> findCustomer(CustomerEntity customer) async {
//
List<Map<String, dynamic>> map = await database.query(customersTable,
columns: [colFirstName, colLastName, colDateOfBirth, colEmail],
where:
'$colFirstName = ? OR $colLastName = ? OR $colDateOfBirth = ? OR $colEmail = ?',
whereArgs: [
customer.firstName,
customer.lastName,
customer.dateOfBirth,
customer.email
]);
if (map.isEmpty) {
return false;
} else {
return true;
}
}
// Add a customer to the Database
Future<int> insertCustomer(CustomerEntity customer) async {
//
final id = await database.insert(
customersTable,
customer.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
return id;
}
// Get the list of customers
Future<List<CustomerEntity>> getAllCustomers() async {
//
try {
final List<Map<String, Object?>> queryResult = await database.query(
customersTable,
orderBy: colId,
);
return queryResult.isEmpty
? []
: queryResult.map((e) => CustomerEntity.fromMapObject(e)).toList();
} catch (e) {
print('Error reading database : $e');
return [];
}
}
//
// Delete a Customer
Future<int> deleteCustomer(int id) async {
return await database
.delete(customersTable, where: '$colId = ?', whereArgs: [id]);
}
//
//Update a Customer
Future<bool> updateCustomer(CustomerEntity customer) async {
try {
final count = await database.update(customersTable, customer.toMap(),
where: '$colId = ?', whereArgs: [customer.id]);
if (count == 1) {
return true;
} else {
return false;
}
} catch (e) {
print('Failed to update the customer: $e');
return false;
}
}
}

After web request, navigate to other view (async /await proper usage) - Flutter , dart

I want to fetch data from the server, then parse json. When they are done, I want to navigate to another view.
void getServerData() async{
WebRequests ws = WebRequests('https://sampleurl');
Map<String, dynamic> map = await ws.getData();
mychamplist = map['mychampionships'];
mychamplist.forEach((f){
mychampionships.add(MyChampionships(
name: f['name'],
id: int.parse(f['id']),
numberOfPlayers: int.parse(f['nofplayers']),
));
});
Navigator
.of(context)
.pushReplacement(new MaterialPageRoute(builder: (BuildContext context) {
return FantasyNbi();
}));
}
It navigates to the FantasyNbi class before the previous code finished.
How could it do in proper way?
I do have a example class for you that you could use:
class API {
static Future getData(String url) {
return http.get('api link' + url);
}
static Future<List<BasicDiskInfo>> fetchAllDisks() async {
final response = await getData('disk');
if (response.statusCode == 200) {
Iterable list = json.decode(response.body);
List<BasicDiskInfo> disks =
list.map((model) => BasicDiskInfo.fromJson(model)).toList();
return disks;
} else {
throw Exception('Failed to load disks');
}
}
static Future<Disk> fetchDisk(int id) async {
final response = await getData('disk/' + id.toString());
if (response.statusCode == 200) {
return Disk.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load disk');
}
}
}
class Disk {
int id;
String name;
String volumeLable;
bool isReady;
String driveType;
String driveFormat;
int totalSize;
int totalFreeSpace;
int availableFreeSpace;
Disk(
{this.id,
this.name,
this.volumeLable,
this.isReady,
this.driveType,
this.driveFormat,
this.totalSize,
this.totalFreeSpace,
this.availableFreeSpace});
factory Disk.fromJson(Map<String, dynamic> json) {
return Disk(
id: json['id'],
name: json['name'],
volumeLable: json['volumeLable'],
isReady: json['isReady'],
driveType: json['driveType'],
driveFormat: json['driveFormat'],
totalSize: json['totalSize'],
totalFreeSpace: json['totalFreeSpace'],
availableFreeSpace: json['availableFreeSpace']);
}
}
And to get the data I can do this:
var data = await API.fetchAllDisks();
// or
API.fetchAllDisks().then((response) => {/* do something */})

only static members can be accessed in initializers

I have an int value passed from the first screen to the second screen and I can have my value without any problem...My problem is that I want to add my recieved value to astring to complete and start working with my api which reqires to add the imported value of the first screen...Iam trapped between the recieved int which I can not change it to a static, and the api string which reqires the added value to be a static
the second Screen:
class CatsNews extends StatefulWidget {
#override
_CatsNewsState createState() => new _CatsNewsState();
}
class _CatsNewsState extends State<CatsNews> {
int _id ;
String root = "http://api.0mr.net";
String url = "/api/GetCategoryById/$_id";
#override
List data;
Future<String> getDrawerItem() async {
var response = await http.get(
Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Cat Screen'),
),
body: Center()
I get the value through the shared preferences and it works fine if it was inside Widget build(BuildContext context)
#override
void initState() {
getIntPrefrence().then(updatId);
super.initState();
this.getDrawerItem();
}
void updatId(int id) {
setState(() {
this._id = id;
});
}
}
UPDATE:
I have added the Srting url to the initstate() and the code is as foloows:
class _CatsNewsState extends State<CatsNews> {
#override
List data;
int _id ;
var response;
String root = "http://-api.0mr.net";
String url ;
Future<String> getDrawerItem() async {
response = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$_id'),
),
body: Center()
}
Future<int> getIntPrefrence() async {
SharedPreferences pref = await SharedPreferences.getInstance();
int id = pref.getInt("id");
return id;
}
#override
void initState() {
super.initState();
getIntPrefrence().then(updatId);
updateurl();
this.getDrawerItem();
//initResponse();
}
updateurl() {
setState(() {
this.url= "$root/api/CategoryId/$_id";
});
}
void updatId(int id) {
setState(() {
this._id = id;
});
}
}
the previous issue was solved by adiing the String to the initstate(), but the updated values does not add to the String and deals witht the imported int id as null or zero,however the int id works fine and shows the imported value in any widget inside the Widget build(BuildContext context)
The offending code is probably
var response = await http.get(...
You can't have arbitrary code in a field initializer which is the part after = in above code.
Dart has a strict order in object creation steps.
Field initializers are executed before the constructor initialization list and before the constructor initialization list of super classes.
Only after all the constructor initialization lists of all super classes are executed, the constructor bodies are executed and only from then on is it allowed to access this, the instance of your class being created.
Your code accesses this (implicitly) before that and at this point object initialization isn't done and therefore access to this prohibited to prevent undefined bahavior.
Static members are safe to access, because they don't depend on object initialization. They are ready to use without an instance entirely.
What the error message is telling you is that your initializer is trying to do things that are not possible at this point and you need to move the code somewhere else.
The field initialization code can be moved to the constructor initialization list. This is usually done if the field is supposed to be final,
or to the constructor body, or to a method - as shown below.
var response;
#override
void initState() {
super.initState();
_initResponse();
}
void _initResponse() async {
response = await http.get(
Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Because initState does not allow async we move the code to another method (_initResponse) that we call from initState.
the solution of my problem was adding both the imported value and the String which I want to edit according to it inside the method which will start my api request as follows:
void initState() {
super.initState();
this.getDrawerItem();
}
Future<String> getDrawerItem() async {
int _id ;
String url;
SharedPreferences pref = await
SharedPreferences.getInstance();
_id = (pref.getInt("id")?? 0);
url = "http://gomhuriaonline-api.0mr."
"net/api/GetCategoryById/$_id";
print(url);
response = await http
.get(Uri.encodeFull(url), headers:
{"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
}

Flutter Sqflite error says The method 'query' was called on null

I am trying to fetch some data from network and store it in sqlite database. Following is the model class
class StudentModel {
int status;
String msg;
StudentModelData studentModelData;
StudentModel({this.status, this.msg, this.studentModelData});
StudentModel.fromJson(Map<String, dynamic> json) {
status = json['status'];
msg = json['msg'];
studentModelData = json['data'] != null ? new StudentModelData.fromJson(json['data']) : null;
}
StudentModel.fromDb(Map<String, dynamic> parsedJson) {
status = parsedJson['status'];
msg = parsedJson['msg'];
studentModelData = studentModelData = jsonDecode(json['data']) != null ? new StudentModelData.fromJson(jsonDecode(json['data'])) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['msg'] = this.msg;
if (this.studentModelData != null) {
data['data'] = this.studentModelData.toJson();
}
return data;
}
}
class StudentModelData {
int lastIndex;
List<StudentData> studentData;
StudentModelData({this.lastIndex, this.studentData});
StudentModelData.fromJson(Map<String, dynamic> json) {
lastIndex = json['lastIndex'];
if (json['studentData'] != null) {
studentData = new List<StudentData>();
json['studentData'].forEach((v) {
studentData.add(new StudentData.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['lastIndex'] = this.lastIndex;
if (this.studentData != null) {
data['studentData'] = this.studentData.map((v) => v.toJson()).toList();
}
return data;
}
}
class StudentData {
String studentId;
String studName;
String studProfilepic;
String studentEmail;
String studentMobile;
String courseName;
String classCode;
int minAvg;
int avg;
StudentData(
{this.studentId,
this.studName,
this.studProfilepic,
this.studentEmail,
this.studentMobile,
this.courseName,
this.classCode,
this.minAvg,
this.avg});
StudentData.fromJson(Map<String, dynamic> json) {
studentId = json['student_id'];
studName = json['stud_name'];
studProfilepic = json['stud_profilepic'];
studentEmail = json['student_email'];
studentMobile = json['student_mobile'];
courseName = json['course_name'];
classCode = json['class_code'];
minAvg = json['minAvg'];
avg = json['avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['student_id'] = this.studentId;
data['stud_name'] = this.studName;
data['stud_profilepic'] = this.studProfilepic;
data['student_email'] = this.studentEmail;
data['student_mobile'] = this.studentMobile;
data['course_name'] = this.courseName;
data['class_code'] = this.classCode;
data['minAvg'] = this.minAvg;
data['avg'] = this.avg;
return data;
}
}
And my database provider class looks like following
class StudentDbProvider implements Source, Cache {
Database db;
StudentDbProvider() {
init();
}
void init() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, "students.db");
db = await openDatabase(path, version: 1,
onCreate: (Database newDb, int version) {
newDb.execute("""
CREATE TABLE STUDENTS(
id INTEGER PRIMARY KEY,
status INTEGER,
msg TEXT,
data BLOB
)
""");
});
}
#override
Future<int> clear() {
return db.delete("STUDENTS");
}
#override
Future<StudentModel> fetchStudents(String disciplineId, String schoolId,
String year_id, String lastIndex) async {
final maps =
await db.query("STUDENTS");
if (maps.length > 0) {
return StudentModel.fromDb(maps.first);
}
return null;
}
#override
Future<int> addStudent(StudentModel studentModel) {
return db.insert("STUDENTS", studentModel.toJson(),conflictAlgorithm: ConflictAlgorithm.ignore);
}
}
final studentDbProvider = StudentDbProvider();
Whenever I tried to fetch the data and stored in the database, I get the following error in the console
NoSuchMethodError: The method 'query' was called on null.
Receiver: null
Tried calling: query("STUDENTS")
#0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
The data gets added to the database but I am not able to query the data from the database.
Reducing at minimum your example, this throws the exception The method 'query' was called on null
because fetch is executed before db is properly initialized:
class Database {
Future<int> query() {
return Future.value(1);
}
}
const oneSecond = Duration(seconds: 1);
class Provider {
Database db;
Provider() {
init();
}
void init() async {
db = await Future.delayed(oneSecond, () => Database());
}
Future<int> fetch() {
return db.query();
}
}
main() async {
var provider = Provider();
await provider.fetch();
}
The problem resides in calling an async method inside a constructor, see also:
Calling an async method from component constructor in Dart
This works:
class Database {
Future<int> query() {
return Future.value(1);
}
}
const oneSecond = Duration(seconds: 1);
class Provider {
Database db;
Provider() {
//init();
}
void init() async {
db = await Future.delayed(oneSecond, () => Database());
}
Future<int> fetch() {
return db.query();
}
}
main() async {
var provider = Provider();
await provider.init();
await provider.fetch();
}
Please note that init must be awaited, otherwise you will catch the same The method 'query' was called on null.
the problem is the init must be awaited. here what I did to fix it
_onCreate(Database db, int version) async {
await db.execute('CREATE TABLE ... <YOUR QUERY CREATION GOES HERE>');
}
Future<Database> getDatabaseInstance() async {
final String databasesPath = await getDatabasesPath();
final String path = join(databasesPath, '<YOUR DB NAME>');
return await openDatabase(path, version: 1, onCreate: _onCreate);
}
Future<int> save(Contact contact) {
return getDatabaseInstance().then((db) {
final Map<String, dynamic> contactMap = Map();
contactMap['name'] = contact.name;
contactMap['account_number'] = contact.accountNumber;
return db.insert('contacts', contactMap);
});
}
The SQFlite page gives a good example about it and helps a lot.
https://github.com/tekartik/sqflite/blob/master/sqflite/doc/opening_db.md

Flutter and Firebase: How can i receive data from function?

i use the following function to fetch userData from Firestore:
Future<String>getRegisterUserData({String userID}) async {
Firestore.instance.collection("Users").document(userID).get().then(
(datasnapshot) {
if (datasnapshot.exists) {
return datasnapshot.data['Email'];
} else {
return "Loading...";
}
},
);
}
I execute this function on my UserProfilePage like this:
_email = widget.firestore.getRegisterUserData(widget.userID).toString();
But i always get the print statement: Instance of 'Future' and not the saved email-address...
i also try this:
Future<String> getRegisterUserData({String userID}) async {
String email;
Firestore.instance.collection("Users").document(userID).get().then(
(datasnapshot) {
if (datasnapshot.exists) {
email = datasnapshot.data['Email'];
} else {
email = "Loading...";
}
},
);
return email;
}
The Print Statement is always the same...
So where is my mistake? I want to display the Value of 'Email' on the UserProfilePage in a variable, or is my firestore function incorrect?
Thank you for our help
Add await keyword. But at a different place.
tempEmail = await widget.firestore.getRegisterUserData(widget.userID);
setState(() {
_email = tempEmail;
});
// we don't need toString as getRegisterUserData itself is returning Future<String>
Note for using await: As we are using await the method/function which contains this should have a async in its signature.
Or you can use then block
widget.firestore.getRegisterUserData(widget.userID).then((email) {
setState(() {
_email = email;
});
});
Explanation: widget.firestore.getRegisterUserData(widget.userID) is of type Future<String>. that's why it is printed as Instance of 'Future'. We have to convert the Future to String by await or by then block
Using SharedPreferences:
Future<String> getEmail() async {
final prefs = await SharedPreferences.getInstance();
String email = prefs.getString('email');
if (email != null) {
return email;
} else {
email = await widget.firestore.getRegisterUserData(widget.userID);
prefs.setString('email', email); //update shared preferences
return email;
}
}
// usage (may be in initState)
widget.getEmail().then((email) {
setState(() {
_email = email;
})
})
Updated
Based on your information, you need a FutureBuilder in order to wait the response to build your widget:
return FutureBuilder(
future: getRegisterUserData(userID: "1234"),
builder: (context, asyncsnapshot){
return asyncsnapshot.hasData && asyncsnapshot.data != null ? TextFormField(
initialValue: asyncsnapshot.data,
) : CircularProgressIndicator();
}
);

Resources