How to fetch data from REST API and display on Listview, Flutter - flutter

I am fairly new to using REST API to GET data and display in a Listview in Flutter. I have since been working on something like this, But i get Lost along the line. Hence I need Help with this.
My code Goes thus
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class TransactionDetails {
final String avatar;
final String name;
final String date;
final String amount;
TransactionDetails({required this.avatar, required this.name, required this.date, required this.amount});
factory TransactionDetails.fromJson(Map<String, dynamic> json) {
return TransactionDetails(
avatar: json['avatar'],
name: json['name'],
date: json['date'],
amount: json['amount']);
}
}
class BaseScreen extends StatelessWidget {
const BaseScreen({Key? key}) : super(key: key);
Future<TransactionDetails> fetchTransaction() async {
final response = await http
.get('https://brotherlike-navies.000webhostapp.com/people/people.php');
if (response.statusCode == 200) {
return TransactionDetails.fromJson(json.decode(response.body));
} else {
throw Exception('Request Failed.');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: double.infinity,
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$5200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$1200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
SizedBox(height: 24),
],
),
),
Padding(
padding: EdgeInsets.all(15),
child: Text(
"Recent Transactions",
style: TextStyle(
fontSize: 14, fontWeight: FontWeight.bold, color: Colors.green),
),
),
ListView() //Display the data here from the REST API
],
)));
}
}
How do I Display it on the Listview? Please I need help with this. Just for the sake of clarification as I am learning here with this.

Try below code:
Your TransactionDetails Class
class TransactionDetails {
String? avatar;
String? name;
String? date;
String? amount;
TransactionDetails({
this.avatar,
this.name,
this.date,
this.amount,
});
TransactionDetails.fromJson(Map<String, dynamic> json) {
avatar = json['avatar'];
name = json['name'];
date = json['date'];
amount = json['amount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['avatar'] = avatar;
data['name'] = name;
data['date'] = date;
data['amount'] = amount;
return data;
}
}
API Call:
Future<List<TransactionDetails>> fetchAlbum() async {
final response = await http.get(Uri.parse(
'https://brotherlike-navies.000webhostapp.com/people/people.php'));
if (response.statusCode == 200) {
final List result = json.decode(response.body);
return result.map((e) => TransactionDetails.fromJson(e)).toList();
} else {
throw Exception('Failed to load data');
}
}
Your Widget:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: double.infinity,
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$5200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$1200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 24),
],
),
),
const Padding(
padding: EdgeInsets.all(15),
child: Text(
"Recent Transactions",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.green),
),
),
Center(
child: FutureBuilder<List<TransactionDetails>>(
future: fetchAlbum(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: Image.network(
snapshot.data![index].avatar.toString()),
),
title: Text(snapshot.data![index].name.toString()),
trailing:
Text(snapshot.data![index].amount.toString()),
subtitle: Text(snapshot.data![index].date.toString()),
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
],
),
Result Screen->

There are multiple ways you can achieve this.
Using FutureBuilder
Using StatefulWidget & setState
Currently, you are using a StatelessWidget (BaseScreen) so let's just go with FutureBuilder.
On hitting the url that you have given:
https://brotherlike-navies.000webhostapp.com/people/people.php
It gives the following response:
[{"avatar":"https:\/\/static.vecteezy.com\/system\/resources\/thumbnails\/002\/002\/403\/small\/man-with-beard-avatar-character-isolated-icon-free-vector.jpg","name":"Kayo Johnson","date":"09\/29\/2022","amount":"5000.00"},{"avatar":"https:\/\/static.vecteezy.com\/system\/resources\/thumbnails\/002\/002\/403\/small\/man-with-beard-avatar-character-isolated-icon-free-vector.jpg","name":"Kuta Joy","date":"09\/29\/2022","amount":"5000.00"},{"avatar":"https:\/\/static.vecteezy.com\/system\/resources\/thumbnails\/001\/993\/889\/small\/beautiful-latin-woman-avatar-character-icon-free-vector.jpg","name":"Timmi Phillips","date":"09\/28\/2022","amount":"3500.00"}]
It returns a list of transaction details objects. Hence, you should add another method in your model TransactionDetail which will return a list of TransactionDetails as follows:
class TransactionDetails {
final String avatar;
final String name;
final String date;
final String amount;
TransactionDetails(
{required this.avatar,
required this.name,
required this.date,
required this.amount});
factory TransactionDetails.fromJson(Map<String, dynamic> json) {
return TransactionDetails(
avatar: json['avatar'],
name: json['name'],
date: json['date'],
amount: json['amount']);
}
static List<TransactionDetails> fromJsonList(dynamic jsonList) {
final transactionDetailsList = <TransactionDetails>[];
if (jsonList == null) return transactionDetailsList;
if (jsonList is List<dynamic>) {
for (final json in jsonList) {
transactionDetailsList.add(
TransactionDetails.fromJson(json),
);
}
}
return transactionDetailsList;
}
}
Update your fetchTransaction method as follows:
Future<List<TransactionDetails>> fetchTransaction() async {
final response = await http
.get('https://brotherlike-navies.000webhostapp.com/people/people.php');
if (response.statusCode == 200) {
return TransactionDetails.fromJsonList(json.decode(response.body));
} else {
throw Exception('Request Failed.');
}
}
Just wrap your ListView widget with a FutureBuilder widget as follows:
FutureBuilder(
future: fetchTransaction(),
builder: (context, AsyncSnapshot<List<TransactionDetails>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const Center(child: Text('Something went wrong'));
}
return ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data![index].name),
subtitle: Text(snapshot.data![index].amount),
);
});
}
return const CircularProgressIndicator();
},
),

