findById returns null on hot reload - flutter

I have the following dart class which loads an object and an image. When I perform hot reload, the function findById returns an error message Bad State: No element. Upon debugging, I see that the list that I am querying on is null once I do a hot realod. I want to know why that happens and how do I fix this?
class ProductDetailScreen extends StatefulWidget {
static const routeName = '/product-detail';
#override
_ProductDetailScreenState createState() => _ProductDetailScreenState();
}
class _ProductDetailScreenState extends State<ProductDetailScreen> {
var loadedProduct;
var productId;
var _isLoading;
var _imageUrl;
var cartId;
var imagePath = '';
#override
void initState() {
super.initState();
createCartId();
}
Future<void> createCartId()async {
var id = await nanoid(10);
cartId = id;
}
Future<void> didChangeDependencies() async {
productId = ModalRoute.of(context).settings.arguments as String;
imagePath = productId;
setState(() {
_isLoading = true;
});
final myCacheManager = MyCacheManager();
await myCacheManager.cacheImage(imagePath).then((String imageUrl) {
setState(() {
_imageUrl = imageUrl;
_isLoading = false;
});
});
}
#override
Widget build(BuildContext context) {
loadedProduct = Provider.of<Products>(context,listen:false).findById(productId);
Size size = MediaQuery.of(context).size;
var userId = Provider.of<Auth>(context, listen: false).userId;
return !(_isLoading)?Scaffold(
appBar: AppBar(
actions: null,
title: Text(loadedProduct.title),
),
body:
CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child:
Column(
children: <Widget>[
Container(
height: size.height * 0.35,
width: double.infinity,
child:_imageUrl != 'No picture loaded'?
CachedNetworkImage(
imageUrl: _imageUrl,
progressIndicatorBuilder: (context, url, downloadProgress) =>
Center(child:Loading()),
errorWidget: (context, url, error) => Image.asset('assets/images/placeholder.png'),
)
:Image.asset('assets/images/placeholder.png'),
),
Expanded(
child: ItemInfo(
cartId,
loadedProduct.id,
loadedProduct.title,
loadedProduct.price,
loadedProduct.description,
loadedProduct.categoryName,
loadedProduct.isAvailable),
),
],
))]))
:Center(child:Loading());
}
}
class ItemInfo extends StatefulWidget {
ItemInfo(this.cartId,this.id,this.name,this.price,this.description,this.category,this.isAvailable);
var id;
var cartId;
var name;
var price;
var description;
var category;
var isAvailable;
#override
_ItemInfoState createState() => _ItemInfoState();
}
class _ItemInfoState extends State<ItemInfo> {
bool changePrice = false;
List<String> selectedItems;
#override
Widget build(BuildContext context) {
return
Container(
padding: EdgeInsets.all(15),
width: double.infinity,
decoration: BoxDecoration(
boxShadow: [BoxShadow(color: Colors.orange, spreadRadius: 1)],
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child:
Column(
children: <Widget>[
TitlePriceRating(
name: widget.name,
price: widget.price,
),
],
),
);
}
}
Here's the function function findById
Product findById(String id) {
return _items.firstWhere((prod) => prod.id == id);
}
Here's the function which sets the _items list:
Future<void> fetchAndSetProducts(String title) async {
var encodedTitle = Uri.encodeComponent(title);
var url = 'https://mm-nn-default-rtdb.firebaseio.com/products.json?auth=$authToken';
try {
final response = await http.get(Uri.parse(url));
final extractedData = json.decode(response.body) as Map<String, dynamic>;
final List<Product> loadedProducts = [];
if (extractedData == null || extractedData['error'] != null) {
_items = loadedProducts;
return _items;
}
extractedData.forEach((prodId, prodData) {
loadedProducts.add(Product(
id: prodId,
title: prodData['title'],
description: prodData['description'],
price: prodData['price'],
categoryName: prodData['categoryName'],
));
});
notifyListeners();
_items = loadedProducts;
return _items;
} catch (error) {
throw (error);
}
}
This function is called in a class called ProductOverviewScreen which then calls a class called ProductGrid. ProductGrid basically contains a listviewbuilder of the products. This class then calls ProductItem which is basically a single product in the product list. ProductItem contains a button which when clicked, calls ProductDetailsScreen which is the class causing the error.

Use setState method.
Future<void> didChangeDependencies() async {
setState(() {
productId = ModalRoute.of(context).settings.arguments as String;
imagePath = productId;
_isLoading = true;
});
final myCacheManager = MyCacheManager();
await myCacheManager.cacheImage(imagePath).then((String imageUrl) {
setState(() {
_imageUrl = imageUrl;
_isLoading = false;
});
});
}

replace
return _items.firstWhere((prod) => prod.id == id);
with
return _items.firstWhere((prod) => prod.id == id, orElse: () => null,);

The problem is in this code.
extractedData.forEach((prodId, prodData) {
loadedProducts.add(Product(
id: prodId,
title: prodData['title'],
description: prodData['description'],
price: prodData['price'],
categoryName: prodData['categoryName'],
));
});
In this code, you are directly adding the product to the state. The state is immutable. So make a temporary list there add all the data into the temporary list and finally use setState() to put the temporary list data into the state. The code should be like this.
List<Product> tempList = [];
extractedData.forEach((prodId, prodData) {
tempList.add(Product(
id: prodId,
title: prodData['title'],
description: prodData['description'],
price: prodData['price'],
categoryName: prodData['categoryName'],
));
});
setState((){
loadedProducts = tempList;
})

Related

How to fetch data API to dropdown

how do I call the API data into the dropdown I have done several times, when I used local data in the form of a String list I managed to call the data, but when I tried to call from the API data the result was empty and the results in the dropdown did not display any data
Future<List<Agama>> getAgama() async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
final response = await http.get(
Uri.parse(
'$url/auth/mhs_siakad/dosen',
),
headers: {
'Authorization': 'Bearer $token',
},
);
print(response.statusCode);
print(response.body);
if (response.statusCode == 200) {
final result =
json.decode(response.body)['nm_agama'] as Map<String, dynamic>;
UserBiodata agama = UserBiodata.fromJson(result);
List<Agama> l = agama.data ?? [];
return l;
} else {
throw Exception();
}
}
in widget
List<Agama>? religion = [];
#override
void initState() {
super.initState();
BiodataProvider().getAgama();
}
var dropdownAgama;
...
FutureBuilder(
future: BiodataProvider().getBiodata(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print(' ini Agama $religion');
return Column(
children: [
Text(
snapshot.data!.data!.name.toString(),
style: bold5,
),
DropdownButton(
hint: const Text('Religion'),
items: religion!.map((item) {
return DropdownMenuItem(
value: item.nmAgama.toString(),
child: Text(item.idAgama.toString()),
);
}).toList(),
onChanged: (newVal) {
setState(() {
dropdownAgama = newVal;
});
},
value: dropdownAgama,
),
],
);
} else {
return const Text('No Data');
}
}),
...
this is the model i am using which is converted from API
class Agama {
String? id;
String? idAgama;
String? nmAgama;
String? createdAt;
String? updatedAt;
dynamic createdBy;
dynamic updatedBy;
Agama(
{this.id,
this.idAgama,
this.nmAgama,
this.createdAt,
this.updatedAt,
this.createdBy,
this.updatedBy});
Agama.fromJson(Map<String, dynamic> json) {
id = json['id'];
idAgama = json['id_agama'];
nmAgama = json['nm_agama'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
createdBy = json['created_by'];
updatedBy = json['updated_by'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['id_agama'] = idAgama;
data['nm_agama'] = nmAgama;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
data['created_by'] = createdBy;
data['updated_by'] = updatedBy;
return data;
}
}
it looks like you are not assigning your api data to the
List<Agama>? religion;
you can solve this by either mapping your snapshot data directly:
#override
void initState() {
super.initState();
BiodataProvider().getAgama();
}
var dropdownAgama;
...
FutureBuilder(
future: BiodataProvider().getBiodata(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print(' ini Agama $religion');
return Column(
children: [
Text(
snapshot.data!.data!.name.toString(),
style: bold5,
),
DropdownButton(
hint: const Text('Religion'),
items: snapshot.data!.data!.map((item) {
return DropdownMenuItem(
value: item.nmAgama.toString(),
child: Text(item.idAgama.toString()),
);
}).toList(),
onChanged: (newVal) {
setState(() {
dropdownAgama = newVal;
});
},
value: dropdownAgama,
),
],
);
} else {
return const Text('No Data');
}
}),
or
by assigning the snapshot data to your list:
List<Agama>? religion = [];
#override
void initState() {
super.initState();
BiodataProvider().getAgama();
}
var dropdownAgama;
...
FutureBuilder(
future: BiodataProvider().getBiodata(),
builder: (context, snapshot) {
if (snapshot.hasData) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState((){
religion = snapshot.data!.data;
})
});
print(' ini Agama $religion');
return Column(
children: [
Text(
snapshot.data!.data!.name.toString(),
style: bold5,
),
DropdownButton(
hint: const Text('Religion'),
items: religion!.map((item) {
return DropdownMenuItem(
value: item.nmAgama.toString(),
child: Text(item.idAgama.toString()),
);
}).toList(),
onChanged: (newVal) {
setState(() {
dropdownAgama = newVal;
});
},
value: dropdownAgama,
),
],
);
} else {
return const Text('No Data');
}
}),
hope this helps!
Solution
In Controller Class,
Initialize your Future Method as your Model : Future<List<LeaveModel>> getLeaves(){}
create a List of Obj for Custom Class like this :
List<LeaveModel> leaveTypeObjList = [];
After fetching data from API add the datas in obj list via Object
type :LeaveModel leaveTypeObj = LeaveModel(leaveTypeResponseList[i]["LEAVE_TYPE_EN"]);
And in loop add it to your obj list: leaveTypeObjList.add(leaveTypeObj);
** In widget class at AsyncSnapshot use your List of Generics like this :
AsyncSnapshot<List<LeaveModel>>.
Demo Code
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main(List<String> args) {
runApp(MyHome());
}
class MyHome extends StatelessWidget {
const MyHome({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<String> leaveTypeList = [];
int currentLeaveType = 0;
String? value;
String? selectedLeaveType;
late Future<List<LeaveModel>> futureLeaves;
#override
void initState() {
super.initState();
futureLeaves = FoodTypeController().getLeaves();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: Row(
children: [
Text(
"Leave Type = ",
style: TextStyle(fontSize: 24.0),
),
FutureBuilder(
future: futureLeaves,
builder: ((BuildContext context,
AsyncSnapshot<List<LeaveModel>> snapshot) {
if (snapshot.hasData) {
leaveTypeList = [];
List<LeaveModel>? LeaveTypes = snapshot.data;
for (int i = 0; i < LeaveTypes!.length; i++) {
leaveTypeList.add(LeaveTypes[i].leaveTypes!);
}
return SizedBox(
width: 200,
height: 50,
child: Container(
padding: EdgeInsets.only(left: 2.0, right: 4.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.black, width: 1)),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
borderRadius: BorderRadius.circular(12),
value: currentLeaveType == null? null: leaveTypeList[currentLeaveType],
icon: Icon(Icons.arrow_drop_down_rounded),
items: leaveTypeList.map(buildMenuItem).toList(),
onChanged: (value) => setState(() {
currentLeaveType = leaveTypeList.indexOf(value.toString());
this.value = value;
selectedLeaveType = this.value;
}),
),
),
),
);
} else if (snapshot.hasError) {
return const Text("Error to load Snapshot");
} else {
return const CircularProgressIndicator();
}
})),
],
),
),
);
}
DropdownMenuItem<String> buildMenuItem(String item) => DropdownMenuItem(
value: item,
child: Text(
item.toString(),
style: TextStyle(fontWeight: FontWeight.w400, fontSize: 24.0),
),
);
}
class LeaveModel {
String? leaveTypes;
LeaveModel(this.leaveTypes);
}
class FoodTypeController {
Future<List<LeaveModel>> getLeaves() async {
List<LeaveModel> leaveTypeObjList = [];
String url = "";
http.Response response =
await http.post(Uri.parse(url), body: {"user": "", "pass": ""});
if (response.statusCode == 200) {
List leaveTypeResponseList = jsonDecode(response.body);
for (int i = 0; i < leaveTypeResponseList.length; i++) {
LeaveModel leaveTypeObj =
LeaveModel(leaveTypeResponseList[i]["LEAVE_TYPE"]);
leaveTypeObjList.add(leaveTypeObj);
}
return leaveTypeObjList;
} else {
throw Exception("Error to load data in Leave Controller");
}
}
}
Response
Visual

