From 25623281460b6f32e547e8b73e48438537dbf9b0 Mon Sep 17 00:00:00 2001 From: Frederik Benoist Date: Sat, 1 Apr 2023 21:02:43 +0200 Subject: [PATCH] feat: photo_detail tag --- lib/config/global_style.dart | 6 + lib/objectbox.dart | 19 +- lib/ui/home/photo_detail.dart | 637 ++++++++-------------------------- pubspec.lock | 8 + pubspec.yaml | 5 +- 5 files changed, 183 insertions(+), 492 deletions(-) diff --git a/lib/config/global_style.dart b/lib/config/global_style.dart index afca780..807d719 100644 --- a/lib/config/global_style.dart +++ b/lib/config/global_style.dart @@ -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); } diff --git a/lib/objectbox.dart b/lib/objectbox.dart index 206db36..35e5657 100644 --- a/lib/objectbox.dart +++ b/lib/objectbox.dart @@ -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().put(_VisiteTag); } + List 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().put(_PhotoTypology); } + List getPhotoTypologiesLabels() { + final query = photoTypologyBox.query().order(PhotoTypology_.ordre).build(); + final photoTypologies = query.find(); + return photoTypologies + .map((photoTypology) => photoTypology.libelle) + .toList(); + } + Stream> getPhotoTypologies() { // Query for all Typologies, sorted by their order. // https://docs.objectbox.io/queries diff --git a/lib/ui/home/photo_detail.dart b/lib/ui/home/photo_detail.dart index 6b017f4..ca58909 100644 --- a/lib/ui/home/photo_detail.dart +++ b/lib/ui/home/photo_detail.dart @@ -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 { final _globalFunction = GlobalFunction(); final _reusableWidget = ReusableWidget(); - final List _imgProductSlider = []; - int _currentImageSlider = 0; + // Typology list + List _typologyList = objectbox.getPhotoTypologiesLabels(); + int _typologyIndex = 0; - // size data - List _sizeList = ['XS', 'S', 'M', 'L', 'XL', 'XXL']; - int _sizeIndex = 0; - - // color data - List _colorList = [ - 'Red', - 'Black', - 'Green', - 'White', - 'Blue', - 'Yellow', - 'Pink' - ]; - int _colorIndex = 0; - - // wishlist - bool _isLove = false; + List _chickenParts = []; + int _maxChickenParts = 2; // shopping cart count int _shoppingCartCount = 3; + // tag(s) list + final List tagsList = objectbox.getVisiteTagsLabels(); + List 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 { @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 { 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 { ); } - 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 { 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,101 +281,105 @@ class _PhotoDetailPageState extends State { 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( + behavior: HitTestBehavior.translucent, onTap: () { setState(() { - _colorIndex = index; + if (_chickenParts.contains(value)) { + _chickenParts.remove(value); + } else { + if (_chickenParts.length < _maxChickenParts) { + _chickenParts.add(value); + } + } }); }, - 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)), + child: Row( + children: [ + 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 _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( + Widget _buildPhotoVisibility() { + return Container( + margin: EdgeInsets.only(top: 12), + padding: EdgeInsets.all(16), + color: Colors.white, + child: Column( + children: [ + 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: [ - 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) + Text('Visibility', style: GlobalStyle.sectionTitle), ], - )), + ), + SizedBox(height: 16), + _checboxChicken(value: 'public', primaryText: 'Public'), + Divider( + height: 32, + color: Colors.grey[400], + ), + _checboxChicken(value: 'principal', primaryText: 'Principal') + ], + ), ); } - Widget _createProductInformation() { + Widget _buildPhotoTag() { return Container( margin: EdgeInsets.only(top: 12), padding: EdgeInsets.all(16), @@ -535,261 +387,66 @@ class _PhotoDetailPageState extends State { 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))); - }, - child: Column( - children: [ - 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) - ], - ), - ) - ], - ), - ), - ], - ), - ), - ), - ); - }, - ), - ), - ], - )); - } - - Widget _createProductReview() { - 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), - ) - ], - ), - 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: [ - Divider( - height: 32, - color: Colors.grey[400], - ), - Container( - child: Column( + Text("Tags sélectionnés:"), + SizedBox(height: 8), + selectedTagsList.isEmpty + ? Text("Aucun tag sélectionné.") + : 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), - ], - ), - SizedBox(height: 4), - Text(reviewData[index].review) - ], - )) - ], - ); - })), + children: tagsList.map((tag) => Text("- $tag")).toList(), + ), + SizedBox(height: 16), + ElevatedButton( + onPressed: () => _openTagSelectionDialog(), + child: Text("Modifier"), + ), ], )); } + + Future _openTagSelectionDialog() async { + await FilterListDialog.display( + 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]), + ), + ); + },*/ + ); + } } diff --git a/pubspec.lock b/pubspec.lock index cb9258c..b5de166 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/pubspec.yaml b/pubspec.yaml index 53d0476..d5d2c9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,9 +55,12 @@ 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 + # https://pub.dev/packages/intl intl: 0.17.0 carousel_slider: 4.2.1