Related

Null Check operator used on a null value

when click the showModalBottomSheet it's show on the screen
Null Check operator used on a null value
can anyone help to figure out which is the code error? kindly refer full code as below, TQ!
class ProductContentFirst extends StatefulWidget {
final List _productContentList;
const ProductContentFirst(this._productContentList, {Key? key})
: super(key: key);
#override
State<ProductContentFirst> createState() => _ProductContentFirstState();
}
class _ProductContentFirstState extends State<ProductContentFirst> {
ProductContentItem? _productContent;
List _attr = [];
#override
void initState() {
super.initState();
_productContent = widget._productContentList[0];
this._attr = this._productContent!.attr!;
//print(this._attr);
}
List<Widget>? _getAttrItemWidget(attrItem) {
List<Widget> attrItemList = [];
attrItem.list.forEach((item) {
attrItemList.add(Container(
margin: EdgeInsets.all(5),
child: Chip(
label: Text("${item}"),
padding: EdgeInsets.all(10),
),
));
print (item);
});
}
List<Widget>? _getAttrWidget() {
List<Widget> attrList = [];
this._attr.forEach((attrItem) {
attrList.add(Wrap(
children: [
Container(
width: ScreenAdapter.width(80.0),
child: Padding(
padding: EdgeInsets.only(top: ScreenAdapter.height(30.0)),
child: Text(
"${attrItem.cate}",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
),
Container(
width: ScreenAdapter.width(600.0),
child: Wrap(
//children: [],
children: _getAttrItemWidget(attrItem)!,
),
)
],
));
print(attrItem.cate);
});
//return null;
}
_attrBottomSheet() {
showModalBottomSheet(
context: context,
builder: (context) {
return GestureDetector(
onTap: () {
false;
},
child: Stack(
children: [
Container(
padding: EdgeInsets.all(10),
child: ListView(
children: [
Column(
children: _getAttrWidget()!,
)
],
),
),
Positioned(
bottom: 0,
width: ScreenAdapter.width(750.0),
height: ScreenAdapter.height(100),
child: Row(
children: [
Expanded(
flex: 1,
child: Container(
child: JdButton(
color: Color.fromRGBO(253, 1, 0, 0.9),
text: "Add Cart",
cb: () {
print("Add Cart");
},
),
)),
Expanded(
flex: 1,
child: Container(
child: JdButton(
color: Color.fromRGBO(255, 165, 0, 0.9),
text: "Buy",
cb: () {
print("Buy");
},
),
)),
],
))
],
),
);
});
}
#override
Widget build(BuildContext context) {
String pic = Config.domain + this._productContent!.pic!;
pic = pic.replaceAll("\\", "/");
return Container(
padding: EdgeInsets.all(10.0),
child: ListView(
children: [
AspectRatio(
aspectRatio: 16 / 12,
child: Image.network(
"${pic}",
fit: BoxFit.cover,
),
),
Container(
padding: EdgeInsets.only(top: 7),
child: Text(
"${this._productContent!.title}",
style: TextStyle(
color: Colors.black87, fontSize: ScreenAdapter.size(36)),
),
),
Container(
padding: EdgeInsets.only(top: 7),
child: Text(
"${this._productContent!.subTitle}",
style: TextStyle(
color: Colors.black54, fontSize: ScreenAdapter.size(28)),
),
),
Container(
padding: EdgeInsets.only(top: 7),
child: Row(
children: [
Expanded(
flex: 1,
child: Row(
children: [
Text("Price: "),
Text(
"${this._productContent!.price}",
style: TextStyle(
color: Colors.red,
fontSize: ScreenAdapter.size(46)),
)
],
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("Old Price: "),
Text(
"${this._productContent!.oldPrice}",
style: TextStyle(
color: Colors.black38,
fontSize: ScreenAdapter.size(32),
decoration: TextDecoration.lineThrough),
),
],
),
),
],
),
),
Container(
margin: EdgeInsets.only(top: 7),
height: ScreenAdapter.height(80.0),
child: InkWell(
onTap: () {
_attrBottomSheet();
},
child: Row(
children: [
Text(
"Select:",
style: TextStyle(fontWeight: FontWeight.bold),
),
Text("115, Black, XL, 1 pcs")
],
),
),
),
Divider(),
Container(
height: ScreenAdapter.height(80.0),
child: Row(
children: [
Text(
"Delivery Fees:",
style: TextStyle(fontWeight: FontWeight.bold),
),
Text("Free Delivery")
],
),
),
Divider(),
],
),
);
}
}
class ProductContentModel {
ProductContentItem? result;
ProductContentModel({this.result});
ProductContentModel.fromJson(Map<String, dynamic> json) {
result = json['result'] != null
? new ProductContentItem.fromJson(json['result'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.result != null) {
data['result'] = this.result!.toJson();
}
return data;
}
}
class ProductContentItem {
String? sId;
String? title;
String? cid;
Object? price;
String? oldPrice;
Object? isBest;
Object? isHot;
Object? isNew;
String? status;
String? pic;
String? content;
String? cname;
List<Attr>? attr;
String? subTitle;
Object? salecount;
ProductContentItem(
{this.sId,
this.title,
this.cid,
this.price,
this.oldPrice,
this.isBest,
this.isHot,
this.isNew,
this.status,
this.pic,
this.content,
this.cname,
this.attr,
this.subTitle,
this.salecount});
ProductContentItem.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
title = json['title'];
cid = json['cid'];
price = json['price'];
oldPrice = json['old_price'];
isBest = json['is_best'];
isHot = json['is_hot'];
isNew = json['is_new'];
status = json['status'];
pic = json['pic'];
content = json['content'];
cname = json['cname'];
if (json['attr'] != null) {
attr = <Attr>[];
json['attr'].forEach((v) {
attr!.add(new Attr.fromJson(v));
});
}
subTitle = json['sub_title'];
salecount = json['salecount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['_id'] = this.sId;
data['title'] = this.title;
data['cid'] = this.cid;
data['price'] = this.price;
data['old_price'] = this.oldPrice;
data['is_best'] = this.isBest;
data['is_hot'] = this.isHot;
data['is_new'] = this.isNew;
data['status'] = this.status;
data['pic'] = this.pic;
data['content'] = this.content;
data['cname'] = this.cname;
if (this.attr != null) {
data['attr'] = this.attr!.map((v) => v.toJson()).toList();
}
data['sub_title'] = this.subTitle;
data['salecount'] = this.salecount;
return data;
}
}
class Attr {
String? cate;
List<String>? list;
Attr({this.cate, this.list});
Attr.fromJson(Map<String, dynamic> json) {
cate = json['cate'];
list = json['list'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['cate'] = this.cate;
data['list'] = this.list;
return data;
}
}
When the exception was thrown, this was the stack:
\#0 \_ProductContentFirstState.\_getAttrWidget.\<anonymous closure (package:flutter_jdshop/pages/ProductContent/ProductContentFirst.dart:64:54)
Don't use ! without checking null. While the snippet is large, follow these steps.
Check null, and then perform operation the place you've used !. It will be like if(result!=null)result.add(new FocusItemModel.fromJson(v));
the children can be children: _getAttrItemWidget(attrItem)??[]
You aren't returning widgets from _getAttrItemWidget and others. It will be
List<Widget> _getAttrItemWidget(attrItem) { // no need to return null
List<Widget> attrItemList = [];
attrItem.list.forEach((item) {
....
print (item);
});
return attrItemList;
}
In short Don't use ! without checking null. or provide default value on null case.
Find more about null-safety.

How to increase quantity of same item click in Flutter (Bloc)

In HomePage.dart i am fetching an api data by using http package and flutter_bloc for the architecture. Now once i get the data and if i select any of the item in HomePage.dart then it will be added in MenuCart.dart. Now the problem i am facing is
If i click the same item more than one time then it shows duplicate card widget.
In MenuCart.dart i have added two buttons + & - and i increases the quantity to 3 then i decided to delete the item. Now i go back to previous screen and again i add that same item and if i go to MenuCart.dart then quantity displays 4.
What i am Expecting
It should not generate duplicate card instead quantity should increment.
After i delete an item and again add the same item then quantity must show 1.
In HomePage.dart i show very limited amount of code because of lengthy, but i have added the code of bloc and MenuCart.dart.
HomePage.dart
GestureDetector(
onTap: () {
BlocProvider.of<MenuCartBloc>(context)
.add(AddToCart(data[index])); // Here adding an item in MenuCart
},
child: Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(4.5)
child: Text('ADD'),
),
),
),
);
DishMenuTypesId.dart (Model)
class DishMenuTypesIdData {
String? id;
int? status;
int? quantity;
String? dishPrice;
String? photo;
DishMenuTypesIdData({
this.id,
this.status,
this.quantity = 1,
this.dishPrice,
this.photo,
});
DishMenuTypesIdData.fromJson(Map<String, dynamic> json) {
id = json['id']?.toString();
status = json['status']?.toInt();
quantity = 1;
dishPrice = json['dish_price']?.toString();
photo = json['photo']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['status'] = status;
data["quantity"] = quantity;
data['dish_price'] = dishPrice;
data['photo'] = photo;
return data;
}
}
class DishMenuTypesId {
String? message;
List<DishMenuTypesIdData?>? data;
DishMenuTypesId({
this.message,
this.data,
});
DishMenuTypesId.fromJson(Map<String, dynamic> json) {
message = json['message']?.toString();
if (json['data'] != null) {
final v = json['data'];
final arr0 = <DishMenuTypesIdData>[];
v.forEach((v) {
arr0.add(DishMenuTypesIdData.fromJson(v));
});
this.data = arr0;
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['message'] = message;
if (this.data != null) {
final v = this.data;
final arr0 = [];
v!.forEach((v) {
arr0.add(v!.toJson());
});
data['data'] = arr0;
}
return data;
}
}
MenuCartBloc.dart
class MenuCartBloc extends Bloc<MenuCartEvent, MenuCartState> {
MenuCartBloc() : super(MenuLoaded([]));
List<DishMenuTypesIdData> cart = [];
#override
Stream<MenuCartState> mapEventToState(
MenuCartEvent event,
) async* {
yield MenuLoading();
try {
if (event is AddToCart) {
final itemExist = cart.where((e) => e.id == event.dish.id);
if (itemExist.isNotEmpty) {
print("Not Empty"); // Here i am expecting to show qty 2 if i click an item more than 1 time
} else {
print("Empty");
cart.add(event.dish);
}
} else if (event is DeleteToCart) {
cart.remove(event.dish);
} else if (event is ClearAllCart) {
cart = [];
}
yield MenuLoaded(cart);
} catch (e) {
yield MenuFailed(e.toString());
}
}
}
MenuCartEvent.dart
abstract class MenuCartEvent {}
class AddToCart extends MenuCartEvent {
late final DishMenuTypesIdData dish;
AddToCart(this.dish);
}
class DeleteToCart extends MenuCartEvent {
late final DishMenuTypesIdData dish;
DeleteToCart(this.dish);
}
class ClearAllCart extends MenuCartEvent {}
MenuCartState.dart
abstract class MenuCartState {}
class MenuLoading extends MenuCartState {}
class MenuLoaded extends MenuCartState {
final List<DishMenuTypesIdData> cart;
MenuLoaded(this.cart);
}
class MenuFailed extends MenuCartState {
final String message;
MenuFailed(this.message);
}
MenuCart.dart
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var lang = translator.activeLanguageCode;
return Container(
child: Padding(
padding: const EdgeInsets.only(top: 20.0, left: 10.0, right: 10.0),
child: BlocBuilder<MenuCartBloc, MenuCartState>(
builder: (context, state) {
DishMenuTypesIdData _cart = DishMenuTypesIdData();
if (state is MenuLoading) {
return PlatformCircularProgressIndicator();
} else if (state is MenuLoaded) {
return SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Your Reservation Details'.tr(),
style: TextStyle(
color: TuxedoColor.blackColor,
fontSize: lang == "en" ? 14.0 : 15.0,
fontFamily: lang == "en"
? 'OpenSansBold'
: 'ArabicSemiBold'),
),
GestureDetector(
onTap: () {
BlocProvider.of<MenuCartBloc>(context)
.add(ClearAllCart());
},
child: Row(
children: [
Icon(
Icons.close,
color: TuxedoColor.redColor,
size: 15.0,
),
Text(
'Clear All'.tr(),
style: TextStyle(
color: TuxedoColor.redColor,
fontSize: 12.0,
fontFamily: lang == "en"
? 'OpenSansRegular'
: 'ArabicLight'),
),
],
),
)
],
),
SizedBox(
height: 25.0,
),
Container(
height: height * 0.3,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: state.cart.length,
itemBuilder: (BuildContext context, int index) {
_cart = state.cart[index];
return Dismissible(
key: UniqueKey(),
background: Container(
alignment: AlignmentDirectional.centerEnd,
color: TuxedoColor.redBrightColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.delete,
color: TuxedoColor.whiteColor,
),
),
),
onDismissed: (direction) {
BlocProvider.of<MenuCartBloc>(context)
.add(DeleteToCart(_cart));
setState(() {
state.cart.removeAt(index);
});
},
direction: DismissDirection.endToStart,
child: Card(
child: Row(
children: [
Stack(
children: [
ClipRRect(
borderRadius:
BorderRadius.circular(10.0),
child: CachedNetworkImage(
imageUrl:
_cart.photo!,
placeholder: (context, url) => Center(
child:
PlatformCircularProgressIndicator()),
errorWidget:
(context, url, error) =>
Icon(Icons.error),
width: width * 0.25,
height: height * 0.1,
fit: BoxFit.fill,
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
color: TuxedoColor.redColor,
borderRadius: lang == "en"
? BorderRadius.only(
bottomRight:
Radius.circular(
10),
)
: BorderRadius.only(
bottomLeft:
Radius.circular(
10),
)),
height: 25.0,
width: 45.0,
child: Padding(
padding: const EdgeInsets.only(
left: 3.0, right: 3.0),
child: FittedBox(
fit: BoxFit.fitWidth,
child: Text(
'${_cart.dishPrice!} \SR',
style: TextStyle(
color: TuxedoColor
.whiteColor,
fontFamily: lang == "en"
? 'OpenSansSemiBold'
: 'ArabicSemiBold'),
),
),
),
),
)
],
),
Padding(
padding:
const EdgeInsets.only(left: 10.0),
child: Column(
children: [
Container(
width: width * 0.6,
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Container(
height: 25.0,
child: Row(
children: [
MaterialButton(
color: TuxedoColor
.redColor,
shape: CircleBorder(),
onPressed: () {
setState(() {
var minus = state
.cart[index]
.quantity;
if (minus !=
null) {
minus--;
state
.cart[index]
.quantity = minus;
}
});
},
child: Text(
'-',
style: TextStyle(
color: Colors
.white,
fontSize: 20),
),
),
Text(_cart.quantity
.toString()),
MaterialButton(
color: TuxedoColor
.redColor,
shape: CircleBorder(),
onPressed: () {
setState(() {
var add = state
.cart[index]
.quantity;
if (add != null) {
add++;
state
.cart[index]
.quantity = add;
}
});
},
child: Text(
'+',
style: TextStyle(
color: Colors
.white,
fontSize: 20),
),
),
],
),
),
GestureDetector(
onTap: () {
BlocProvider.of<
MenuCartBloc>(
context)
.add(DeleteToCart(
state.cart[
index]));
},
child: Icon(
Icons.delete,
color:
TuxedoColor.redColor,
),
)
],
),
),
SizedBox(
height: 15.0,
)
],
),
),
],
),
),
);
}),
),
],
),
);
} else if (state is MenuFailed) {
return Center(child: Text(state.message));
}
return Container();
},
)),
);
}
Something like that should work:
final itemExist = cart.firstWhere((e) => e.id == event.dish.id, orElse: () => null);
if (itemExist != null) {
itemExist.quantity++;
print("Not Empty"); // Here i am expecting to show qty 2 if i click an item more than 1 time
} else {
print("Empty");
cart.add(event.dish);
}
Basically, we need to check whether the cart already has a dish with the same id or not. If the cart contains the dish with this id we increment dish quantity else we just add a dish to the cart.
Besides that, I see a potential problem with your code. Bloc to work properly requires either creating new instances of models or properly implementing of equals and hashCode. So even the code above may not work properly due to this problem. Be aware of it. You can fix it using this package.