I want to get api data from online and want to put into a list in flutter

fetchData is my function from where I call the API and put the data into an object, which is UserModel Somehow, it is working perfectly. But I want to put my data into a list because I want to make a search function where I can search by name. Look into my code, which will help you to understand.
Future<UserModel>? futureUser;
Future<UserModel>? fetchData() async {
final response =
await http.get(Uri.parse('https://reqres.in/api/users?page=2'));
print('This is Response: $response');
if (response.statusCode == 200) {
// this is a way which I've tried already and it works
// return UserModel.fromJson(jsonDecode(response.body));
} else {
return throw Exception('Failed to load album');
}
}
But I want to put the data into a list and make the search available. Like if i put some name like r+a+b+b+i, it will show the matching name from the API.
I have tried this but I am not clear about the consepet. I am not familiar with how to manipulate the JSON data in a list or object or how to convert an object into a list.
List<UserModel>? userList = [];
Future<UserModel>? fetchData() async {
final response =
await http.get(Uri.parse('https://reqres.in/api/users?page=2'));
print('This is Response: $response');
if (response.statusCode == 200) {
// var result= UserModel.fromJson(jsonDecode(response.body));
// print('this is result $userList');
return userList.add(UserModel.fromJson(jsonDecode(response.body)));
// this is an way which i tried already and its works
// return UserModel.fromJson(jsonDecode(response.body));
} else {
return throw Exception('Failed to load album');
}
}
This is my whole code
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:learning_1ui_6228/utilities/app_colors.dart';
import 'package:learning_1ui_6228/utilities/helper.dart';
import 'package:learning_1ui_6228/utilities/widgets/app_line.dart';
import 'package:learning_1ui_6228/utilities/widgets/list_tile_widget.dart';
import 'package:learning_1ui_6228/views/nav_pages/profile_page.dart';
import 'package:http/http.dart' as http;
import '../model/UserModel.dart';
class FirstScreen extends StatefulWidget {
const FirstScreen({Key? key}) : super(key: key);
#override
State<FirstScreen> createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreen> {
Future<UserModel>? futureUser;
TextEditingController textController = TextEditingController();
// List<UserModel> userList=[];
#override
void initState() {
// searchedList = userList;
futureUser = fetchData();
super.initState();
}
List<UserModel>? userList = [];
Future<UserModel>? fetchData() async {
final response =
await http.get(Uri.parse('https://reqres.in/api/users?page=2'));
print('This is Response: $response');
if (response.statusCode == 200) {
// var result= UserModel.fromJson(jsonDecode(response.body));
// print('this is result $userList');
return userList.add(UserModel.fromJson(jsonDecode(response.body)));
// this is an way which i tried already and its works
// return UserModel.fromJson(jsonDecode(response.body));
} else {
return throw Exception('Failed to load album');
}
}
List<UserModel> searchedList = [];
void searchUser(String enteredData){
print('entered word + ${enteredData}');
searchedList = [];
for(int i=0; i<userList!.length; i++){
if(userList[i].data![i].firstName!.toLowerCase().contains(enteredData.toLowerCase())){
searchedList.add(userList![i]);
}
}
}
#override
Widget build(BuildContext context) {
//print('user list data + $searchedList');
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xfff8f8fa),
body: Column(
children: [
//1st Section
Container(
height: HelperClass.h250,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: AppColors.gradientColor,
),
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(HelperClass.r10),
bottomLeft: Radius.circular(HelperClass.r10))),
child: Column(
children: <Widget>[
//Text and cross button
Container(
margin: EdgeInsets.only(
left: HelperClass.w10,
right: HelperClass.w10,
top: HelperClass.h20),
height: HelperClass.h50,
// color: Colors.red,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: IconButton(
onPressed: () {},
icon: Icon(
Icons.clear,
color: Colors.white,
size: 30,
))),
Expanded(
child: Container(
margin: EdgeInsets.only(right: 30),
alignment: Alignment.center,
// color: Colors.lightBlueAccent,
child: Text(
'Search',
style: TextStyle(
fontSize: HelperClass.t25,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
),
],
),
),
SizedBox(
height: HelperClass.h25,
),
//Search Bar
Container(
margin: EdgeInsets.only(
left: HelperClass.w10, right: HelperClass.w10),
//color: Colors.amber,
height: HelperClass.h70,
width: double.infinity,
child: TextField(
controller: textController,
onChanged: (name) {
setState(() {
searchUser(name);
});
},
decoration: InputDecoration(
prefix: Icon(
Icons.search,
size: 26,
),
suffix: IconButton(
onPressed: () {
setState(() {
textController.clear();
searchedList = userList;
});
},
icon: Icon(
Icons.clear,
size: 26,
),
),
hintText: 'Search',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(3),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.white,
),
),
),
],
),
),
// List View
Expanded(
child: FutureBuilder<UserModel>(
future: futureUser,
builder: (context, snapshot){
if(snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data!.data!.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfilePage(
userName:snapshot.data!.data![index].firstName??'',
followers: snapshot.data!.data![index].id.toString(),
address: snapshot.data!.data![index].email.toString(),
following: snapshot.data!.data![index].lastName.toString(),
imageUrl: snapshot.data!.data![index].avatar.toString(),
),
));
},
child: ListTileWidgets(
following: snapshot.data!.data![index].lastName.toString(),
address: snapshot.data!.data![index].email.toString(),
imageUrl:snapshot.data!.data![index].avatar.toString(),
name: snapshot.data!.data![index].firstName??'',
followersCount:
'Followers: ${snapshot.data!.data![index].id.toString()}',
iconWidget: Icon(
Icons.person_add_alt_outlined,
color: Colors.red,
size: 25,
),
),
),
AppLine(
paddingLeft: 10,
paddingRight: 10,
heightLine: 1,
lineColor: Colors.grey),
],
),
);
},
);
}
else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return Center(child: CircularProgressIndicator());
}),
),
],
),
),
);
}
}
This is my UserModel class
import 'dart:convert';
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel {
UserModel({
this.page,
this.perPage,
this.total,
this.totalPages,
this.data,
this.support,});
UserModel.fromJson(dynamic json) {
page = json['page'];
perPage = json['per_page'];
total = json['total'];
totalPages = json['total_pages'];
if (json['data'] != null) {
data = [];
json['data'].forEach((v) {
data?.add(Data.fromJson(v));
});
}
support = json['support'] != null ? Support.fromJson(json['support']) : null;
}
int? page;
int? perPage;
int? total;
int? totalPages;
List<Data>? data;
Support? support;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['page'] = page;
map['per_page'] = perPage;
map['total'] = total;
map['total_pages'] = totalPages;
if (data != null) {
map['data'] = data?.map((v) => v.toJson()).toList();
}
if (support != null) {
map['support'] = support?.toJson();
}
return map;
}
}
Support supportFromJson(String str) => Support.fromJson(json.decode(str));
String supportToJson(Support data) => json.encode(data.toJson());
class Support {
Support({
this.url,
this.text,});
Support.fromJson(dynamic json) {
url = json['url'];
text = json['text'];
}
String? url;
String? text;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['url'] = url;
map['text'] = text;
return map;
}
}
Data dataFromJson(String str) => Data.fromJson(json.decode(str));
String dataToJson(Data data) => json.encode(data.toJson());
class Data {
Data({
this.id,
this.email,
this.firstName,
this.lastName,
this.avatar,});
Data.fromJson(dynamic json) {
id = json['id'];
email = json['email'];
firstName = json['first_name'];
lastName = json['last_name'];
avatar = json['avatar'];
}
int? id;
String? email;
String? firstName;
String? lastName;
String? avatar;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['email'] = email;
map['first_name'] = firstName;
map['last_name'] = lastName;
map['avatar'] = avatar;
return map;
}
}
this is my api link
https://reqres.in/api/users?page=2
No need to add UserModel to list.
change this
class _FirstScreenState extends State<FirstScreen> {
UserModel? usermodel;
List<Data?> searchResult= [];
change your fetch data . this will return UserModel as result.
Future<UserModel?> fetchData() async {
final response =
await http.get(Uri.parse('https://reqres.in/api/users?page=2'));
print('This is Response: $response');
if (response.statusCode == 200) {
return UserModel.fromJson(jsonDecode(response.body));
} else {
return throw Exception('Failed to load album');
}
}
create init function to set your initial data
void initFunction() async {
UserModel data = await fetchData(); // you have to await until get the response
//then setState to local variable so it can display to widget
// if you skip this , your usermodel is null
setState ({
usermodel = data ;
});
}
then in your initState
#override
void initState() {
initFunction();
super.initState();
}
usermodel.data consist of data user.
to then you can apply logic to search user from the list.
void searchUser(String enteredData){
List<Data?> temp = [];
for(int i=0; i<usermodel.data.length; i++){
if(enteredData.toLowerCase() == usermodel.data[i].firstName.toLowerCase()){
temp.add(usermodel.data[i];
}
}
// you need to setState again
setState({
searchResult = temp;
});
}
last in you can acces the data in userModel
#override
Widget build(BuildContext context) {
//print('user list data + $searchedList');
return SafeArea(
child: Scaffold(
backgroundColor: Color(0xfff8f8fa),
body: Column(
children: [
Text('${usermodel.data.length}'), /// number all list user
Text('${searchResult.length}'), /// number search user
// now you have list all user
// and all searched list user
// additional, you need to add logic when query is empty
.................
maybe there error in null-safety, please debug first.
You can simply do this
var myList= []; // declare an empty list
if (response.statusCode == 200) {
var result= UserModel.fromJson(jsonDecode(response.body));
if(result != null){
myList.clear();
myList.addAll(result);
}
}

Flutter display server data / response from json

I have built a page which reads the json from recipeURL and I wish for it to display the product_name value in the json file. However for some reason my future fetchData () class isn't being read as none of the text in my if else statement is being displayed. Am I missing a simple oversight here?
EDIT: The main dart file is my main screen. This is where my navbar is created. Users are redirected to other pages when they click on the corresponding icon. Im having trouble passing BarcodePage(title:title); as parameters in my main file,26th line, can be found under Class MyAppState extends State<MyApp> {
My main dart file:
import 'package:flutter/material.dart';
import 'pages/BarcodePage.dart';
import 'pages/FoodPage.dart';
import 'pages/RecipePage.dart';
import 'pages/ShoppingPage.dart';
void main() {
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),);
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState(){
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
int _selectedPage =0;
final _pageOptions= [
FoodPage(),
RecipePage(),
BarcodePage(title: ,),
ShoppingPage(),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
//title: 'Best B4',
theme: ThemeData(
primarySwatch: Colors.teal,),
debugShowCheckedModeBanner: false,
home: Scaffold (
appBar: AppBar(
title:Text(
'BestB4',
style: TextStyle(
fontFamily: 'PacificoRegular',
fontSize: 30,
),
),
backgroundColor: Colors.teal,
elevation: 20,
actions: [
IconButton(
icon: Icon(Icons.qr_code_2_rounded),
tooltip: 'Add item',
onPressed:(){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => BarcodePage()));
},
)
],
//ONPRESSED MENU OPEN
),
body:_pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.teal,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white70,
iconSize: 40,
selectedFontSize: 15,
unselectedFontSize: 15,
currentIndex:_selectedPage,
onTap: (int index) {
setState(() {
_selectedPage = index;
});
},
items: [
BottomNavigationBarItem(
icon:Icon(Icons.restaurant_rounded),
label: 'Food',
), //, title:Text('Food')
BottomNavigationBarItem(
icon:Icon(Icons.menu_book_rounded),
label:'Recipes',
),//, title:Text('Recipes')
BottomNavigationBarItem(
icon:Icon(Icons.add_outlined),
label:'Add',
),//, title:Text('Add')
BottomNavigationBarItem(
icon:Icon(Icons.shopping_cart_rounded),
label:'Shopping',
),//,title:Text('Shopping')
],
),
),
);
}
}
My BarcodePage dart file:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as p;
import 'package:flutter/services.dart';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:http/http.dart';
class BarcodePage extends StatefulWidget{
const BarcodePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_BarcodePageState createState() => _BarcodePageState();
}
var futuredata = {};
class _BarcodePageState extends State<BarcodePage> {
int counter=0;
String result= "";
Future _scanBarcode() async{
try{
ScanResult scanResult = await BarcodeScanner.scan();
String barcodeResult = scanResult.rawContent;
setState(() {
result = barcodeResult;
});
} on PlatformException catch (ex) {
if (ex.code == BarcodeScanner.cameraAccessDenied) {
setState((){
result = "CAMERA PERMISSION WAS DENIED. \n EDIT THIS IN YOUR SETTINGS";
});
}else {
setState(() {
result = "404 ERROR UNKNOWN $ex";
});
}
} on FormatException {
setState(() {
result = "SCAN AN ITEM";
});
} catch (ex){
setState(() {
result = "404 ERROR UNKNOWN $ex";
});
}
}
#override
void initState() {}
fetchmydata() async {
var request = p.Request(
'GET',
Uri.parse(
'https://world.openfoodfacts.org/api/v0/product/5060391623139.json'));
StreamedResponse response = await request.send();
if (response.statusCode == 200) {
// print(await response.stream.bytesToString());
var data = await response.stream.bytesToString();
futuredata = json.decode(data);
} else {
print(response.reasonPhrase);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal,
title:Text('Add an item',
style: TextStyle(
fontFamily: 'Fredoka',
fontSize: 25,
),
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FutureBuilder(
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(
child: Container(
child: CircularProgressIndicator(),
height: 50,
width: 50,
),
);
else if (snapshot.connectionState == ConnectionState.done)
return ListTile(
title: Text(futuredata["product"]["product_name"].toString()),
subtitle: Text("France:" +
futuredata["product"]["product_name_en"].toString()),
);
else {
return Container(
child: CircularProgressIndicator(),
height: 50,
width: 50,
);
}
},
future: fetchmydata(),
)
],
),
floatingActionButton: FloatingActionButton.extended(
backgroundColor: Color.fromRGBO(51, 171, 160, 100),
icon: Icon(Icons.camera_alt),
label: Text("Scan"),
onPressed: _scanBarcode,
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
The Json file looks like this:
JSON LINK: https://world.openfoodfacts.org/api/v0/product/5060391623139.json
You can read your json like this
var futuredata = {};
var productname= futuredata["product"]["product_name"]
Your fetch method like this
fetchmydata() async {
var request = p.Request(
'GET',
Uri.parse(
'https://world.openfoodfacts.org/api/v0/product/5060391623139.json'));
StreamedResponse response = await request.send();
if (response.statusCode == 200) {
// print(await response.stream.bytesToString());
var data = await response.stream.bytesToString();
futuredata = json.decode(data);
} else {
print(response.reasonPhrase);
}
}
With model Class
SampleModel? Mymodel = null;
fetchmydata() async {
var request = p.Request(
'GET',
Uri.parse(
'https://world.openfoodfacts.org/api/v0/product/5060391623139.json'));
StreamedResponse response = await request.send();
if (response.statusCode == 200) {
// print(await response.stream.bytesToString());
var data = await response.stream.bytesToString();
futuredata = json.decode(data);
Mymodel = SampleModel.fromJson(futuredata);
} else {
print(response.reasonPhrase);
}
}
in the method we first read data from json as string or text
then we decode the string type or text from server to map type
SampleCode Dartpad live code check
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:http/http.dart' as p;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
var futuredata = {};
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void initState() {}
fetchmydata() async {
var request = p.Request(
'GET',
Uri.parse(
'https://world.openfoodfacts.org/api/v0/product/5060391623139.json'));
StreamedResponse response = await request.send();
if (response.statusCode == 200) {
// print(await response.stream.bytesToString());
var data = await response.stream.bytesToString();
futuredata = json.decode(data);
} else {
print(response.reasonPhrase);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FutureBuilder(
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(
child: Container(
child: CircularProgressIndicator(),
height: 50,
width: 50,
),
);
else if (snapshot.connectionState == ConnectionState.done)
return ListTile(
title: Text(futuredata["product"]["product_name"].toString()),
subtitle: Text("France:" +
futuredata["product"]["product_name_en"].toString()),
);
else {
return Container(
child: CircularProgressIndicator(),
height: 50,
width: 50,
);
}
},
future: fetchmydata(),
)
],
),
);
}
}
Sample ModelClass
///
/// Code generated by jsonToDartModel https://ashamp.github.io/jsonToDartModel/
///
class SampleModelProduct {
/*
{
"_id": "5060391623139",
"_keywords": [
"peanut"
],
"product_name": "Peanut butter",
"product_name_en": "Peanut butter",
"product_name_fr": "Peanut butter"
}
*/
String? Id;
List<String?>? Keywords;
String? productName;
String? productNameEn;
String? productNameFr;
SampleModelProduct({
this.Id,
this.Keywords,
this.productName,
this.productNameEn,
this.productNameFr,
});
SampleModelProduct.fromJson(Map<String, dynamic> json) {
Id = json['_id']?.toString();
if (json['_keywords'] != null) {
final v = json['_keywords'];
final arr0 = <String>[];
v.forEach((v) {
arr0.add(v.toString());
});
Keywords = arr0;
}
productName = json['product_name']?.toString();
productNameEn = json['product_name_en']?.toString();
productNameFr = json['product_name_fr']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['_id'] = Id;
if (Keywords != null) {
final v = Keywords;
final arr0 = [];
v!.forEach((v) {
arr0.add(v);
});
data['_keywords'] = arr0;
}
data['product_name'] = productName;
data['product_name_en'] = productNameEn;
data['product_name_fr'] = productNameFr;
return data;
}
}
class SampleModel {
/*
{
"code": "5060391623139",
"product": {
"_id": "5060391623139",
"_keywords": [
"peanut"
],
"product_name": "Peanut butter",
"product_name_en": "Peanut butter",
"product_name_fr": "Peanut butter"
},
"status": 1,
"status_verbose": "product found"
}
*/
String? code;
SampleModelProduct? product;
int? status;
String? statusVerbose;
SampleModel({
this.code,
this.product,
this.status,
this.statusVerbose,
});
SampleModel.fromJson(Map<String, dynamic> json) {
code = json['code']?.toString();
product = (json['product'] != null)
? SampleModelProduct.fromJson(json['product'])
: null;
status = json['status']?.toInt();
statusVerbose = json['status_verbose']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['code'] = code;
if (product != null) {
data['product'] = product!.toJson();
}
data['status'] = status;
data['status_verbose'] = statusVerbose;
return data;
}
}
try creating a .g.dart file using flutter packages pub run build_runner build. flutter automatically will create your binding class factory. once you have the binding classes created flutter will automatically code your binding class, including nested classes. I personally think automation is the way to handle all interactions with json from the server. The reason you want to use the future .g.dart code generator is to reduce the possibility of error and incorrect type casting.
file.dart
factory ProductView.fromJson(Map<String, dynamic> json) =>
_$ProductFromJson(json);
Map<String, dynamic> toJson() => _$ProductViewToJson(this);
file.g.dart
ProductView _$ProductViewFromJson(Map<String, dynamic> json) {
return
ProductView(
json['field1'] as int,
json['field2'] as String,
json['field3'] == null
? null
: DateTime.parse(json['field3'] as String),
);
}
Map<String, dynamic> _$ProjectViewToJson(ProductView instance) =>
<String, dynamic>{
'field1': instance.field1,
'field2': instance.field2,
'field3': instance.field3?.toIso8601String(),
};
decoding the json
var client = http.Client();
Map<String, String> headers = new HashMap();
headers['Accept'] = 'application/json';
headers['Content-Type'] = 'application/json';
headers['Authorization'] = 'Bearer $token';
var response = await client.get(url, headers: headers).timeout(_TIMEOUT);
var parsed = json.decode(response.body);
var view = ProductView.fromJson(parsed);

load data using provider call while navigating to the screen

I have managed to load data from odoo db, the data can be loaded at first call but after updating or creatinig a new customer in server side odoo database nothing happens in my flutter app. the data not loaded in customer screen
i tried to call the provider in initState , setState but nothing worked for me.
customer model
class Customer {
int id;
String name;
Customer({
required this.id,
required this.name,
});
factory Customer.fromJson(Map<dynamic, dynamic> json) => Customer(
name: json["name"],
id: json["id"],
);
Map<dynamic, dynamic> toJson() => {
"name": name,
"id": id,
};
}
Customer provider
class CustomerProvider with ChangeNotifier {
CustomerProvider() {
loadCustomers().then((customers) {
_customers = customers;
notifyListeners();
});
}
List<Customer> _customers = [];
List<Customer> get customers => _customers;
Future getCustomer() async {
notifyListeners();
final client = OdooClient('http://localhost:8050');
await client.authenticate('odoo', 'admin', 'admin');
var res = await client.callKw({
'model': 'res.partner',
'method': 'search_read',
'args': [],
'kwargs': {
'context': {'bin_size': true},
// 'domain': [
// ['id','=',10]
// ],
'fields': ['name'],
},
});
return res;
}
Future loadCustomers() async {
notifyListeners();
final data = await getCustomer();
_customers = ((data as List).map((i) => Customer.fromJson(i)).toList());
return _customers;
}
}
Customer screen
class CustomerPage extends StatefulWidget {
final bool? isMultiSelection;
final List<Customer> customers;
const CustomerPage({
Key? key,
this.isMultiSelection = false,
this.customers = const [],
}) : super(key: key);
#override
_CustomerPageState createState() => _CustomerPageState();
}
class _CustomerPageState extends State<CustomerPage> {
String text = '';
bool loading = false;
List<Customer>? csm;
#override
void initState() {
super.initState();
}
bool containsSearchText(Customer customer) {
final name = customer.name;
final textLower = text.toLowerCase();
final customerLower = name.toLowerCase();
return customerLower.contains(textLower);
}
#override
Widget build(BuildContext context) {
var provider = Provider.of<CustomerProvider>(context, listen: false);
var allcustomers = provider.customers;
var customers = allcustomers.where(containsSearchText).toList();
return ChangeNotifierProvider.value(
value: CustomerProvider(),
child: loading ? const Loading() : Scaffold(
appBar: AppBar(
title: const Text('Selectionner un client'),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(60),
child: SearchWidget(
text:text,
hintText: 'Rechercher',
onChanged: (text) => setState(() => this.text = text),
)),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Color(0xffC71C24), Color(0xffFDBA45)]),
),
),
),
body: customers.isEmpty
// ? const Center(child: CircularProgressIndicator(color: Color(0xffFDBA45),),)
? const Loading()
: ListView(
children: customers.map((customer) {
return CustomerListTileWidget(
customer: customer,
isSelected: false,
onSelectedCustomer: selectCustomer
);
}).toList(),
),
)
);
}

