I am currently developing an ecommerce app. I use a ListView.builder to display data but I don't know how to fetch the products according to the selected category.
Here is the model of the list of categories
class Category {
final int id;
String name;
String image;
String slug;
Category({this.id, this.name, this.image, this.slug,});
factory Category.fromJson(Map<String, dynamic> json) {
return Category(
id: json['id'],
name: json['name'],
image: json['image'].toString(),
slug: json['slug'],
);
}
}
class Produit {
String productName;
String productImage;
int productPrice;
String description;
int rating;
int numberOfRating;
int category;
Produit(
{this.productName,
this.productImage,
this.productPrice,
this.description,
this.rating,
this.numberOfRating,
this.category});
factory Produit.fromJson(Map<String, dynamic> json) {
return Produit(
productName: json['name'],
productImage: json["image"],
productPrice: json["prix"],
description: json["description"].toString(),
rating: json["rating"],
numberOfRating: json["numberOfRating"],
category: json["category"]
);
}
}
This is the widget that displays the list of products by category.
class ProduitPage extends StatefulWidget {
ProduitPage({
Key key,
this.categoryId,
}) : super(key: key);
int categoryId;
#override
_ProduitPageState createState() => _ProduitPageState();
}
class _ProduitPageState extends State<ProduitPage> {
List<Produit> _produits = List<Produit>();
Future<List<Produit>> fetchProduits() async {
//var url = 'http://192.168.8.100:8000/api/produits';
var url = apilink + prod_url;
final response = await http.get(url);
var produits = List<Produit>();
if (response.statusCode == 200) {
var produitsJson = json.decode(response.body);
for (var produitJson in produitsJson) {
produits.add(Produit.fromJson(produitJson));
}
return produits;
} else {
throw Exception('Impossible de charger les données.');
}
}
#override
Widget build(BuildContext context) {
fetchProduits().then((value) {
_produits.addAll(value);
});
return Container(
child: Scaffold(
appBar: AppBar(
title: Text('Restaurant'),
backgroundColor: Colors.amber,
brightness: Brightness.light,
actions: <Widget>[
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) =>
new SearchWidget()));
},
icon: Icon(
Icons.search,
color: Colors.white,
),
iconSize: 30,
),
CartIconWithBadge(),
]),
body: Container(
child: ListView.builder(
itemCount: _produits.length,
itemBuilder: (BuildContext context, int i) {
return Container(
child: (_produits[i].category == categoryId),
);
},
))),
);
}
}
Now I want to display data by selected category. Please I really need your help. thanks!!
Thank you for updating your question
While there are many ways you can achieve your desired result, I have mentioned one solution below.
Since you have a list of products available in the widget, you can use the List.where method to filter the product list by category and display the results of that function in your widget
More details and sample code available on
Note: Replace calling the API function in the build method directly and replace it with the FutureBuilder widget. The code sample above will make a call to the API on each build.
More details and sample code available on flutter docs
Related
I'm trying out the StreamBuilder so I call the API and the return values of the snapshot are always null. When I print the print(snapshot.error.toString()); it returns null. I've tried parsing the data differently but failed to do so. Here is the complete code:
var posts = <Post>[];
var controller = StreamController<List<Post>>();
#override
void initState() {
super.initState();
fetchPosts();
}
fetchPosts() async {
final resp =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
final data = json.decode(resp.body);
posts = data.map<Post>((e) => Post()).toList();
print(posts.isEmpty); // returns false
pirnt(data); // returns the entire data
controller.add(posts);
}
bool isSwitched = false;
void toggleSwitch(bool value) {
if (isSwitched == false) {
controller.add(posts..sort((k1, k2) => k1.id.compareTo(k2.id)));
print('Switch Button is ON');
} else {
controller.add(posts..sort((k1, k2) => k2.id.compareTo(k1.id)));
print('Switch Button is OFF');
}
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Expanded(
child: TextButton(
child: Text('sort ascending'),
onPressed: () {
toggleSwitch(isSwitched = !isSwitched);
}),
),
],
),
Expanded(
child: StreamBuilder<List<Post>>(
initialData: posts,
stream: controller.stream,
builder: (ctx, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
print(snapshot.error.toString);
Post post = snapshot.data[index];
return ListTile(
title: Text('${posts[index].id}'), // returns null
subtitle: Text('${post.id}'), // returns null
trailing: Text('${snapshot.data[index].id}'), // returns null
);
},
);
},
),
),
],
);
}
The Post model:
import 'package:flutter/foundation.dart';
import 'dart:core';
class Post extends ChangeNotifier {
final int userId;
final int id;
final String title;
final String body;
Post({this.userId, this.id, this.title, this.body});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
Am I parsing the data wrong in the API call? Or is it something else? Thanks in advance for the help.
So my new teacher/friend #pskink that makes me learn and makes me use my brain these days, again helped me realize the mistake. Always make sure you are parsing the JSON data as it should. In my case, I forgot the fromJson(e) in the fetchPosts() method.
So the answer to this problem is:
posts = data.map<Post>((e) => Post.fromJson(e)).toList();
I have an API https://sdgfortb.herokuapp.com/onetreeplanted
This is how I fetch data.
Future<List<TreeInfo>> fetchGoals(http.Client client) async {
final response =
await client.get('https://sdgfortb.herokuapp.com/onetreeplanted');
// Use the compute function to run parseGoalss in a separate isolate.
return compute(parseGoalss, response.body);
}
// A function that converts a response body into a List<TreeInfo>.
List<TreeInfo> parseGoalss(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<TreeInfo>((json) => TreeInfo.fromJson(json)).toList();
}
This is my model
class TreeInfo {
String region;
String country;
String name;
String overview;
String impact;
String treespecies;
String imagelink;
TreeInfo(
{this.region,
this.country,
this.name,
this.overview,
this.impact,
this.treespecies,
this.imagelink});
factory TreeInfo.fromJson(Map<String, dynamic> json) {
return TreeInfo(
region: json["region"] as String,
country: json["country"] as String,
name: json["name"] as String,
overview: json["overview"] as String,
treespecies: json["tree_species"] as String,
impact: json["impact"] as String,
imagelink: json["image_link"] as String,
);
}
}
Fetching Data
class Loading extends StatelessWidget {
final String destination;
Loading({this.destination});
#override
Widget build(BuildContext context) {
return FutureBuilder<List<TreeInfo>>(
future: fetchGoals(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? GoalPage(
goals: snapshot.data,
// destination: destination,
)
: Center(
child: CircularProgressIndicator(
backgroundColor: Colors.greenAccent,
));
},
);
}
}
class GoalPage extends StatelessWidget {
List<Goals> goals;
GoalPage({this.goals});
#override
Widget build(BuildContext context) {
return Scaffold(
Container(
height: 470,
padding: const EdgeInsets.only(left: 32),
child: Swiper(
itemCount: goals.length,
itemWidth: MediaQuery.of(context).size.width - 2 * 64,
layout: SwiperLayout.STACK,
pagination: SwiperPagination(
builder:
DotSwiperPaginationBuilder(activeSize: 8, space: 3),
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, a, b) => DetailPage(
goalInfo: goals[index],
),
),
);
},
I want to fetch the array which includes the region as North America.
There are three other regions but I want to get data from North America from the API
You can use where function from List
final filteredList = goals.where((goals) => goal.region == 'North America')
i try to fetch api json id , username , photo ..etc...
and when use jsonplaceholder it's working fine
and when use mine don't get any data
flutter code
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class ListViewJsonapi extends StatefulWidget {
_ListViewJsonapiState createState() => _ListViewJsonapiState();
}
class _ListViewJsonapiState extends State<ListViewJsonapi> {
final String uri = 'https://www.christian-dogma.com/android-index.php';
Future<List<Users>> _fetchUsers() async {
var response = await http.get(uri);
if (response.statusCode == 200) {
final items = json
.decode(utf8.decode(response.bodyBytes))
.cast<Map<String, dynamic>>();
List<Users> listOfUsers = items.map<Users>((json) {
return Users.fromJson(json);
}).toList();
return listOfUsers;
} else {
throw Exception('Failed to load internet');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Users>>(
future: _fetchUsers(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
return ListView(
children: snapshot.data
.map((user) => ListTile(
title: Text(user.name),
subtitle: Text(user.email),
leading: CircleAvatar(
backgroundColor: Colors.red,
child: Text(user.name[0],
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
)),
),
))
.toList(),
);
},
),
);
}
}
class Users {
int id;
String name;
String username;
String email;
Users({
this.id,
this.name,
this.username,
this.email,
});
factory Users.fromJson(Map<String, dynamic> json) {
return Users(
id: json['id'],
name: json['name'],
email: json['email'],
username: json['username'],
);
}
}
when use https://jsonplaceholder.typicode.com/users it's working fine
and when use mine https://www.christian-dogma.com/android-index.php i don't get any data
He managed to make it work, one of the problems he has is that the id asks me to be a String since you had it as an integer, I hope it worked for you.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class JsonApiPhp extends StatefulWidget {
#override
_JsonApiPhpState createState() => _JsonApiPhpState();
}
class _JsonApiPhpState extends State<JsonApiPhp> {
bool loading = true;
final String url = 'https://www.christian-dogma.com/android-index.php';
var client = http.Client();
List<User> users = List<User>();
#override
void initState(){
fetchData();
super.initState();
}
Future<void> fetchData() async {
http.Response response = await client.get(url);
if(response.statusCode == 200){ // Connection Ok
List responseJson = json.decode(response.body);
responseJson.map((m) => users.add(new User.fromJson(m))).toList();
setState(() {
loading = false;
});
} else {
throw('error');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: loading ?
Container(
child: Center(
child: CircularProgressIndicator(),
),
) :
ListView.builder(
itemCount: users.length,
itemBuilder: (BuildContext context, int index){
return Card(
child: ListTile(
title: Text(users[index].username),
),
);
},
)
),
);
}
}
class User {
final String id;
final String name;
final String username;
final String email;
User({
this.id,
this.name,
this.username,
this.email,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
username: json['username'],
);
}
}
I kind of banged my head around and finally found out that your JSON response is returning the id as a string and not as an integer.
Change the factory to following code.
factory Users.fromJson(Map<String, dynamic> json) {
return Users(
id: int.parse(json['id']),
name: json['name'],
email: json['email'],
username: json['username'],
);
Cheers!
I have a list of strings which is held in an object and is stored in shared preferences.
I have used ScopedModel for my state management and I am trying to get it to read the list from the shared preferences from here.
class Item {
String _weight;
String _name;
String _id;
Item(this._weight, this._name, this._id);
Item.fromJson(Map<String, dynamic> m) {
_weight = m['weight'] as String;
_name = m['name'] as String;
_id = m['id'] as String;
}
String get id => _id;
String get name => _name;
String get weight => _weight;
Map<String, dynamic> toJson() => {
'weight': _weight,
'name': _name,
'id': _id,
};
}
My Model in the ScopedModel folder which is passed down;
mixin ListItem on Model {
String itemKey = 'itemKey';
List<Item> _items = [];
List<Item> get items {
return List.from(_items);
}
Future<Null> readList() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final data = json.decode(prefs.getString(itemKey).toString());
final item = List<Item>.from(data.map((i) => Item.fromJson(i)));
_items = item;
print(jsonDecode(prefs.getString(itemKey)));
notifyListeners();
}
Future<Null> addItem({String id, String name, String weight}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final Item item = Item(
id,
name,
weight,
);
_items.add(item);
prefs.setString(itemKey, jsonEncode(_items));
notifyListeners();
}
Future<Null> deleteProduct() async {
notifyListeners();
}
}
Part of my stateful widget which runs the initState to call the list from sharedPreferences
class _ListItemsState extends State<ListItems> {
final MainModel _model = MainModel();
final TextEditingController controller = TextEditingController();
#override
void initState() {
_model.readList();
super.initState();
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return Scaffold(
appBar: AppBar(),
body: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
TextField(
controller: controller,
),
FlatButton(
child: Text('Submit'),
onPressed: () {
model.addItem(
id: controller.text,
name: controller.text,
weight: controller.text,
);
},
),
]),
),
model.items.length > 0
? SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Dismissible(
key: Key(model.items[index].id),
background: Container(
color: Colors.redAccent,
),
onDismissed: (DismissDirection direction) {
model.deleteProduct();
},
child: ListTile(
leading: Text(model.items[index].name),
trailing: Text(model.items[index].weight),
onTap: () {},
),
);
},
childCount: model.items.length,
),
)
: SliverFillRemaining(),
],
));
},
);
}
}
My issue is that on the initState the list doesn't appear from the readList() - I'm not sure what I am doing wrong as when I run the print(json.decode(prefs.getString(itemKey)); it calls the list of items that are held from the sharedPreferences as [{weight: HelloWorld, name: HelloWorld, id: HelloWorld}] which looks like it should be fine to decode.
Can anyone help point out what I'm doing wrong? Thanks in advance.
In your code, you have 2 different models:
final MainModel _model = MainModel();
and another model from builder
builder: (BuildContext context, Widget child, MainModel model)
It looks like that there is no connection/link/map between _model and model. You have to link them together like in this discussion.
I`m going to use data from a server in my application. For that, I will receive the data using json from my local server, but the problem is that data is null and it is not fetched by my code.
Here is the code :
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp( MaterialApp(
home: MyHomePage(),
));
}
class MyHomePage extends StatefulWidget {
var title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<Base> _getBase() async {
var data = await http.get(Uri.encodeFull("http://192.168.1.13:5000/json"));
var jsonData = json.decode(data.body);
Base base = Base.fromJson(jsonData);
return base;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hi'),
),
body: Container(
child: FutureBuilder(
future: _getBase(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.teal)),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.categories.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
subtitle: Text(snapshot.data.categories[index].category_id),
title: Text(snapshot.data.categories[index].devices[index].title),
leading: CircleAvatar(
// ignore: argument_type_not_assignable
// backgroundImage: NetworkImage(snapshot.data.devices[index].thumbnailUrl),
)
);
},
);
}
},
),
),
);
}
}
and here is the data model used for json:
class Base{
List <Category> array;
List<Category> categories;
Base( {this.categories});
factory Base.fromJson(Map<String,dynamic> parsedJson){
var list = parsedJson['categories'] as List;
List<Category> categoryList = list.map((i) => Category.fromJson(i)).toList();
return Base(
categories: categoryList
);
}
}
class Category {
int category_id,device_type_id,room_id;
List<Device_type> device_types;
List<Rooms> rooms;
List<Devices> devices;
Category({this.category_id,this.device_types,this.device_type_id,this.devices,this.rooms,this.room_id});
factory Category.fromJson(Map<String,dynamic>parsedJson){
var list1 = parsedJson['Device_types'] as List;
var list2 = parsedJson['Rooms'] as List;
var list3 = parsedJson['Devices'] as List;
List<Device_type> deviceTypeList =list1.map((i)=>Device_type.fromJson(i)).toList();
List<Rooms> roomsList =list2.map((i)=>Rooms.fromJson(i)).toList();
List<Devices> devicesList =list3.map((i)=>Devices.fromJson(i)).toList();
return Category(
category_id: parsedJson["category_id"],
device_type_id: parsedJson["device_type_id"],
room_id: parsedJson["room_id"],
device_types: deviceTypeList,
rooms : roomsList,
devices: devicesList
);
}
}
class Device_type {
int device_type_id, image , device_no;
String title ;
Device_type ({this.device_type_id,this.title,this.image,this.device_no});
factory Device_type.fromJson(Map<String,dynamic>parsedJson){
return Device_type(
device_type_id: parsedJson["device_type_id"],
title: parsedJson["title"],
image: parsedJson["image"],
device_no: parsedJson["device_no"],
);
}
}
class Rooms {
int id, image , device_no,roomcode;
String description,title ;
Rooms ({this.id,this.title,this.image,this.device_no,this.roomcode,this.description});
factory Rooms.fromJson(Map<String,dynamic> parsedJson){
return Rooms(
id: parsedJson["id"],
title:parsedJson["title"],
image: parsedJson["image"],
device_no: parsedJson["device_no"],
roomcode: parsedJson["roomcode"],
description: parsedJson["description"]
);
}
}
class Devices {
int device_id, image_path ,type,status,fav_flag, category_id;
String description,title ;
Devices ({this.device_id,this.title,this.image_path,
this.description,this.fav_flag,this.status,this.type,this.category_id});
factory Devices.fromJson(Map<String,dynamic> parsedJson){
return Devices(
device_id: parsedJson["device_id"],
title:parsedJson["title"],
image_path: parsedJson["image_path"],
type: parsedJson["type"],
status: parsedJson["status"],
fav_flag: parsedJson["fav_flag"],
category_id : parsedJson['category_id'],
description: parsedJson["description"]
);
}
}
for better understanding i put the image below:
And when I trace the project to find out where is the problem, I realized that it just loop one time, and so the data will be null forever, don't know why
Any help will be appreciated