only single row comes from the json api

I am beginner in flutter ,I used an API of soccer to get some information of player transferts ,so I have created a model class that contains only the information that I need to ,but when I executed, I had this error:
E/flutter ( 5073): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
E/flutter ( 5073): #0 new Transferts.fromJson (package:tl_fantasy/models/transfert_json.dart:12:26)
E/flutter ( 5073): #1 _PlayerDetailsState.getTeams.<anonymous closure>.<anonymous closure> (package:tl_fantasy/players/player_details.dart:45:45)
So I changed my model class
if i put this in the model class it works when i put it for example json['transfers'][0]['date'], but the problem is it gives me only the first result , what i need is all results, how to replace[0] by a kind of index or something? how to take all elements?
Here the json that i am trying to access:
{
"response": [
{
"player": {
"id": 35845,
"name": "Hernán Darío Burbano"
},
"update": "2020-02-06T00:08:15+00:00",
"transfers": [
{
"date": "2019-07-15",
"type": "Free",
"teams": {
"in": {
"id": 2283,
"name": "Atlas",
"logo": "https://media.api-sports.io/football/teams/2283.png"
}
}
},
{
"date": "2019-01-01",
"type": "N/A",
"teams": {
"in": {
"id": 1937,
"name": "Atletico Atlas",
"logo": "https://media.api-sports.io/football/teams/1937.png"
}
}
}
]
}
]
}
Here my model class tranfert_json:
class Response {
Player player;
String update;
List<Transfers> transfers;
Response({this.player, this.update, this.transfers});
Response.fromJson(Map<String, dynamic> json) {
player = json['player'] != null ? new Player.fromJson(json['player']) : null;
update = json['update'];
if (json['transfers'] != null) {
transfers = new List<Transfers>();
json['transfers'].forEach((v) { transfers.add(new Transfers.fromJson(v)); });
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.player != null) {
data['player'] = this.player.toJson();
}
data['update'] = this.update;
if (this.transfers != null) {
data['transfers'] = this.transfers.map((v) => v.toJson()).toList();
}
return data;
}
}
class Player {
int id;
String name;
Player({this.id, this.name});
Player.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
return data;
}
}
class Transfers {
String date;
String type;
Teams teams;
Transfers({this.date, this.type, this.teams});
Transfers.fromJson(Map<String, dynamic> json) {
date = json['date'];
type = json['type'];
teams = json['teams'] != null ? new Teams.fromJson(json['teams']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['date'] = this.date;
data['type'] = this.type;
if (this.teams != null) {
data['teams'] = this.teams.toJson();
}
return data;
}
}
class Teams {
In teamIn;
Teams({this.teamIn});
Teams.fromJson(Map<String, dynamic> json) {
teamIn = json['in'] != null ? new In.fromJson(json['in']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.teamIn != null) {
data['in'] = this.teamIn.toJson();
}
return data;
}
}
class In {
int id;
String name;
String logo;
In({this.id, this.name, this.logo});
In.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
logo = json['logo'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['logo'] = this.logo;
return data;
}
}
Here my player_details class:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:tl_fantasy/models/transfert_json.dart';
class PlayerDetails extends StatefulWidget {
int id;
String name;
String lastname;
String image;
String club;
String position;
String nationality;
String age;
int matches;
String goals;
String assists;
String saves;
PlayerDetails({this.id,this.name, this.lastname,this.image, this.club, this.position,
this.nationality, this.age, this.matches, this.goals, this.assists, this.saves});
#override
_PlayerDetailsState createState() => _PlayerDetailsState();
}
class _PlayerDetailsState extends State<PlayerDetails> {
List<Response> teams = [];
Future<void> getTeams(int id) async {
http.Response response = await http.get(
'https://v3.football.api-sports.io/transfers?player=$id',
headers: {'x-rapidapi-key': 'c52370f8295e1525b7f7ba38655e243f',
'x-rapidapi-host':'v3.football.api-sports.io'});
String body = response.body;
var data = jsonDecode(body);
List<dynamic> clubList = data['response'];
setState(() {
teams = clubList
.map((dynamic item) => Response.fromJson(item))
.toList();
});
}
#override
void initState() {
super.initState();
getTeams(widget.id);
}
#override
Widget build(BuildContext context) {
if(widget.matches == null ){
widget.matches == 0;
}
if(widget.goals == null ){
widget.goals == "0";
}
if(widget.assists == null ){
widget.assists == "0";
}
if(widget.saves == null ){
widget.saves == "0";
}
List<Stats> stats = [
Stats("Matches", widget.matches.toString() ),
Stats("Goals", widget.goals ),
Stats("Assists", widget.assists ),
Stats("Saves", widget.saves ),
];
return Scaffold(
appBar: AppBar(
title: Text("Player Details"),
backgroundColor: Colors.blue[300],
elevation: 0.0,
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.purple, Colors.blue])
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.purple, Colors.black38])),
child: ListView(
children: [
SizedBox(
height: 20,
),
Container(
margin: EdgeInsets.fromLTRB(8.0,0,8.0,0),
width: double.infinity,
child: Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child:
Row(
children: <Widget>[
Container(
height: 60,
width: 60,
child:
Image.network(this.widget.image,
),
),
const SizedBox(width:10.0),
Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget> [
Text(widget.name, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0,
)),
Text(widget.lastname, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0,
)),
const SizedBox(height: 5.0, ),
Text(widget.club, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0,
)),
const SizedBox(height: 5.0, ),
Text("Role : "+widget.position, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0, color: Colors.grey[600],
)),
const SizedBox(height: 5.0, ),
Text("Age : "+widget.age, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0, color: Colors.grey[600],
)),
const SizedBox(height: 5.0, ),
Text("Nationality : "+widget.nationality, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0, color: Colors.grey[600],
)),
],
),
],
),
),
),
),
Container(
margin: EdgeInsets.fromLTRB(10, 15, 0, 0),
child: Text(
"Season Stats",
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w900, color: Colors.white),
),
),
SizedBox(
height: 10,
),
Container(
padding: EdgeInsets.all(12.0),
child: GridView.builder(
shrinkWrap: true,
itemCount: stats.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0
),
itemBuilder: (BuildContext context, int index){
return Card(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
alignment: Alignment.topCenter,
padding: EdgeInsets.fromLTRB(0, 5, 0, 0),
child: Text(stats[index].result,style: TextStyle(fontSize: 20.0)),
),
Container(
alignment: Alignment.bottomCenter,
child: Text(stats[index].title,style: TextStyle(fontSize: 25.0)),),
]
),
),
);
},
)
),
SizedBox(
height: 10,
),
Container(
margin: EdgeInsets.fromLTRB(10, 0, 0, 10),
child: Text(
"Teams",
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w900, color: Colors.white),
),
),
SizedBox(
height: 10,
),
Container(
margin: EdgeInsets.fromLTRB(5, 0, 5, 0),
child: ListView.builder(
shrinkWrap: true,
physics : NeverScrollableScrollPhysics(),
itemBuilder: (context, index){
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child:
Row(
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(teams[index].transfers[index].teams.teamIn.logo),
),
const SizedBox(width:10.0),
Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget> [
Text(teams[index].transfers[index].teams.teamIn.name, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0,
)),
const SizedBox(height: 5.0, ),
Text("joined : "+teams[index].transfers[index].date, style: TextStyle( fontWeight:FontWeight.bold,
fontSize: 18.0, color: Colors.grey[600],
)),
],
),
],
),
),
);
},
itemCount: teams.length,
),
),
SizedBox(
height: 70,
),
],
),
)
),
);
}
}
class Stats{
String title;
String result;
Stats(this.title,this.result);
}
class Team {
String name;
String image;
String date;
Team(this.name,this.image,this.date);
}
i am trying to find a way to get all results , not just the first index of my api json
Example of nested ListviewBuilder , don't forget to add shrinkWrap inside the listview
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: teams.length,
itemBuilder: (BuildContext context, int index) {
List<Transfers> transfers = teams[index].transfers;
return ListView.builder(
shrinkWrap: true,
itemCount: transfers.length,
itemBuilder: (BuildContext context, int index) {
return Text(transfers[index].teams.teamIn.name);
},
) ;
},
),
);
}

