feat: photo_detail tag

release/mobdr-v0.0.1
Frédérik Benoist 2023-04-01 21:02:43 +02:00
parent ed37fed45d
commit 2562328146
5 changed files with 183 additions and 492 deletions

View File

@ -165,4 +165,10 @@ class GlobalStyle {
static const Icon iconTime =
Icon(Icons.access_time, size: 14, color: SOFT_GREY);
static const TextStyle detailFoodOptions =
TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold);
static const TextStyle detailFoodSubOptions =
TextStyle(color: BLACK77, fontSize: 12);
}

View File

@ -67,7 +67,7 @@ class ObjectBox {
etabBox.removeAll();
concurrentBox.removeAll();
visiteBox.removeAll();
visiteTagBox.removeAll();
//visiteTagBox.removeAll();
//photoBox.removeAll();
//photoTypologyBox.removeAll();
@ -336,6 +336,15 @@ class ObjectBox {
store.box<VisiteTag>().put(_VisiteTag);
}
List<String> getVisiteTagsLabels() {
final query = visiteTagBox
.query(VisiteTag_.langage.equals('fr'))
.order(VisiteTag_.libelle)
.build();
final visiteTags = query.find();
return visiteTags.map((VisiteTag) => VisiteTag.libelle).toList();
}
int getVisiteTagCount() {
return visiteTagBox.count();
}
@ -437,6 +446,14 @@ class ObjectBox {
store.box<PhotoTypology>().put(_PhotoTypology);
}
List<String> getPhotoTypologiesLabels() {
final query = photoTypologyBox.query().order(PhotoTypology_.ordre).build();
final photoTypologies = query.find();
return photoTypologies
.map((photoTypology) => photoTypology.libelle)
.toList();
}
Stream<List<PhotoTypology>> getPhotoTypologies() {
// Query for all Typologies, sorted by their order.
// https://docs.objectbox.io/queries

View File

@ -1,23 +1,19 @@
import 'dart:io';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:filter_list/filter_list.dart';
import 'package:mobdr/config/constant.dart';
import 'package:mobdr/config/global_style.dart';
import 'package:mobdr/model/related_product_model.dart';
import 'package:mobdr/model/review_model.dart';
import 'package:mobdr/main.dart';
import 'package:mobdr/ui/general/chat_us.dart';
import 'package:mobdr/ui/general/notification.dart';
import 'package:mobdr/ui/general/product_detail/delivery_estimated.dart';
import 'package:mobdr/ui/general/product_detail/product_description.dart';
import 'package:mobdr/ui/general/product_detail/product_review.dart';
import 'package:mobdr/ui/home/product_category.dart';
import 'package:mobdr/ui/home/search.dart';
import 'package:mobdr/ui/reusable/reusable_widget.dart';
import 'package:mobdr/ui/shopping_cart/tab_shopping_cart.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:fluttertoast/fluttertoast.dart';
class PhotoDetailPage extends StatefulWidget {
final String name;
@ -50,40 +46,22 @@ class _PhotoDetailPageState extends State<PhotoDetailPage> {
final _globalFunction = GlobalFunction();
final _reusableWidget = ReusableWidget();
final List<String> _imgProductSlider = [];
int _currentImageSlider = 0;
// Typology list
List<String> _typologyList = objectbox.getPhotoTypologiesLabels();
int _typologyIndex = 0;
// size data
List<String> _sizeList = ['XS', 'S', 'M', 'L', 'XL', 'XXL'];
int _sizeIndex = 0;
// color data
List<String> _colorList = [
'Red',
'Black',
'Green',
'White',
'Blue',
'Yellow',
'Pink'
];
int _colorIndex = 0;
// wishlist
bool _isLove = false;
List<String> _chickenParts = [];
int _maxChickenParts = 2;
// shopping cart count
int _shoppingCartCount = 3;
// tag(s) list
final List<String> tagsList = objectbox.getVisiteTagsLabels();
List<String> selectedTagsList = [];
@override
void initState() {
// image slider for the product
_imgProductSlider.add(widget.image);
_imgProductSlider.add(widget.image);
_imgProductSlider.add(widget.image);
_imgProductSlider.add(widget.image);
_imgProductSlider.add(widget.image);
super.initState();
}
@ -94,7 +72,6 @@ class _PhotoDetailPageState extends State<PhotoDetailPage> {
@override
Widget build(BuildContext context) {
final double boxImageSize = (MediaQuery.of(context).size.width / 3);
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
@ -171,13 +148,9 @@ class _PhotoDetailPageState extends State<PhotoDetailPage> {
children: [
Image.file(File(widget.image), fit: BoxFit.cover),
//_createProductSlider(),
_createProductPriceTitleEtc(),
_createProductVariant(),
_createDeliveryEstimated(),
_createProductInformation(),
_createProductDescription(),
_createProductRelated(boxImageSize),
_createProductReview(),
_buildPhotoTypology(),
_buildPhotoVisibility(),
_buildPhotoTag(),
SizedBox(height: 16)
],
),
@ -283,108 +256,7 @@ class _PhotoDetailPageState extends State<PhotoDetailPage> {
);
}
Widget _createProductSlider() {
return Stack(
children: [
CarouselSlider(
items: _imgProductSlider
.map((item) => Container(
child:
buildCacheNetworkImage(width: 0, height: 0, url: item),
))
.toList(),
options: CarouselOptions(
aspectRatio: 1,
viewportFraction: 1.0,
autoPlay: false,
enlargeCenterPage: false,
onPageChanged: (index, reason) {
setState(() {
_currentImageSlider = index;
});
}),
),
Positioned(
bottom: 16,
left: 16,
child: Container(
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
decoration: BoxDecoration(
color: SOFT_BLUE, borderRadius: BorderRadius.circular(4)),
child: Text(
(_currentImageSlider + 1).toString() +
'/' +
_imgProductSlider.length.toString(),
style: TextStyle(color: Colors.white, fontSize: 11)),
),
),
],
);
}
Widget _createProductPriceTitleEtc() {
return Container(
color: Colors.white,
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(widget.photo.toString() + ' photo(s)',
style: GlobalStyle.detailProductPrice),
GestureDetector(
onTap: () {
setState(() {
if (_isLove == true) {
_isLove = false;
Fluttertoast.showToast(
msg: "La photo n'est plus taguée comme favorite",
toastLength: Toast.LENGTH_LONG);
} else {
Fluttertoast.showToast(
msg: "La photo est taguée comme favorite",
toastLength: Toast.LENGTH_LONG);
_isLove = true;
}
});
},
child: Icon(Icons.favorite,
color: _isLove == true ? ASSENT_COLOR : BLACK_GREY,
size: 28),
)
],
),
SizedBox(height: 12),
Text(widget.name,
style: TextStyle(
fontSize: 14,
)),
SizedBox(height: 12),
IntrinsicHeight(
child: Row(
children: [
Text(widget.date,
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
VerticalDivider(
width: 30,
thickness: 1,
color: Colors.grey[300],
),
Icon(Icons.location_on, color: SOFT_GREY, size: 16),
Text('Brooklyn',
style: TextStyle(fontSize: 13, color: SOFT_GREY))
],
),
),
],
),
);
}
Widget _createProductVariant() {
Widget _buildPhotoTypology() {
return Container(
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.all(16),
@ -392,37 +264,13 @@ class _PhotoDetailPageState extends State<PhotoDetailPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Variant', style: GlobalStyle.sectionTitle),
Text('Typologie', style: GlobalStyle.sectionTitle),
SizedBox(
height: 16,
),
Row(
children: [
Text('Size : ',
style: TextStyle(color: BLACK_GREY, fontSize: 14)),
Text(_sizeList[_sizeIndex],
style: TextStyle(fontWeight: FontWeight.bold)),
],
),
Wrap(
children: List.generate(_sizeList.length, (index) {
return radioSize(_sizeList[index], index);
}),
),
SizedBox(
height: 16,
),
Row(
children: [
Text('Color : ',
style: TextStyle(color: BLACK_GREY, fontSize: 14)),
Text(_colorList[_colorIndex],
style: TextStyle(fontWeight: FontWeight.bold)),
],
),
Wrap(
children: List.generate(_colorList.length, (index) {
return radioColor(_colorList[index], index);
children: List.generate(_typologyList.length, (index) {
return radioSize(_typologyList[index], index);
}),
),
],
@ -433,363 +281,172 @@ class _PhotoDetailPageState extends State<PhotoDetailPage> {
return GestureDetector(
onTap: () {
setState(() {
_sizeIndex = index;
_typologyIndex = index;
});
},
child: Container(
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
margin: EdgeInsets.only(right: 8, top: 8),
decoration: BoxDecoration(
color: _sizeIndex == index ? SOFT_BLUE : Colors.white,
color: _typologyIndex == index ? SOFT_BLUE : Colors.white,
border: Border.all(
width: 1,
color: _sizeIndex == index ? SOFT_BLUE : Colors.grey[300]!),
color: _typologyIndex == index ? SOFT_BLUE : Colors.grey[300]!),
borderRadius: BorderRadius.all(
Radius.circular(10) // <--- border radius here
)),
child: Text(txt,
style: TextStyle(
color: _sizeIndex == index ? Colors.white : CHARCOAL)),
color: _typologyIndex == index ? Colors.white : CHARCOAL)),
),
);
}
Widget radioColor(String txt, int index) {
Widget _checboxChicken({value = 'breast', primaryText = 'Chicken Breast'}) {
return GestureDetector(
onTap: () {
setState(() {
_colorIndex = index;
});
},
child: Container(
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
margin: EdgeInsets.only(right: 8, top: 8),
decoration: BoxDecoration(
color: _colorIndex == index ? SOFT_BLUE : Colors.white,
border: Border.all(
width: 1,
color: _colorIndex == index ? SOFT_BLUE : Colors.grey[300]!),
borderRadius: BorderRadius.all(
Radius.circular(10) // <--- border radius here
)),
child: Text(txt,
style: TextStyle(
color: _colorIndex == index ? Colors.white : CHARCOAL)),
),
);
}
Widget _createDeliveryEstimated() {
return GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DeliveryEstimatedPage()));
},
child: Container(
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.all(16),
color: Colors.white,
child: Row(
children: [
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Delivery', style: GlobalStyle.sectionTitle),
SizedBox(
height: 16,
),
RichText(
text: new TextSpan(
// Note: Styles for TextSpans must be explicitly defined.
// Child text spans will inherit styles from parent
style: new TextStyle(
fontSize: 15.5,
color: BLACK_GREY,
),
children: <TextSpan>[
new TextSpan(
text:
'Calculate the estimated cost for shipping goods to '),
new TextSpan(
text: 'West New York, NJ',
style:
new TextStyle(fontWeight: FontWeight.bold)),
],
),
),
],
),
),
Icon(Icons.chevron_right, size: 36, color: CHARCOAL)
],
)),
);
}
Widget _createProductInformation() {
return Container(
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.all(16),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Information', style: GlobalStyle.sectionTitle),
SizedBox(
height: 16,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Weight', style: TextStyle(color: BLACK_GREY)),
Text('300 Gram', style: TextStyle(color: BLACK_GREY))
],
),
SizedBox(
height: 12,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Condition', style: TextStyle(color: BLACK_GREY)),
Text('Second', style: TextStyle(color: BLACK_GREY))
],
),
SizedBox(
height: 12,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Category', style: TextStyle(color: BLACK_GREY)),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductCategoryPage(
categoryId: 3, categoryName: 'Electronic')));
},
child: Text('Electronic', style: TextStyle(color: SOFT_BLUE)),
)
],
),
],
));
}
Widget _createProductDescription() {
return Container(
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.all(16),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Description',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
SizedBox(
height: 16,
),
Text(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nQuisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales.'),
SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDescriptionPage(
name: widget.name, image: widget.image)));
},
child: Center(
child: Text('Read More', style: TextStyle(color: SOFT_BLUE)),
),
),
],
));
}
Widget _createProductRelated(boxImageSize) {
return Container(
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.only(bottom: 16),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.all(16),
child: Text('Related Product', style: GlobalStyle.sectionTitle),
),
Container(
height: boxImageSize *
GlobalStyle.horizontalProductHeightMultiplication,
child: ListView.builder(
padding: EdgeInsets.only(left: 12, right: 12),
scrollDirection: Axis.horizontal,
itemCount: relatedProductData.length,
itemBuilder: (BuildContext context, int index) {
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) => PhotoDetailPage(
name: relatedProductData[index].name,
image: relatedProductData[index].image,
price: relatedProductData[index].price,
rating: relatedProductData[index].rating,
review: relatedProductData[index].review,
sale: 36)));
setState(() {
if (_chickenParts.contains(value)) {
_chickenParts.remove(value);
} else {
if (_chickenParts.length < _maxChickenParts) {
_chickenParts.add(value);
}
}
});
},
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: relatedProductData[index].image)),
Container(
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
relatedProductData[index].name,
style: GlobalStyle.productName,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
'\$ ' +
_globalFunction
.removeDecimalZeroFormat(
relatedProductData[index]
.price),
style: GlobalStyle.productPrice),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
children: [
_reusableWidget.createRatingBar(
rating: relatedProductData[index]
.rating,
size: 12),
Text(
'(' +
relatedProductData[index]
.review
.toString() +
')',
style:
GlobalStyle.productTotalReview)
],
Container(
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: (_chickenParts.contains(value))
? PRIMARY_COLOR
: BLACK77),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
child: Padding(
padding: const EdgeInsets.all(2),
child: (_chickenParts.contains(value))
? Icon(
Icons.check,
size: 12.0,
color: PRIMARY_COLOR,
)
: Icon(
Icons.check_box_outline_blank,
size: 12.0,
color: Colors.white,
),
),
),
SizedBox(width: 16),
Text(primaryText,
style: TextStyle(
fontSize: 13,
color: BLACK77,
fontWeight: (_chickenParts.contains(value))
? FontWeight.bold
: FontWeight.normal)),
],
),
),
],
),
),
),
);
},
),
),
],
));
}
Widget _createProductReview() {
Widget _buildPhotoVisibility() {
return Container(
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.all(16),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Review', style: GlobalStyle.sectionTitle),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductReviewPage()));
},
child: Text('View All',
style: GlobalStyle.viewAll.copyWith(color: SOFT_BLUE),
textAlign: TextAlign.end),
)
Text('Visibility', style: GlobalStyle.sectionTitle),
],
),
SizedBox(
height: 8,
),
Row(
children: [
_reusableWidget.createRatingBar(
rating: widget.rating, size: 12),
Text('(' + widget.review.toString() + ')',
style: TextStyle(fontSize: 11, color: SOFT_GREY))
],
),
Column(
children: List.generate(reviewData.length, (index) {
return Column(
children: [
SizedBox(height: 16),
_checboxChicken(value: 'public', primaryText: 'Public'),
Divider(
height: 32,
color: Colors.grey[400],
),
Container(
_checboxChicken(value: 'principal', primaryText: 'Principal')
],
),
);
}
Widget _buildPhotoTag() {
return Container(
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.all(16),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(reviewData[index].date,
style: TextStyle(fontSize: 13, color: SOFT_GREY)),
SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(reviewData[index].name,
style: TextStyle(
fontSize: 14, fontWeight: FontWeight.bold)),
_reusableWidget.createRatingBar(
rating: reviewData[index].rating, size: 12),
],
Text("Tags sélectionnés:"),
SizedBox(height: 8),
selectedTagsList.isEmpty
? Text("Aucun tag sélectionné.")
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: tagsList.map((tag) => Text("- $tag")).toList(),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => _openTagSelectionDialog(),
child: Text("Modifier"),
),
SizedBox(height: 4),
Text(reviewData[index].review)
],
))
],
);
})),
],
));
}
Future<void> _openTagSelectionDialog() async {
await FilterListDialog.display<String>(
context,
hideSelectedTextCount: true,
themeData: FilterListThemeData(context),
headlineText: 'Select Users',
height: 500,
listData: tagsList,
selectedListData: selectedTagsList,
choiceChipLabel: (item) => item,
validateSelectedItem: (list, val) => list!.contains(val),
controlButtons: [ControlButtonType.All, ControlButtonType.Reset],
onItemSearch: (user, query) {
/// When search query change in search bar then this method will be called
///
/// Check if items contains query
return user.toLowerCase().contains(query.toLowerCase());
},
onApplyButtonClick: (list) {
setState(() {
selectedTagsList = list!;
});
Navigator.pop(context);
},
/// uncomment below code to create custom choice chip
/*
choiceChipBuilder: (context, item, isSelected) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(
border: Border.all(
color: isSelected! ? Colors.blue[300]! : Colors.grey[300]!,
)),
child: Text(
item,
style: TextStyle(
color: isSelected ? Colors.blue[300] : Colors.grey[500]),
),
);
},*/
);
}
}

View File

@ -321,6 +321,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.4"
filter_list:
dependency: "direct main"
description:
name: filter_list
sha256: "2d80d6d19beb7847c1176e8bf6fe06d302b23eb7d1bf48c231dd730409ff9b4d"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
fixnum:
dependency: transitive
description:

View File

@ -55,6 +55,9 @@ dependencies:
permission_handler: 10.2.0
image: ^4.0.15
# https://pub.dev/packages/filter_list/install
filter_list: ^1.0.2
flutter_cache_manager: ^3.3.0
# https://pub.dev/packages/intl