Skip to content

Commit 1d83e7c

Browse files
committedApr 14, 2020
add product details screen
1 parent a6e4f44 commit 1d83e7c

9 files changed

+529
-0
lines changed
 

‎assets/fonts/Roboto-Bold.ttf

166 KB
Binary file not shown.

‎assets/fonts/Roboto-Light.ttf

166 KB
Binary file not shown.

‎assets/fonts/Roboto-Regular.ttf

167 KB
Binary file not shown.

‎lib/common_widget/GridTilesCategory.dart

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class GridTilesCategory extends StatelessWidget {
2222
return InkWell(
2323
onTap: () {
2424
if (fromSubProducts) {
25+
print(slug);
2526
Navigator.push(
2627
context,
2728
MaterialPageRoute(

‎lib/common_widget/GridTilesProducts.dart

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:developer';
22
import 'package:flutter/material.dart';
3+
import 'package:flutter_ecommerce_app/screens/ProductDetailScreen.dart';
34
import 'package:flutter_ecommerce_app/screens/ProductsScreen.dart';
45
import 'package:flutter_ecommerce_app/screens/SubCategoryScreen.dart';
56

@@ -41,6 +42,13 @@ class GridTilesProducts extends StatelessWidget {
4142
)),
4243
);
4344
}*/
45+
Navigator.push(
46+
context,
47+
MaterialPageRoute(
48+
builder: (context) => ProductDetailScreen(
49+
slug: "products/" + slug + "/",
50+
)),
51+
);
4452
},
4553
child: Container(
4654
padding: EdgeInsets.only(top: 5),

‎lib/main.dart

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:flutter_ecommerce_app/common_widget/AppBarWidget.dart';
44
import 'package:flutter_ecommerce_app/common_widget/BottomNavBarWidget.dart';
55
import 'package:flutter_ecommerce_app/common_widget/DrawerWidget.dart';
66
import 'package:flutter_ecommerce_app/screens/HomeScreen.dart';
7+
import 'package:flutter_ecommerce_app/screens/ProductDetailScreen.dart';
78
import 'package:flutter_ecommerce_app/screens/ShoppingCartScreen.dart';
89
import 'package:flutter_ecommerce_app/screens/WishListScreen.dart';
910
import 'package:flutter_ecommerce_app/components/AppSignIn.dart';
@@ -17,6 +18,7 @@ class MyApp extends StatelessWidget {
1718
return MaterialApp(
1819
home: MyHomePage(),
1920
theme: ThemeData(
21+
fontFamily: 'Roboto',
2022
primaryColor: Colors.white,
2123
primaryColorDark: Colors.white,
2224
backgroundColor: Colors.white),

‎lib/models/ProductDetails.dart

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
class ProductDetails {
2+
bool success;
3+
String message;
4+
Data data;
5+
6+
ProductDetails({this.success, this.message, this.data});
7+
8+
ProductDetails.fromJson(Map<String, dynamic> json) {
9+
success = json['success'];
10+
message = json['message'];
11+
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
12+
}
13+
14+
Map<String, dynamic> toJson() {
15+
final Map<String, dynamic> data = new Map<String, dynamic>();
16+
data['success'] = this.success;
17+
data['message'] = this.message;
18+
if (this.data != null) {
19+
data['data'] = this.data.toJson();
20+
}
21+
return data;
22+
}
23+
}
24+
25+
class Data {
26+
List<Attributes> attributes;
27+
List<ProductVariants> productVariants;
28+
List<ProductSpecifications> productSpecifications;
29+
30+
Data({this.attributes, this.productVariants, this.productSpecifications});
31+
32+
Data.fromJson(Map<String, dynamic> json) {
33+
if (json['attributes'] != null) {
34+
attributes = new List<Attributes>();
35+
json['attributes'].forEach((v) {
36+
attributes.add(new Attributes.fromJson(v));
37+
});
38+
}
39+
if (json['product_variants'] != null) {
40+
productVariants = new List<ProductVariants>();
41+
json['product_variants'].forEach((v) {
42+
productVariants.add(new ProductVariants.fromJson(v));
43+
});
44+
}
45+
if (json['product_specifications'] != null) {
46+
productSpecifications = new List<ProductSpecifications>();
47+
json['product_specifications'].forEach((v) {
48+
productSpecifications.add(new ProductSpecifications.fromJson(v));
49+
});
50+
}
51+
}
52+
53+
Map<String, dynamic> toJson() {
54+
final Map<String, dynamic> data = new Map<String, dynamic>();
55+
if (this.attributes != null) {
56+
data['attributes'] = this.attributes.map((v) => v.toJson()).toList();
57+
}
58+
if (this.productVariants != null) {
59+
data['product_variants'] =
60+
this.productVariants.map((v) => v.toJson()).toList();
61+
}
62+
if (this.productSpecifications != null) {
63+
data['product_specifications'] =
64+
this.productSpecifications.map((v) => v.toJson()).toList();
65+
}
66+
return data;
67+
}
68+
}
69+
70+
class Attributes {
71+
String attributeSlug;
72+
String attributeName;
73+
List<AttributeValues> attributeValues;
74+
75+
Attributes({this.attributeSlug, this.attributeName, this.attributeValues});
76+
77+
Attributes.fromJson(Map<String, dynamic> json) {
78+
attributeSlug = json['attribute_slug'];
79+
attributeName = json['attribute_name'];
80+
if (json['attribute_values'] != null) {
81+
attributeValues = new List<AttributeValues>();
82+
json['attribute_values'].forEach((v) {
83+
attributeValues.add(new AttributeValues.fromJson(v));
84+
});
85+
}
86+
}
87+
88+
Map<String, dynamic> toJson() {
89+
final Map<String, dynamic> data = new Map<String, dynamic>();
90+
data['attribute_slug'] = this.attributeSlug;
91+
data['attribute_name'] = this.attributeName;
92+
if (this.attributeValues != null) {
93+
data['attribute_values'] =
94+
this.attributeValues.map((v) => v.toJson()).toList();
95+
}
96+
return data;
97+
}
98+
}
99+
100+
class AttributeValues {
101+
String value;
102+
int key;
103+
104+
AttributeValues({this.value, this.key});
105+
106+
AttributeValues.fromJson(Map<String, dynamic> json) {
107+
value = json['value'];
108+
key = json['key'];
109+
}
110+
111+
Map<String, dynamic> toJson() {
112+
final Map<String, dynamic> data = new Map<String, dynamic>();
113+
data['value'] = this.value;
114+
data['key'] = this.key;
115+
return data;
116+
}
117+
}
118+
119+
class ProductVariants {
120+
String sku;
121+
int variantId;
122+
String productName;
123+
int approved;
124+
dynamic minPrice;
125+
dynamic maxPrice;
126+
String productDescription;
127+
String brandName;
128+
String brandSlug;
129+
String categorySlug;
130+
int categoryId;
131+
String categoryName;
132+
List<int> attributeValues;
133+
List<String> productImages;
134+
String colorImage;
135+
136+
ProductVariants(
137+
{this.sku,
138+
this.variantId,
139+
this.productName,
140+
this.approved,
141+
this.minPrice,
142+
this.maxPrice,
143+
this.productDescription,
144+
this.brandName,
145+
this.brandSlug,
146+
this.categorySlug,
147+
this.categoryId,
148+
this.categoryName,
149+
this.attributeValues,
150+
this.productImages,
151+
this.colorImage});
152+
153+
ProductVariants.fromJson(Map<String, dynamic> json) {
154+
sku = json['sku'];
155+
variantId = json['variant_id'];
156+
productName = json['product_name'];
157+
approved = json['approved'];
158+
minPrice = json['min_price'];
159+
maxPrice = json['max_price'];
160+
productDescription = json['product_description'];
161+
brandName = json['brand_name'];
162+
brandSlug = json['brand_slug'];
163+
categorySlug = json['category_slug'];
164+
categoryId = json['category_id'];
165+
categoryName = json['category_name'];
166+
attributeValues = json['attribute_values'].cast<int>();
167+
productImages = json['product_images'].cast<String>();
168+
colorImage = json['color_image'];
169+
}
170+
171+
Map<String, dynamic> toJson() {
172+
final Map<String, dynamic> data = new Map<String, dynamic>();
173+
data['sku'] = this.sku;
174+
data['variant_id'] = this.variantId;
175+
data['product_name'] = this.productName;
176+
data['approved'] = this.approved;
177+
data['min_price'] = this.minPrice;
178+
data['max_price'] = this.maxPrice;
179+
data['product_description'] = this.productDescription;
180+
data['brand_name'] = this.brandName;
181+
data['brand_slug'] = this.brandSlug;
182+
data['category_slug'] = this.categorySlug;
183+
data['category_id'] = this.categoryId;
184+
data['category_name'] = this.categoryName;
185+
data['attribute_values'] = this.attributeValues;
186+
data['product_images'] = this.productImages;
187+
data['color_image'] = this.colorImage;
188+
return data;
189+
}
190+
}
191+
192+
class ProductSpecifications {
193+
int id;
194+
String specificationName;
195+
String specificationValue;
196+
197+
ProductSpecifications(
198+
{this.id, this.specificationName, this.specificationValue});
199+
200+
ProductSpecifications.fromJson(Map<String, dynamic> json) {
201+
id = json['id'];
202+
specificationName = json['specification_name'];
203+
specificationValue = json['specification_value'];
204+
}
205+
206+
Map<String, dynamic> toJson() {
207+
final Map<String, dynamic> data = new Map<String, dynamic>();
208+
data['id'] = this.id;
209+
data['specification_name'] = this.specificationName;
210+
data['specification_value'] = this.specificationValue;
211+
return data;
212+
}
213+
}

‎lib/screens/ProductDetailScreen.dart

+300
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
import 'dart:convert';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter_ecommerce_app/common_widget/AppBarWidget.dart';
5+
import 'package:flutter_ecommerce_app/common_widget/CircularProgress.dart';
6+
import 'package:flutter_ecommerce_app/models/ProductDetails.dart';
7+
import 'package:flutter_ecommerce_app/utils/Urls.dart';
8+
import 'package:http/http.dart';
9+
10+
ProductDetails productDetails;
11+
12+
class ProductDetailScreen extends StatefulWidget {
13+
String slug;
14+
15+
ProductDetailScreen({Key key, @required this.slug}) : super(key: key);
16+
17+
@override
18+
_ProductDetailScreenState createState() => _ProductDetailScreenState();
19+
}
20+
21+
//https://api.evaly.com.bd/core/public/products/leather-backpack-for-women-6dba2a50d/
22+
//https://api.evaly.com.bd/core/public/products/special-women-kids-cotton-panjabi-kameez-by-swapons-world-combo-pack-c8648f9f9/
23+
24+
class _ProductDetailScreenState extends State<ProductDetailScreen> {
25+
@override
26+
Widget build(BuildContext context) {
27+
return Scaffold(
28+
backgroundColor: Color(0xFFfafafa),
29+
appBar: appBarWidget(context),
30+
body: FutureBuilder(
31+
future: getDetailData(widget.slug),
32+
builder: (context, AsyncSnapshot snapshot) {
33+
switch (snapshot.connectionState) {
34+
case ConnectionState.none:
35+
case ConnectionState.waiting:
36+
return CircularProgress();
37+
default:
38+
if (snapshot.hasError)
39+
return Text('Error: ${snapshot.error}');
40+
else
41+
return createDetailView(context, snapshot);
42+
}
43+
},
44+
),
45+
bottomNavigationBar: BottomNavBar(),
46+
);
47+
}
48+
}
49+
50+
class BottomNavBar extends StatelessWidget {
51+
@override
52+
Widget build(BuildContext context) {
53+
return Container(
54+
padding: EdgeInsets.only(left: 20, right: 10),
55+
child: Row(
56+
children: <Widget>[
57+
Icon(
58+
Icons.favorite_border,
59+
color: Color(0xFF5e5e5e),
60+
),
61+
Spacer(),
62+
RaisedButton(
63+
elevation: 0,
64+
shape: new RoundedRectangleBorder(
65+
borderRadius: new BorderRadius.only(
66+
topLeft: Radius.circular(10),
67+
bottomLeft: Radius.circular(10)),
68+
side: BorderSide(color: Color(0xFFfef2f2))),
69+
onPressed: () {},
70+
color: Color(0xFFfef2f2),
71+
textColor: Colors.white,
72+
child: Container(
73+
padding: EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
74+
child: Text("Add to cart".toUpperCase(),
75+
style: TextStyle(
76+
fontSize: 14,
77+
fontWeight: FontWeight.w400,
78+
color: Color(0xFFff665e))),
79+
),
80+
),
81+
RaisedButton(
82+
elevation: 0,
83+
shape: new RoundedRectangleBorder(
84+
borderRadius: new BorderRadius.only(
85+
topRight: Radius.circular(10),
86+
bottomRight: Radius.circular(10)),
87+
side: BorderSide(color: Color(0xFFff665e))),
88+
onPressed: () {},
89+
color: Color(0xFFff665e),
90+
textColor: Colors.white,
91+
child: Container(
92+
padding: EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
93+
child: Text("available at shops".toUpperCase(),
94+
style: TextStyle(
95+
fontSize: 14,
96+
fontWeight: FontWeight.w400,
97+
color: Color(0xFFFFFFFF))),
98+
),
99+
),
100+
],
101+
),
102+
);
103+
}
104+
}
105+
106+
Widget createDetailView(BuildContext context, AsyncSnapshot snapshot) {
107+
ProductDetails values = snapshot.data;
108+
return DetailScreen(
109+
productDetails: values,
110+
);
111+
}
112+
113+
class DetailScreen extends StatefulWidget {
114+
ProductDetails productDetails;
115+
116+
DetailScreen({Key key, this.productDetails}) : super(key: key);
117+
118+
@override
119+
_DetailScreenState createState() => _DetailScreenState();
120+
}
121+
122+
class _DetailScreenState extends State<DetailScreen> {
123+
@override
124+
Widget build(BuildContext context) {
125+
return SingleChildScrollView(
126+
child: Column(
127+
children: <Widget>[
128+
/*Image.network(
129+
widget.productDetails.data.productVariants[0].productImages[0]),*/
130+
Image.network( widget.productDetails.data.productVariants[0].productImages[0],fit: BoxFit.fill,
131+
loadingBuilder:(BuildContext context, Widget child,ImageChunkEvent loadingProgress) {
132+
if (loadingProgress == null) return child;
133+
return Center(
134+
child: CircularProgressIndicator(
135+
value: loadingProgress.expectedTotalBytes != null ?
136+
loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes
137+
: null,
138+
),
139+
);
140+
},
141+
),
142+
SizedBox(
143+
height: 10,
144+
),
145+
Container(
146+
padding: EdgeInsets.only(left: 15, right: 15, top: 20, bottom: 20),
147+
color: Color(0xFFFFFFFF),
148+
child: Row(
149+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
150+
children: <Widget>[
151+
Text("SKU".toUpperCase(),
152+
style: TextStyle(
153+
fontSize: 16,
154+
fontWeight: FontWeight.w700,
155+
color: Color(0xFF565656))),
156+
Text(widget.productDetails.data.productVariants[0].sku,
157+
style: TextStyle(
158+
fontSize: 16,
159+
fontWeight: FontWeight.w700,
160+
color: Color(0xFFfd0100))),
161+
Icon(
162+
Icons.arrow_forward_ios,
163+
color: Color(0xFF999999),
164+
)
165+
],
166+
),
167+
),
168+
SizedBox(
169+
height: 10,
170+
),
171+
Container(
172+
padding: EdgeInsets.only(left: 15, right: 15, top: 20, bottom: 20),
173+
color: Color(0xFFFFFFFF),
174+
child: Row(
175+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
176+
children: <Widget>[
177+
Text("Price".toUpperCase(),
178+
style: TextStyle(
179+
fontSize: 16,
180+
fontWeight: FontWeight.w700,
181+
color: Color(0xFF565656))),
182+
Text(
183+
"৳ ${(widget.productDetails.data.productVariants[0].maxPrice != null) ? widget.productDetails.data.productVariants[0].maxPrice : "Unavailable"}"
184+
.toUpperCase(),
185+
style: TextStyle(
186+
color: (widget.productDetails.data.productVariants[0]
187+
.maxPrice !=
188+
null)
189+
? Color(0xFFf67426)
190+
: Color(0xFF0dc2cd),
191+
fontFamily: 'Roboto-Light.ttf',
192+
fontSize: 20,
193+
fontWeight: FontWeight.w500)),
194+
],
195+
),
196+
),
197+
SizedBox(
198+
height: 10,
199+
),
200+
Container(
201+
alignment: Alignment.topLeft,
202+
width: double.infinity,
203+
padding: EdgeInsets.only(left: 15, right: 15, top: 20, bottom: 20),
204+
color: Color(0xFFFFFFFF),
205+
child: Column(
206+
crossAxisAlignment: CrossAxisAlignment.start,
207+
children: <Widget>[
208+
Text("Description",
209+
textAlign: TextAlign.left,
210+
style: TextStyle(
211+
fontSize: 16,
212+
fontWeight: FontWeight.w700,
213+
color: Color(0xFF565656))),
214+
SizedBox(
215+
height: 15,
216+
),
217+
Text(
218+
"${widget.productDetails.data.productVariants[0].productDescription}",
219+
textAlign: TextAlign.justify,
220+
style: TextStyle(
221+
fontSize: 16,
222+
fontWeight: FontWeight.w400,
223+
color: Color(0xFF4c4c4c))),
224+
],
225+
),
226+
),
227+
SizedBox(
228+
height: 10,
229+
),
230+
Container(
231+
alignment: Alignment.topLeft,
232+
width: double.infinity,
233+
padding: EdgeInsets.only(left: 15, right: 15, top: 20, bottom: 20),
234+
color: Color(0xFFFFFFFF),
235+
child: Column(
236+
crossAxisAlignment: CrossAxisAlignment.start,
237+
children: <Widget>[
238+
Text("Specification",
239+
textAlign: TextAlign.left,
240+
style: TextStyle(
241+
fontSize: 16,
242+
fontWeight: FontWeight.w700,
243+
color: Color(0xFF565656))),
244+
SizedBox(
245+
height: 15,
246+
),
247+
Column(
248+
children: generateProductSpecification(context),
249+
)
250+
],
251+
),
252+
)
253+
],
254+
),
255+
);
256+
}
257+
258+
List<Widget> generateProductSpecification(BuildContext context) {
259+
List<Widget> list = [];
260+
int count = 0;
261+
widget.productDetails.data.productSpecifications.forEach((specification) {
262+
Widget element = Container(
263+
height: 30,
264+
child: Row(
265+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
266+
children: <Widget>[
267+
Text(specification.specificationName,
268+
textAlign: TextAlign.left,
269+
style: TextStyle(
270+
fontSize: 14,
271+
fontWeight: FontWeight.w400,
272+
color: Color(0xFF444444))),
273+
Text(specification.specificationValue,
274+
textAlign: TextAlign.left,
275+
style: TextStyle(
276+
fontSize: 14,
277+
fontWeight: FontWeight.w400,
278+
color: Color(0xFF212121))),
279+
],
280+
),
281+
);
282+
list.add(element);
283+
count++;
284+
});
285+
return list;
286+
}
287+
}
288+
289+
Future<ProductDetails> getDetailData(String slug) async {
290+
Response response;
291+
response = await get(Urls.ROOT_URL + slug);
292+
int statusCode = response.statusCode;
293+
final body = json.decode(response.body);
294+
if (statusCode == 200) {
295+
productDetails = ProductDetails.fromJson(body);
296+
return productDetails;
297+
} else {
298+
return productDetails = ProductDetails();
299+
}
300+
}

‎pubspec.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ flutter:
8585
- family: Roboto-Light
8686
fonts:
8787
- asset: assets/Roboto-Light.ttf
88+
- family: Roboto
89+
fonts:
90+
- asset: assets/fonts/Roboto-Regular.ttf
91+
- asset: assets/fonts/Roboto-Bold.ttf
92+
- asset: assets/fonts/Roboto-Light.ttf
8893
# - family: Schyler
8994
# fonts:
9095
# - asset: fonts/Schyler-Regular.ttf

0 commit comments

Comments
 (0)
Please sign in to comment.