Flutter/dart accessing listdata within another list

I have a JSON response from RestApi like this:
[{"_id":"5eee4b3630cff64ee216e4fb",
"assignee_user_id":"5eab4a435647780af311d3a7",
"task_name":"Another Test work",
"task_description":"Test Description",
"assignee_name":"Test Assignee",
"status":"assigned",
"assignment_date":"20-06-2020 11:15",
"assignor_name":"Test Assignor",
"assignor_remarks":[{"commentTime":"21-06-2020 05:17","comment":"Test Comment"}]}]
and the podo class build is like this:
import 'dart:convert';
List<Work> workFromMap(String str) => List<Work>.from(json.decode(str).map((x) => Work.fromMap(x)));
String workToMap(List<Work> data) => json.encode(List<dynamic>.from(data.map((x) => x.toMap())));
class Work {
Work({
this.id,
this.assigneeUserId,
this.taskName,
this.taskDescription,
this.assigneeName,
this.status,
this.assignmentDate,
this.assignorName,
this.assignorRemarks,
});
String id;
String assigneeUserId;
String taskName;
String taskDescription;
String assigneeName;
String status;
String assignmentDate;
String assignorName;
List<AssignorRemark> assignorRemarks;
factory Work.fromMap(Map<String, dynamic> json) => Work(
id: json["_id"],
assigneeUserId: json["assignee_user_id"],
taskName: json["task_name"],
taskDescription: json["task_description"],
assigneeName: json["assignee_name"],
status: json["status"],
assignmentDate: json["assignment_date"],
assignorRemarks: List<AssignorRemark>.from(json["assignor_remarks"].map((x) => AssignorRemark.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"_id": id,
"assignee_user_id": assigneeUserId,
"task_name": taskName,
"task_description": taskDescription,
"assignee_name": assigneeName,
"status": status,
"assignment_date": assignmentDate,
"assignor_remarks": List<dynamic>.from(assignorRemarks.map((x) => x.toMap())),
};
}
class AssignorRemark {
AssignorRemark({
this.commentTime,
this.comment,
});
String commentTime;
String comment;
factory AssignorRemark.fromMap(Map<String, dynamic> json) => AssignorRemark(
commentTime: json["commentTime"],
comment: json["comment"],
);
Map<String, dynamic> toMap() => {
"commentTime": commentTime,
"comment": comment,
};
}
and my api call is like this:
import './work.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
Future<List<Work>> fetchWork() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final userid = prefs.getString('user_id');
final response =
await http.get("https://myserver/api/work-monitor/work/?id=$userid",
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
print(response.body);
return workFromMap(response.body);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load Profile');
}
}
and my screen file content is like this:
class WorkMonitor extends StatefulWidget {
#override
_WorkMonitorState createState() => _WorkMonitorState();
}
class _WorkMonitorState extends State<WorkMonitor> {
Future<Work> futureMyWorkMonitor;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Work Monitor'),
),
body: Container(
child: FutureBuilder(
future: fetchWork(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, index) {
Work work = snapshot.data[index];
// return Text('${work.taskName}');
return Container(
padding: const EdgeInsets.all(5),
width: double.infinity,
child: Card(
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Subject: ',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
Text(
'${work.taskName}',
style: TextStyle(
fontSize: 20, color: Colors.black),
),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Description: ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
Flexible(
child: Text(
'${work.taskDescription}',
)),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Assigned Date: ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
Text(
'${work.assignmentDate}' ?? " ",
style: TextStyle(fontSize: 16),
),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Description: ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
Text(
'${work.status}' ?? "Gone",
style: TextStyle(fontSize: 16),
),
]),
Text(
'${work.assignorRemarks[index]}'
),
]),
),
),
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
);
}
}
Only issue I am facing here is, I am not able to access/iterate over comments. How I can do this?
You need to somehow also iterate through each comment inside each list item. One way (probably only for a small amount of remarks), would be a Column (or if you need scrolling you could use a SingleChildScrollView with a Column). Example:
Column(
children: work.assignorRemarks.map<Text>((remark) {
return Text(remark.comment);
}).toList()
)
If you have a lot of items you are probably better of using a nested ListView