How to implement search delegate with sqflite flutter

I have created an app that stores some notes in sqlite database. I did all CRUD operations and it's working well, but when I'm trying to make search operation inside my database with SearchDelegate, I got some problem. I'll show you my code before I make search with SearchDelegate
databaseHelper:
import 'dart:async';
import 'package:plants/model/plant.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
final String tableNote = 'noteTable';
final String columnId = 'id';
final String columnLocation = 'location';
final String columnTitle = 'title';
final String columnDescription = 'description';
static Database _db;
DatabaseHelper.internal();
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
initDb() async {
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'notes.db');
// await deleteDatabase(path); // just for testing
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
void _onCreate(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $tableNote($columnId INTEGER PRIMARY KEY,$columnLocation TEXT, $columnTitle TEXT, $columnDescription TEXT)');
}
Future<int> saveNote(Note note) async {
var dbClient = await db;
var result = await dbClient.insert(tableNote, note.toMap());
// var result = await dbClient.rawInsert(
// 'INSERT INTO $tableNote ($columnTitle, $columnDescription) VALUES (\'${note.title}\', \'${note.description}\')');
return result;
}
Future<List> getAllNotes() async {
var dbClient = await db;
var result = await dbClient.query(tableNote, columns: [columnId, columnLocation , columnTitle, columnDescription]);
// var result = await dbClient.rawQuery('SELECT * FROM $tableNote');
return result.toList();
}
Future<int> getCount() async {
var dbClient = await db;
return Sqflite.firstIntValue(await dbClient.rawQuery('SELECT COUNT(*) FROM $tableNote'));
}
Future<Note> getNote(int id) async {
var dbClient = await db;
List<Map> result = await dbClient.query(tableNote,
columns: [columnId, columnLocation , columnTitle, columnDescription],
where: '$columnId = ?',
whereArgs: [id]);
// var result = await dbClient.rawQuery('SELECT * FROM $tableNote WHERE $columnId = $id');
if (result.length > 0) {
return new Note.fromMap(result.first);
}
return null;
}
Future<int> deleteNote(int id) async {
var dbClient = await db;
return await dbClient.delete(tableNote, where: '$columnId = ?', whereArgs: [id]);
// return await dbClient.rawDelete('DELETE FROM $tableNote WHERE $columnId = $id');
}
Future<int> updateNote(Note note) async {
var dbClient = await db;
return await dbClient.update(tableNote, note.toMap(), where: "$columnId = ?", whereArgs: [note.id]);
// return await dbClient.rawUpdate(
// 'UPDATE $tableNote SET $columnTitle = \'${note.title}\', $columnDescription = \'${note.description}\' WHERE $columnId = ${note.id}');
}
Future close() async {
var dbClient = await db;
return dbClient.close();
}
}
Class Notes
class Note {
int _id;
String _location;
String _title;
String _description;
Note(this._location,this._title, this._description);
Note.map(dynamic obj) {
this._id = obj['id'];
this._location = obj['location'];
this._title = obj['title'];
this._description = obj['description'];
}
int get id => _id;
String get location => _location;
String get title => _title;
String get description => _description;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
if (_id != null) {
map['id'] = _id;
}
map['location'] = _location;
map['title'] = _title;
map['description'] = _description;
return map;
}
Note.fromMap(Map<String, dynamic> map) {
this._id = map['id'];
this._location = map ['location'];
this._title = map['title'];
this._description = map['description'];
}
}
List.dart
import 'package:flutter/material.dart';
import 'package:plants/AddPlant.dart';
import 'package:plants/model/plant.dart';
import 'database/dbhelper.dart';
class ListViewNote extends StatefulWidget {
#override
State<StatefulWidget> createState() => new ListViewNoteState();
}
class ListViewNoteState extends State<ListViewNote> {
List<Note> items = new List();
DatabaseHelper db = new DatabaseHelper();
#override
void initState() {
super.initState();
db.getAllNotes().then((notes) {
setState(() {
notes.forEach((note) {
items.add(Note.fromMap(note));
});
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Plant List',
),
centerTitle: true,
actions: <Widget>[
IconButton(icon: Icon(Icons.search,
color: Colors.white,), onPressed: (){
showSearch(context: context,
delegate: DataSearch(this.items));
})
],
),
body: Center(
child: ListView.builder(
itemCount: items.length,
padding: const EdgeInsets.all(15.0),
itemBuilder: (context, position) {
return Dismissible(
background: stackBehindDismiss(),
key: ObjectKey(items[position]),
child: Card(
elevation: 2.0,
margin: new EdgeInsets.symmetric(horizontal: 0.0,vertical: 2.0),
child: Column(
children: <Widget>[
ListTile(
title: Text(
'${items[position].title}',
style: TextStyle(
fontSize: 22.0,
color: Colors.deepOrangeAccent,
),
),
subtitle: Text(
'${items[position].description}' + '' + '${items[position].location}',
style: new TextStyle(
fontSize: 18.0,
fontStyle: FontStyle.italic,
),
),
onTap: () => _navigateToNote(context, items[position]),
),
],
),
),
onDismissed: (dirction){
var item = items.elementAt(position);
_deleteNote(context, items[position], position);
Scaffold.of(context).showSnackBar(SnackBar(
content: Text("Item deleted"),
));
},
);
}
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.green[700],
child: Icon(Icons.add),
onPressed: () => _createNewNote(context),
),
);
}
void _deleteNote(BuildContext context, Note note, int position) async {
db.deleteNote(note.id).then((notes) {
setState(() {
items.removeAt(position);
});
});
}
void _navigateToNote(BuildContext context, Note note) async {
String result = await Navigator.push(
context,MaterialPageRoute(builder: (context) => NoteScreen(note)),
);
if (result == 'update') {
db.getAllNotes().then((notes) {
setState(() {
items.clear();
notes.forEach((note) {
items.add(Note.fromMap(note));
});
});
});
}
}
void _createNewNote(BuildContext context) async {
String result = await Navigator.push(
context,MaterialPageRoute(builder: (context) => NoteScreen(Note('', '', ''))),
);
if (result == 'save') {
db.getAllNotes().then((notes) {
setState(() {
items.clear();
notes.forEach((note) {
items.add(Note.fromMap(note));
});
});
});
}
}
stackBehindDismiss() {
return Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 20.0),
color: Colors.green[700],
child: Icon(
Icons.delete,
color: Colors.white,
),
);
}
}
class DataSearch extends SearchDelegate<Note> {
DatabaseHelper db = new DatabaseHelper();
final List<Note> items = new List();
List<Note> suggestion = new List();
// ListViewNoteState i = ListViewNoteState();
DataSearch(this.suggestion);
#override
ThemeData appBarTheme(BuildContext context) {
assert(context != null);
final ThemeData theme = Theme.of(context);
assert(theme != null);
return theme;
}
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(icon: Icon(Icons.clear), onPressed: () {
query = '';
} )
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: (){
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
}
#override
Widget buildSuggestions(BuildContext context) {
final suggestion = query.isEmpty
? items
: items.where((target) => target.title.startsWith(query)).toList();
if(items.isEmpty)
{
print("Null");
}
return ListView.builder(
itemBuilder: (context, position)=>
ListTile(
title: Text(items[position].title),
),
itemCount: suggestion.length,
);
}
}
On CRUD, everything is working well and I'm adding 3 records but when I'm using SearchDelegate to implement search, I not getting any result.
The thing I need is to searchlist from database using searchbar
Try this:
return ListView.builder(
itemBuilder: (context, index)=>
ListTile(
title: Text(suggestion[index].title),
),
It should work and display the right results, at least mine is working this way, but I think you'll have to point to items list when you build your results or when you tap to open it or something like that.