mobdr/lib/ui/home/tab_home.dart

971 lines
37 KiB
Dart

/*
For this homepage, appBar is created at the bottom after CustomScrollView
we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime
*/
import 'dart:async';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:mobdr/model/category_for_you_model.dart';
import 'package:mobdr/model/category_model.dart';
import 'package:mobdr/model/flashsale_model.dart';
import 'package:mobdr/model/home_banner_model.dart';
import 'package:mobdr/model/home_trending_model.dart';
import 'package:mobdr/model/last_search_model.dart';
import 'package:mobdr/model/recomended_product_model.dart';
import 'package:mobdr/ui/general/chat_us.dart';
import 'package:mobdr/ui/general/notification.dart';
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
import 'package:mobdr/ui/home/coupon.dart';
import 'package:mobdr/ui/home/flashsale.dart';
import 'package:mobdr/ui/home/last_search.dart';
import 'package:mobdr/ui/home/product_category.dart';
import 'package:mobdr/ui/home/search.dart';
import 'package:mobdr/ui/home/search_product.dart';
import 'package:mobdr/ui/reusable/reusable_widget.dart';
import 'package:mobdr/ui/reusable/cache_image_network.dart';
import 'package:mobdr/ui/reusable/global_function.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:mobdr/config/constant.dart';
import 'package:mobdr/config/global_style.dart';
class TabHomePage extends StatefulWidget {
@override
_TabHomePageState createState() => _TabHomePageState();
}
class _TabHomePageState extends State<TabHomePage>
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
// initialize global function and reusable widget
final _globalFunction = GlobalFunction();
final _reusableWidget = ReusableWidget();
int _currentImageSlider = 0;
late ScrollController _scrollController;
Color _topIconColor = Colors.white;
Color _topSearchColor = Colors.white;
late AnimationController _topColorAnimationController;
late Animation _appBarColor;
SystemUiOverlayStyle _appBarSystemOverlayStyle = SystemUiOverlayStyle.light;
Timer? _flashsaleTimer;
late int _flashsaleSecond;
void _startFlashsaleTimer() {
const period = const Duration(seconds: 1);
_flashsaleTimer = Timer.periodic(period, (timer) {
setState(() {
_flashsaleSecond--;
});
if (_flashsaleSecond == 0) {
_cancelFlashsaleTimer();
Fluttertoast.showToast(
msg: 'Flash sale is over', toastLength: Toast.LENGTH_LONG);
}
});
}
void _cancelFlashsaleTimer() {
if (_flashsaleTimer != null) {
_flashsaleTimer?.cancel();
_flashsaleTimer = null;
}
}
// keep the state to do not refresh when switch navbar
@override
bool get wantKeepAlive => true;
@override
void initState() {
_setupAnimateAppbar();
// set how many times left for flashsale
var timeNow = DateTime.now();
// 8000 second = 2 hours 13 minutes 20 second for flashsale timer
var flashsaleTime =
timeNow.add(Duration(seconds: 8000)).difference(timeNow);
_flashsaleSecond = flashsaleTime.inSeconds;
_startFlashsaleTimer();
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
_topColorAnimationController.dispose();
_cancelFlashsaleTimer();
super.dispose();
}
void _setupAnimateAppbar() {
// use this function and paramater to animate top bar
_topColorAnimationController =
AnimationController(vsync: this, duration: Duration(seconds: 0));
_appBarColor = ColorTween(begin: Colors.transparent, end: Colors.white)
.animate(_topColorAnimationController);
_scrollController = ScrollController()
..addListener(() {
_topColorAnimationController.animateTo(_scrollController.offset / 120);
// if scroll for above 150, then change app bar color to white, search button to dark, and top icon color to dark
// if scroll for below 150, then change app bar color to transparent, search button to white and top icon color to light
if (_scrollController.hasClients &&
_scrollController.offset > (150 - kToolbarHeight)) {
if (_topIconColor != BLACK_GREY) {
_topIconColor = BLACK_GREY;
_topSearchColor = Colors.grey[100]!;
_appBarSystemOverlayStyle = SystemUiOverlayStyle.dark;
}
} else {
if (_topIconColor != Colors.white) {
_topIconColor = Colors.white;
_topSearchColor = Colors.white;
_appBarSystemOverlayStyle = SystemUiOverlayStyle.light;
}
}
});
}
@override
Widget build(BuildContext context) {
// if we used AutomaticKeepAliveClientMixin, we must call super.build(context);
super.build(context);
final double boxImageSize = (MediaQuery.of(context).size.width / 3);
final double categoryForYouHeightShort = boxImageSize;
final double categoryForYouHeightLong = (boxImageSize * 2);
return Scaffold(
body: Stack(
children: [
CustomScrollView(
controller: _scrollController,
slivers: [
SliverList(
delegate: SliverChildListDelegate([
_createHomeBannerSlider(),
_createCoupon(),
_createGridCategory(),
Container(
margin: EdgeInsets.only(top: 10, left: 16, right: 16),
child: Text('Flash Sale', style: GlobalStyle.sectionTitle),
),
Container(
margin: EdgeInsets.only(top: 4, left: 16, right: 16),
child: Row(
children: [
Text('Flash sale end in ',
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 13,
color: CHARCOAL)),
_buildFlashsaleTime(),
Expanded(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FlashSalePage(
seconds: _flashsaleSecond)));
},
child: Text('View All',
style: GlobalStyle.viewAll,
textAlign: TextAlign.end),
),
)
],
),
),
Container(
margin: EdgeInsets.only(top: 16),
height: boxImageSize *
GlobalStyle.horizontalProductHeightMultiplication,
child: ListView.builder(
padding: EdgeInsets.only(left: 12, right: 12),
scrollDirection: Axis.horizontal,
itemCount: flashsaleData.length,
itemBuilder: (BuildContext context, int index) {
return _buildFlashsaleCard(index, boxImageSize);
},
),
),
Container(
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
child:
Text('Trending Product', style: GlobalStyle.sectionTitle),
),
Container(
margin: EdgeInsets.fromLTRB(12, 0, 12, 0),
child: GridView.count(
padding: EdgeInsets.fromLTRB(0, 8, 0, 0),
primary: false,
childAspectRatio: 4 / 1.6,
shrinkWrap: true,
crossAxisSpacing: 2,
mainAxisSpacing: 2,
crossAxisCount: 2,
children: List.generate(homeTrendingData.length, (index) {
return _buildTrendingProductCard(index);
}),
),
),
Container(
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Last Search', style: GlobalStyle.sectionTitle),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LastSearchPage()));
},
child: Text('View All',
style: GlobalStyle.viewAll,
textAlign: TextAlign.end),
)
],
),
),
Container(
margin: EdgeInsets.only(top: 16),
height: boxImageSize *
GlobalStyle.horizontalProductHeightMultiplication,
child: ListView.builder(
padding: EdgeInsets.only(left: 12, right: 12),
scrollDirection: Axis.horizontal,
itemCount: lastSearchData.length,
itemBuilder: (BuildContext context, int index) {
return _buildLastSearchCard(index, boxImageSize);
},
),
),
Container(
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
child:
Text('Category For You', style: GlobalStyle.sectionTitle),
),
_createCategoryForYou(boxImageSize, categoryForYouHeightShort,
categoryForYouHeightLong),
Container(
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
child: Text('Recomended Product',
style: GlobalStyle.sectionTitle),
),
CustomScrollView(
shrinkWrap: true,
primary: false,
slivers: <Widget>[
SliverPadding(
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
sliver: SliverGrid(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: GlobalStyle.gridDelegateRatio,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return _buildRecomendedProductCard(index);
},
childCount: recomendedProductData.length,
),
),
),
]),
])),
],
),
// Create AppBar with Animation
Container(
height: AppBar().preferredSize.height +
MediaQuery.of(context).padding.top -
20 +
22,
child: AnimatedBuilder(
animation: _topColorAnimationController,
builder: (context, child) => AppBar(
automaticallyImplyLeading: false,
backgroundColor: _appBarColor.value,
systemOverlayStyle: _appBarSystemOverlayStyle,
elevation: GlobalStyle.appBarElevation,
title: Container(
child: TextButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) => _topSearchColor,
),
overlayColor:
MaterialStateProperty.all(Colors.transparent),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
)),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchPage()));
},
child: Row(
children: [
SizedBox(width: 8),
Icon(
Icons.search,
color: Colors.grey[500],
size: 18,
),
SizedBox(width: 8),
Text(
'Search Product',
style: TextStyle(
color: Colors.grey[500],
fontWeight: FontWeight.normal),
)
],
)),
),
actions: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatUsPage()));
},
child: Icon(Icons.email, color: _topIconColor)),
IconButton(
icon: _reusableWidget.customNotifIcon(
count: 8, notifColor: _topIconColor),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NotificationPage()));
}),
],
),
),
)
],
),
);
}
Widget _createHomeBannerSlider() {
return Column(
children: [
CarouselSlider(
items: homeBannerData
.map((item) => Container(
child: buildCacheNetworkImage(
width: 0, height: 0, url: item.image),
))
.toList(),
options: CarouselOptions(
aspectRatio: 8 / 6,
viewportFraction: 1.0,
autoPlay: true,
autoPlayInterval: Duration(seconds: 6),
autoPlayAnimationDuration: Duration(milliseconds: 300),
enlargeCenterPage: false,
onPageChanged: (index, reason) {
setState(() {
_currentImageSlider = index;
});
}),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: homeBannerData.map((item) {
int index = homeBannerData.indexOf(item);
return Container(
width: 8.0,
height: 8.0,
margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _currentImageSlider == index
? PRIMARY_COLOR
: Colors.grey[300],
),
);
}).toList(),
),
],
);
}
Widget _createCoupon() {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => CouponPage()));
},
child: Container(
padding: EdgeInsets.all(12),
margin: EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: SOFT_BLUE, borderRadius: BorderRadius.circular(5)),
child: Row(
children: [
Expanded(
child: Container(
child: Text(
'There are 10 coupon waiting',
style: TextStyle(
fontSize: 14,
color: Color(0xffffffff),
fontWeight: FontWeight.bold),
),
),
),
Icon(Icons.local_offer, color: Colors.white)
],
),
),
);
}
Widget _buildFlashsaleTime() {
int hour = _flashsaleSecond ~/ 3600;
int minute = _flashsaleSecond % 3600 ~/ 60;
int second = _flashsaleSecond % 60;
return Row(
children: [
Container(
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
decoration: BoxDecoration(
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
child: Text(_globalFunction.formatTime(hour),
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold)),
),
Text(' : ',
style: TextStyle(
color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)),
Container(
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
decoration: BoxDecoration(
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
child: Text(_globalFunction.formatTime(minute),
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold)),
),
Text(' : ',
style: TextStyle(
color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)),
Container(
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
decoration: BoxDecoration(
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
child: Text(_globalFunction.formatTime(second),
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold)),
)
],
);
}
Widget _createGridCategory() {
return GridView.count(
padding: EdgeInsets.fromLTRB(16, 16, 16, 0),
primary: false,
childAspectRatio: 1.1,
shrinkWrap: true,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
crossAxisCount: 4,
children: List.generate(categoryData.length, (index) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductCategoryPage(
categoryId: categoryData[index].id,
categoryName: categoryData[index].name)));
},
child: Column(children: [
buildCacheNetworkImage(
width: 40,
height: 40,
url: categoryData[index].image,
plColor: Colors.transparent),
Flexible(
child: Container(
margin: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Text(
categoryData[index].name,
style: TextStyle(
color: CHARCOAL,
fontWeight: FontWeight.normal,
fontSize: 12,
),
textAlign: TextAlign.center,
),
),
)
]));
}),
);
}
Widget _buildFlashsaleCard(index, boxImageSize) {
return Container(
width: boxImageSize + 10,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 2,
color: Colors.white,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailPage(
name: flashsaleData[index].name,
image: flashsaleData[index].image,
price: flashsaleData[index].price,
rating: 4,
review: 45,
sale: flashsaleData[index].sale)));
},
child: Column(
children: <Widget>[
Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
child: buildCacheNetworkImage(
width: boxImageSize + 10,
height: boxImageSize + 10,
url: flashsaleData[index].image)),
Positioned(
right: 0,
top: 10,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6),
bottomLeft: Radius.circular(6))),
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
child: Text(
flashsaleData[index].discount.toString() + '%',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12)),
),
)
],
),
Container(
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
flashsaleData[index].name,
style: GlobalStyle.productName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
'\$ ' +
_globalFunction.removeDecimalZeroFormat(
flashsaleData[index].price),
style: GlobalStyle.productPriceDiscounted),
),
Container(
margin: EdgeInsets.only(top: 2),
child: Text(
'\$ ' +
_globalFunction.removeDecimalZeroFormat(
((100 - flashsaleData[index].discount) *
flashsaleData[index].price /
100)),
style: GlobalStyle.productPrice),
)
],
),
)
],
),
),
),
);
}
Widget _buildTrendingProductCard(index) {
return GestureDetector(
onTap: () {
StatefulWidget menuPage =
SearchProductPage(words: homeTrendingData[index].name);
Navigator.push(
context, MaterialPageRoute(builder: (context) => menuPage));
},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 2,
color: Colors.white,
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10)),
child: buildCacheNetworkImage(
width:
(MediaQuery.of(context).size.width / 2) * (1.6 / 4) -
12 -
1,
height:
(MediaQuery.of(context).size.width / 2) * (1.6 / 4) -
12 -
1,
url: homeTrendingData[index].image)),
Expanded(
child: Container(
margin: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(homeTrendingData[index].name,
style: TextStyle(
fontSize: 11, fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text(homeTrendingData[index].sale + ' Product',
style: TextStyle(fontSize: 9, color: BLACK_GREY))
],
),
),
)
],
)),
);
}
Widget _buildLastSearchCard(index, boxImageSize) {
return Container(
width: boxImageSize + 10,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 2,
color: Colors.white,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailPage(
name: lastSearchData[index].name,
image: lastSearchData[index].image,
price: lastSearchData[index].price,
rating: lastSearchData[index].rating,
review: lastSearchData[index].review,
sale: lastSearchData[index].sale)));
},
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
child: buildCacheNetworkImage(
width: boxImageSize + 10,
height: boxImageSize + 10,
url: lastSearchData[index].image)),
Container(
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
lastSearchData[index].name,
style: GlobalStyle.productName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
'\$ ' +
_globalFunction.removeDecimalZeroFormat(
lastSearchData[index].price),
style: GlobalStyle.productPrice),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
children: [
_reusableWidget.createRatingBar(
rating: lastSearchData[index].rating, size: 12),
Text(
'(' +
lastSearchData[index].review.toString() +
')',
style: GlobalStyle.productTotalReview)
],
),
)
],
),
),
],
),
),
),
);
}
Widget _createCategoryForYou(
boxImageSize, categoryForYouHeightShort, categoryForYouHeightLong) {
return Container(
margin: EdgeInsets.only(top: 8),
width: MediaQuery.of(context).size.width,
height: categoryForYouHeightLong,
child: Row(
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductCategoryPage(
categoryId: categoryForYouData[0].id,
categoryName: categoryData[0].name)));
},
child: Container(
width: boxImageSize,
height: categoryForYouHeightLong,
child: buildCacheNetworkImage(
width: 0, height: 0, url: categoryForYouData[0].image),
),
),
Column(
children: [
Row(
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductCategoryPage(
categoryId: categoryForYouData[1].id,
categoryName: categoryData[1].name)));
},
child: Container(
width: boxImageSize,
height: categoryForYouHeightShort,
child: buildCacheNetworkImage(
width: 0,
height: 0,
url: categoryForYouData[1].image),
),
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductCategoryPage(
categoryId: categoryForYouData[2].id,
categoryName: categoryData[2].name)));
},
child: Container(
width: boxImageSize,
height: categoryForYouHeightShort,
child: buildCacheNetworkImage(
width: 0,
height: 0,
url: categoryForYouData[2].image),
),
)
],
),
Row(
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductCategoryPage(
categoryId: categoryForYouData[3].id,
categoryName: categoryData[3].name)));
},
child: Container(
width: boxImageSize,
height: categoryForYouHeightShort,
child: buildCacheNetworkImage(
width: 0,
height: 0,
url: categoryForYouData[3].image),
),
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductCategoryPage(
categoryId: categoryForYouData[4].id,
categoryName: categoryData[4].name)));
},
child: Container(
width: boxImageSize,
height: categoryForYouHeightShort,
child: buildCacheNetworkImage(
width: 0,
height: 0,
url: categoryForYouData[4].image),
),
)
],
)
],
)
],
),
);
}
Widget _buildRecomendedProductCard(index) {
final double boxImageSize =
((MediaQuery.of(context).size.width) - 24) / 2 - 12;
return Container(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 2,
color: Colors.white,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailPage(
name: recomendedProductData[index].name,
image: recomendedProductData[index].image,
price: recomendedProductData[index].price,
rating: recomendedProductData[index].rating,
review: recomendedProductData[index].review,
sale: recomendedProductData[index].sale)));
},
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
child: buildCacheNetworkImage(
width: boxImageSize,
height: boxImageSize,
url: recomendedProductData[index].image)),
Container(
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
recomendedProductData[index].name,
style: GlobalStyle.productName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'\$ ' +
_globalFunction.removeDecimalZeroFormat(
recomendedProductData[index].price),
style: GlobalStyle.productPrice),
Text(
recomendedProductData[index].sale.toString() +
' Sale',
style: TextStyle(fontSize: 11, color: SOFT_GREY))
],
),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
children: [
Icon(Icons.location_on, color: SOFT_GREY, size: 12),
Text(' ' + recomendedProductData[index].location,
style: GlobalStyle.productSale)
],
),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
children: [
_reusableWidget.createRatingBar(
rating: recomendedProductData[index].rating,
size: 12),
Text(
'(' +
recomendedProductData[index]
.review
.toString() +
')',
style: GlobalStyle.productTotalReview)
],
),
)
],
),
),
],
),
),
),
);
}
}