is there anybody who can help me parsing json data to another page using Hero Widget?

I want somebody to help me to parse JSON data to another Page in HERO widget in the flutter. I parse data to the first page but failed to parse data to another page
make the model for JSON data use PODO style
like this which will deal with all JSON data to parsed to the view class.
class ProductResponse{
List<ProductDetail> results;
ProductResponse({this.results});
ProductResponse.fromJson(Map<String,dynamic> json){
if(json['results'] !=null){
results=new List<ProductDetail>();
json['results'].forEach((v){
results.add(new ProductDetail.fromJson(v));
});
}
}
Map<String,dynamic> toJson(){
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.results != null) {
data['results'] = this.results.map((v) => v.toJson()).toList();
}
return data;
}
}
class ProductDetail{
int phone;
int price;
int qty;
String amount;
String place;
String image_name;
String image_url;
String vendor_name;
String description;
String category;
String productName;
String images;
ProductDetail({this.phone, this.price, this.qty, this.amount, this.vendor_name,
this.description, this.category, this.productName, this.images,this.image_name,this.image_url,this.place});
ProductDetail.fromJson(Map<String,dynamic> json){
phone = json['phone'];
price = json["price"];
qty = json['qty'];
amount =json['amount'];
vendor_name =json['vendor_name'];
description = json['description'];
category = json['category'];
images = json['images'];
productName = json['productName'];
image_url =json['image_url'];
image_name =json['image_name'];
place =json['place'];
}
Map<String,dynamic> toJson(){
final Map<String,dynamic> data =new Map<String,dynamic>();
data['phone'] =this.phone;
data['price'] =this.price;
data['qty'] =this.qty;
data['amount'] =this.amount;
data['vendor_name'] =this.vendor_name;
data['description'] =this.description;
data['category'] =this.category;
data['productName'] =this.productName;
data['images'] =this.images;
data['place'] = this.place;
data['image_url'] =this.image_url;
data['image_name'] =this.image_name;
return data;
}
}
make the class which will make the request to the server
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:kilimo_biashara1/Model/Dialogs.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
import 'package:kilimo_biashara1/Products/ProductPage.dart';
class Products extends StatefulWidget {
#override
_ProductsState createState() => _ProductsState();
}
class _ProductsState extends State<Products> {
String url = "put your api url here";
ProductResponse detail;// declare the class from PODO
fetchProduct() async {
var response = await http.get(url);
var decodeJson = jsonDecode(response.body);
print("response" + response.body);
setState(() {
detail = ProductResponse.fromJson(decodeJson);
});
print(detail);//debug
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: detail == null
? Center(
child: CircularProgressIndicator(),
)
: StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: detail.results.length,
itemBuilder: (BuildContext context, int index) {
return ProductPage(
detail: detail.results[index]
); //Return the product page
},
staggeredTileBuilder: (_) => StaggeredTile.fit(2),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
)
);
}
}
here are sample codes of the product page.in this product page it will show cards with few details in it
import 'package:flutter/material.dart';
import 'package:kilimo_biashara1/Products/DetailProduct.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
class ProductPage extends StatelessWidget {
final ProductDetail detail;
ProductPage({#required this.detail});
#override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4.0),
),
elevation: 4.0,
margin: EdgeInsets.all(4.0),
child: InkWell(
radius: 4.0,
child: getCardView(context),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
DetailProduct(
detail: detail,
),
),
);
},
),
);
}
//////
getCardView(BuildContext context){
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Hero(
tag: detail,//this key /tag will be the same with another page also
child: Container(
height: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
image: DecorationImage(
image: NetworkImage("${detail.image_url}"
,
),
fit: BoxFit.cover),
),
), ),
// Image.asset("images/ndz.jpg"),
Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("sold by: "+
detail.vendor_name,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
Text("product: "+
detail.productName,
softWrap: true,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
Text("price: ${detail.price} ${detail.amount}"
,
softWrap: true,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
],
),
),
],
);
}
}
when it is clicked/tapped it will direct to the other page which will provide full details about the products
import 'package:flutter/material.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
import 'package:kilimo_biashara1/payment/Payment.dart';
class DetailProduct extends StatelessWidget {
final ProductDetail detail;
DetailProduct({this.detail});
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
persistentFooterButtons: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(15.0, 5.0, 40.0, 5.0),
child: new Text("SHOPPING",
style: new TextStyle(fontSize: 25.0,color: Colors.green,fontWeight: FontWeight.bold),
),
),
new FlatButton(
child: new Icon(Icons.shopping_cart,size: 35.0,),
onPressed:(){
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext context)=>new Payment()));
} ,
),
],
body:new Scaffold(
body:ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Hero(
tag: detail,
child: Container(
height: 250.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
shape: BoxShape.rectangle,
image: DecorationImage(
image: NetworkImage(
"${detail.image_url}",
),
fit: BoxFit.cover,
),
),
),
),
// Image.asset("images/ndz.jpg"),
SizedBox(
height: 16.0,
),
Text("vendor name: "+
detail.vendor_name,
style: Theme.of(context).textTheme.title,
),
SizedBox(
height: 16.0,
),
Text("product name: "+
detail.productName,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("vendor place: "+
detail.place,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("price: ${detail.price} ${detail.amount}",
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("short description: "+
detail.description,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("Category: "+detail.category,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("contacts: ${detail.phone}",
style: Theme.of(context).textTheme.subhead,
),
],
),
),
],
),
),
),
);
}
}
after following these steps you should reach to a point where you parse data from the API with hero widget