Merge branches 'development' and 'fix_bugs_Mar' of https://gitlab.com/Cloud_Solution/diplomatic-quarter into fix_bugs_Mar
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 5.8 KiB |
@ -0,0 +1,37 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 299.19 299.19">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #a02228;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #c42829;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-3, .cls-4 {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-4 {
|
||||||
|
font-size: 63.99px;
|
||||||
|
font-family: GESSTwoMedium-Medium, GE SS Two;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="Layer_2" data-name="Layer 2">
|
||||||
|
<g id="Layer_1-2" data-name="Layer 1">
|
||||||
|
<g>
|
||||||
|
<rect class="cls-1" x="154.78" width="144.13" height="42.56"/>
|
||||||
|
<rect class="cls-1" y="154.77" width="42.56" height="144.13"/>
|
||||||
|
<polygon class="cls-2" points="0 154.31 154.31 0 299.19 0 0 299.19 0 154.31"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="cls-2" points="14.51 160.32 160.32 14.51 264.16 14.51 14.51 264.16 14.51 160.32"/>
|
||||||
|
<path class="cls-3" d="M259.49,16.44l-243,243.05V161.12L161.12,16.44Zm9.34-3.86H159.52L12.58,159.52V268.83L268.83,12.58Z"/>
|
||||||
|
</g>
|
||||||
|
<text class="cls-4" transform="translate(64.37 181.67) rotate(-45)">مصخ</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 312.6 292.7" style="enable-background:new 0 0 312.6 292.7;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#A02228;}
|
||||||
|
.st1{fill:#C42829;}
|
||||||
|
.st2{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2_1_">
|
||||||
|
<g id="Layer_1-2">
|
||||||
|
<g>
|
||||||
|
<rect x="152.5" y="1" class="st0" width="141.2" height="41.7"/>
|
||||||
|
<rect x="0.9" y="152.6" class="st0" width="41.7" height="141.2"/>
|
||||||
|
<polygon class="st1" points="0.9,152.2 152.1,1 294,1 0.9,294.1 "/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="15.1,158.1 158,15.2 259.7,15.2 15.1,259.8 "/>
|
||||||
|
<path class="st2" d="M255.1,17.1l-238,238.1v-96.4L158.7,17.1H255.1z M264.3,13.3H157.2L13.2,157.3v107.1
|
||||||
|
C13.2,264.4,264.3,13.3,264.3,13.3z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st2" d="M30.9,176.1l7.3-7.3c6.3-6.3,13.6-6.2,19.3-0.5c5.3,5.3,5.4,12.8-0.7,18.9l-7.4,7.4L30.9,176.1z
|
||||||
|
M49.9,186.6l3-3c3.8-3.8,3.6-7.8,0.4-11c-3.5-3.5-7.5-3.6-11.4,0.3l-2.9,2.9L49.9,186.6z"/>
|
||||||
|
<path class="st2" d="M50.8,156.2L55,152l18.5,18.5l-4.2,4.2L50.8,156.2z"/>
|
||||||
|
<path class="st2" d="M67.9,149.1c1.4,1.4,3.5,0.6,6.3-1.4c5.6-3.8,8.9-4.5,12.4-1c3.3,3.3,2.8,8.5-1.9,13.2
|
||||||
|
c-1.8,1.8-4,3.2-6.5,3.9c-2.6,0.7-5.4,0.4-7.8-0.8l1.3-5.8c3,1.9,7,1.4,9.4-1.2c2-1.9,2.5-3.8,1.4-5c-1.1-1.1-2.7-1.1-6.5,1.7
|
||||||
|
c-4.5,3.4-8.9,4.1-12.4,0.6c-2.9-2.9-2.6-7.9,1.6-12.1c3.5-3.5,7.7-4.5,11.8-2.5l-1.5,5.4c-2.8-1.4-4.9-1.2-6.9,0.8
|
||||||
|
C67.2,146.4,67,148.2,67.9,149.1z"/>
|
||||||
|
<path class="st2" d="M99.9,137c3.1-3.1,3-7,0.5-10.8l5.4-2.3c3.5,5.6,2.9,11.9-2.2,16.9c-5.9,5.9-13.8,5.6-19.2,0.2
|
||||||
|
c-5.3-5.1-5.4-13.6-0.2-18.8c0.1-0.1,0.3-0.3,0.4-0.4c4.9-4.9,10.7-5.3,14.9-2.6l-2.1,5.6c-2.8-2-6.7-1.6-9.1,0.9
|
||||||
|
c-2.9,2.9-3.1,7.6,0.3,11.1c2.8,3.1,7.7,3.4,10.8,0.6C99.7,137.2,99.8,137.1,99.9,137z"/>
|
||||||
|
<path class="st2" d="M125.2,100.4c5.3,5.1,5.2,14.1-0.3,19.3c-5.2,5.4-14.3,5.5-19.3,0.3c-5.3-5.1-5.2-14.1,0.3-19.3
|
||||||
|
C111.1,95.2,120.1,95.1,125.2,100.4z M120.9,104.6c-3.1-3.1-8-3-11.1,0c-3.1,3.1-3,8,0,11.1c3.1,3,8,3,11,0
|
||||||
|
c3.2-2.6,4-7.1,1.3-10.3C121.9,105,121.6,104.6,120.9,104.6z"/>
|
||||||
|
<path class="st2" d="M122.7,84.3l11,11c2.4,2.4,5.2,2.7,7.5,0.4c2.3-2.3,2-5.1-0.4-7.5l-11-11L134,73l11,11
|
||||||
|
c4.8,4.8,4.8,10.8,0,15.6c-4.8,4.8-10.9,4.8-15.6,0l-11-11L122.7,84.3z"/>
|
||||||
|
<path class="st2" d="M160,84l-4.2,4.2l-18.5-18.5l7.2-7.2l22.5,8.9l-15.7-15.7l4.2-4.2L174.1,70l-7.2,7.2l-22.5-8.9L160,84z"/>
|
||||||
|
<path class="st2" d="M178.6,36.1l-6.3,6.3l14.7,14.7l-4.2,4.2L168,46.7l-6.4,6.4l-3.8-3.8l16.9-16.9L178.6,36.1z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 19 KiB |
@ -1 +1 @@
|
|||||||
59a6c452ee075b50114918f17f1ad8f5
|
45a7d43b55124e1b314759554f9a14d3
|
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CLIENT_ID</key>
|
||||||
|
<string>815750722565-da8p56le8bd6apsbm9eft0jjl1rtpgkt.apps.googleusercontent.com</string>
|
||||||
|
<key>REVERSED_CLIENT_ID</key>
|
||||||
|
<string>com.googleusercontent.apps.815750722565-da8p56le8bd6apsbm9eft0jjl1rtpgkt</string>
|
||||||
|
<key>ANDROID_CLIENT_ID</key>
|
||||||
|
<string>815750722565-m14h8mkosm7cnq6uh6rhqr54dn02d705.apps.googleusercontent.com</string>
|
||||||
|
<key>API_KEY</key>
|
||||||
|
<string>AIzaSyDiXnCO00li4V7Ioa2YZ_M4ECxRsu_P9tA</string>
|
||||||
|
<key>GCM_SENDER_ID</key>
|
||||||
|
<string>815750722565</string>
|
||||||
|
<key>PLIST_VERSION</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>BUNDLE_ID</key>
|
||||||
|
<string>com.HMG.HMG-Smartphone</string>
|
||||||
|
<key>PROJECT_ID</key>
|
||||||
|
<string>api-project-815750722565</string>
|
||||||
|
<key>STORAGE_BUCKET</key>
|
||||||
|
<string>api-project-815750722565.appspot.com</string>
|
||||||
|
<key>IS_ADS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_ANALYTICS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_APPINVITE_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_GCM_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_SIGNIN_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>GOOGLE_APP_ID</key>
|
||||||
|
<string>1:815750722565:ios:328ec247a81a2ca23c186c</string>
|
||||||
|
<key>DATABASE_URL</key>
|
||||||
|
<string>https://api-project-815750722565.firebaseio.com</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,34 @@
|
|||||||
|
enum PaymentOptions {
|
||||||
|
VISA,
|
||||||
|
MASTERCARD,
|
||||||
|
MADA,
|
||||||
|
INSTALLMENT,
|
||||||
|
APPLEPAY
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PaymentOptions_ on PaymentOptions{
|
||||||
|
String value(){
|
||||||
|
switch(this){
|
||||||
|
case PaymentOptions.VISA:
|
||||||
|
return "VISA";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PaymentOptions.MASTERCARD:
|
||||||
|
return "MASTERCARD";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PaymentOptions.MADA:
|
||||||
|
return "MADA";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PaymentOptions.INSTALLMENT:
|
||||||
|
return "INSTALLMENT";
|
||||||
|
break;
|
||||||
|
case PaymentOptions.APPLEPAY:
|
||||||
|
return "APPLEPAY";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
class ResponseModel<T>{
|
||||||
|
final bool status;
|
||||||
|
final String error;
|
||||||
|
final T data;
|
||||||
|
|
||||||
|
ResponseModel({this.status, this.data, this.error});
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
class AddProductToCartRequestModel {
|
||||||
|
int quantity;
|
||||||
|
int product_id;
|
||||||
|
String shopping_cart_type;
|
||||||
|
int customer_id;
|
||||||
|
|
||||||
|
AddProductToCartRequestModel({@required this.product_id, this.customer_id, this.shopping_cart_type = "ShoppingCart", this.quantity = 1});
|
||||||
|
|
||||||
|
Map<String, dynamic> json() {
|
||||||
|
return {
|
||||||
|
"shopping_cart_item" : {
|
||||||
|
"quantity": quantity,
|
||||||
|
"product_id": product_id,
|
||||||
|
"shopping_cart_type": shopping_cart_type,
|
||||||
|
"customer_id": customer_id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateProductToCartRequestModel extends AddProductToCartRequestModel{
|
||||||
|
UpdateProductToCartRequestModel({@required int product_id, @required int customer_id, String shopping_cart_type = "ShoppingCart", int quantity = 1})
|
||||||
|
: super(customer_id: customer_id, product_id: product_id, quantity: quantity, shopping_cart_type: shopping_cart_type);
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
class PackagesCustomerRequestModel {
|
||||||
|
|
||||||
|
String email;
|
||||||
|
String phoneNumber;
|
||||||
|
|
||||||
|
PackagesCustomerRequestModel({@required this.email, @required this.phoneNumber});
|
||||||
|
|
||||||
|
Map<String, dynamic> json() {
|
||||||
|
return {
|
||||||
|
"customer" : {
|
||||||
|
"email": email,
|
||||||
|
"addresses": [{
|
||||||
|
"email": email,
|
||||||
|
"phone_number": phoneNumber
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,24 @@
|
|||||||
class OffersProductsRequestModel {
|
class OffersProductsRequestModel {
|
||||||
final int categoryId;
|
final int categoryId;
|
||||||
final int limit;
|
final int limit;
|
||||||
final int page;
|
// final int page;
|
||||||
final int sinceId;
|
int sinceId;
|
||||||
|
|
||||||
OffersProductsRequestModel({this.categoryId, this.limit, this.page, this.sinceId});
|
OffersProductsRequestModel({this.categoryId, this.limit = 50});
|
||||||
|
|
||||||
Map<String, String> toFlatMap() {
|
Map<String, String> toFlatMap() {
|
||||||
return {"limit": limit.toString(), "page": page.toString(), "sinceId": sinceId.toString(), "categoryId": categoryId.toString()};
|
return {
|
||||||
|
|
||||||
|
if(limit != null && limit > 0)
|
||||||
|
"limit": limit.toString(),
|
||||||
|
|
||||||
|
// if(page != null && page > 0)
|
||||||
|
// "page": page.toString(),
|
||||||
|
|
||||||
|
if(categoryId != null && categoryId > 0)
|
||||||
|
"categoryId": categoryId.toString(),
|
||||||
|
|
||||||
|
"sinceId": sinceId != null ? sinceId.toString() : "0",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart';
|
||||||
|
|
||||||
|
class PackagesCartItemsResponseModel {
|
||||||
|
int _quantity;
|
||||||
|
|
||||||
|
set quantity(int value) {
|
||||||
|
_quantity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _shoppingCartType;
|
||||||
|
int _productId;
|
||||||
|
PackagesResponseModel _product;
|
||||||
|
int _id;
|
||||||
|
|
||||||
|
int get quantity => _quantity;
|
||||||
|
String get shoppingCartType => _shoppingCartType;
|
||||||
|
int get productId => _productId;
|
||||||
|
PackagesResponseModel get product => _product;
|
||||||
|
int get id => _id;
|
||||||
|
|
||||||
|
PackagesCartItemsResponseModel({
|
||||||
|
int quantity,
|
||||||
|
String shoppingCartType,
|
||||||
|
int productId,
|
||||||
|
PackagesResponseModel product,
|
||||||
|
int id}){
|
||||||
|
_quantity = quantity;
|
||||||
|
_shoppingCartType = shoppingCartType;
|
||||||
|
_productId = productId;
|
||||||
|
_product = product;
|
||||||
|
_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackagesCartItemsResponseModel.fromJson(dynamic json) {
|
||||||
|
_quantity = json["quantity"];
|
||||||
|
_shoppingCartType = json["shopping_cart_type"];
|
||||||
|
_productId = json["product_id"];
|
||||||
|
_product = json["product"] != null ? PackagesResponseModel().fromJson(json["product"]) : null;
|
||||||
|
_id = json["id"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
var map = <String, dynamic>{};
|
||||||
|
map["quantity"] = _quantity;
|
||||||
|
map["shopping_cart_type"] = _shoppingCartType;
|
||||||
|
map["product_id"] = _productId;
|
||||||
|
if (_product != null) {
|
||||||
|
map["product"] = _product.toJson();
|
||||||
|
}
|
||||||
|
map["id"] = _id;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Images {
|
||||||
|
int _id;
|
||||||
|
int _pictureId;
|
||||||
|
int _position;
|
||||||
|
String _src;
|
||||||
|
dynamic _attachment;
|
||||||
|
|
||||||
|
int get id => _id;
|
||||||
|
int get pictureId => _pictureId;
|
||||||
|
int get position => _position;
|
||||||
|
String get src => _src;
|
||||||
|
dynamic get attachment => _attachment;
|
||||||
|
|
||||||
|
Images({
|
||||||
|
int id,
|
||||||
|
int pictureId,
|
||||||
|
int position,
|
||||||
|
String src,
|
||||||
|
dynamic attachment}){
|
||||||
|
_id = id;
|
||||||
|
_pictureId = pictureId;
|
||||||
|
_position = position;
|
||||||
|
_src = src;
|
||||||
|
_attachment = attachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
Images.fromJson(dynamic json) {
|
||||||
|
_id = json["id"];
|
||||||
|
_pictureId = json["picture_id"];
|
||||||
|
_position = json["position"];
|
||||||
|
_src = json["src"];
|
||||||
|
_attachment = json["attachment"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
var map = <String, dynamic>{};
|
||||||
|
map["id"] = _id;
|
||||||
|
map["picture_id"] = _pictureId;
|
||||||
|
map["position"] = _position;
|
||||||
|
map["src"] = _src;
|
||||||
|
map["attachment"] = _attachment;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// language_id : 1
|
||||||
|
/// localized_name : "Dermatology testing"
|
||||||
|
|
||||||
|
class Localized_names {
|
||||||
|
int _languageId;
|
||||||
|
String _localizedName;
|
||||||
|
|
||||||
|
int get languageId => _languageId;
|
||||||
|
String get localizedName => _localizedName;
|
||||||
|
|
||||||
|
Localized_names({
|
||||||
|
int languageId,
|
||||||
|
String localizedName}){
|
||||||
|
_languageId = languageId;
|
||||||
|
_localizedName = localizedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Localized_names.fromJson(dynamic json) {
|
||||||
|
_languageId = json["language_id"];
|
||||||
|
_localizedName = json["localized_name"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
var map = <String, dynamic>{};
|
||||||
|
map["language_id"] = _languageId;
|
||||||
|
map["localized_name"] = _localizedName;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,367 @@
|
|||||||
|
import 'PackagesCartItemsResponseModel.dart';
|
||||||
|
|
||||||
|
/// shopping_cart_items : []
|
||||||
|
/// billing_address : null
|
||||||
|
/// shipping_address : null
|
||||||
|
/// addresses : [{"first_name":null,"last_name":null,"email":"a2zzuhaib@gmil.com","company":null,"country_id":null,"country":null,"state_province_id":null,"city":null,"address1":null,"address2":null,"zip_postal_code":null,"phone_number":"0500409598","fax_number":null,"customer_attributes":null,"created_on_utc":"2021-03-11T09:40:23.8091261Z","province":null,"id":0}]
|
||||||
|
/// customer_guid : "1367e5c7-be3b-43cc-ad81-ff1fc8d3b130"
|
||||||
|
/// username : null
|
||||||
|
/// email : "a2zzuhaib@gmil.com"
|
||||||
|
/// first_name : null
|
||||||
|
/// last_name : null
|
||||||
|
/// language_id : null
|
||||||
|
/// date_of_birth : null
|
||||||
|
/// gender : null
|
||||||
|
/// admin_comment : null
|
||||||
|
/// is_tax_exempt : false
|
||||||
|
/// has_shopping_cart_items : false
|
||||||
|
/// active : true
|
||||||
|
/// deleted : false
|
||||||
|
/// is_system_account : false
|
||||||
|
/// system_name : null
|
||||||
|
/// last_ip_address : null
|
||||||
|
/// created_on_utc : "2021-03-11T09:40:23.7535859Z"
|
||||||
|
/// last_login_date_utc : null
|
||||||
|
/// last_activity_date_utc : "2021-03-11T09:40:23.7535892Z"
|
||||||
|
/// registered_in_store_id : 0
|
||||||
|
/// subscribed_to_newsletter : false
|
||||||
|
/// role_ids : []
|
||||||
|
/// id : 76823
|
||||||
|
|
||||||
|
class PackagesCustomerResponseModel {
|
||||||
|
List<PackagesCartItemsResponseModel> _shoppingCartItems;
|
||||||
|
dynamic _billingAddress;
|
||||||
|
dynamic _shippingAddress;
|
||||||
|
List<Addresses> _addresses;
|
||||||
|
String _customerGuid;
|
||||||
|
dynamic _username;
|
||||||
|
String _email;
|
||||||
|
dynamic _firstName;
|
||||||
|
dynamic _lastName;
|
||||||
|
dynamic _languageId;
|
||||||
|
dynamic _dateOfBirth;
|
||||||
|
dynamic _gender;
|
||||||
|
dynamic _adminComment;
|
||||||
|
bool _isTaxExempt;
|
||||||
|
bool _hasShoppingCartItems;
|
||||||
|
bool _active;
|
||||||
|
bool _deleted;
|
||||||
|
bool _isSystemAccount;
|
||||||
|
dynamic _systemName;
|
||||||
|
dynamic _lastIpAddress;
|
||||||
|
String _createdOnUtc;
|
||||||
|
dynamic _lastLoginDateUtc;
|
||||||
|
String _lastActivityDateUtc;
|
||||||
|
int _registeredInStoreId;
|
||||||
|
bool _subscribedToNewsletter;
|
||||||
|
List<int> _roleIds;
|
||||||
|
int _id;
|
||||||
|
|
||||||
|
List<dynamic> get shoppingCartItems => _shoppingCartItems;
|
||||||
|
dynamic get billingAddress => _billingAddress;
|
||||||
|
dynamic get shippingAddress => _shippingAddress;
|
||||||
|
List<Addresses> get addresses => _addresses;
|
||||||
|
String get customerGuid => _customerGuid;
|
||||||
|
dynamic get username => _username;
|
||||||
|
String get email => _email;
|
||||||
|
dynamic get firstName => _firstName;
|
||||||
|
dynamic get lastName => _lastName;
|
||||||
|
dynamic get languageId => _languageId;
|
||||||
|
dynamic get dateOfBirth => _dateOfBirth;
|
||||||
|
dynamic get gender => _gender;
|
||||||
|
dynamic get adminComment => _adminComment;
|
||||||
|
bool get isTaxExempt => _isTaxExempt;
|
||||||
|
bool get hasShoppingCartItems => _hasShoppingCartItems;
|
||||||
|
bool get active => _active;
|
||||||
|
bool get deleted => _deleted;
|
||||||
|
bool get isSystemAccount => _isSystemAccount;
|
||||||
|
dynamic get systemName => _systemName;
|
||||||
|
dynamic get lastIpAddress => _lastIpAddress;
|
||||||
|
String get createdOnUtc => _createdOnUtc;
|
||||||
|
dynamic get lastLoginDateUtc => _lastLoginDateUtc;
|
||||||
|
String get lastActivityDateUtc => _lastActivityDateUtc;
|
||||||
|
int get registeredInStoreId => _registeredInStoreId;
|
||||||
|
bool get subscribedToNewsletter => _subscribedToNewsletter;
|
||||||
|
List<dynamic> get roleIds => _roleIds;
|
||||||
|
int get id => _id;
|
||||||
|
|
||||||
|
PackagesCustomerResponseModel({
|
||||||
|
List<dynamic> shoppingCartItems,
|
||||||
|
dynamic billingAddress,
|
||||||
|
dynamic shippingAddress,
|
||||||
|
List<Addresses> addresses,
|
||||||
|
String customerGuid,
|
||||||
|
dynamic username,
|
||||||
|
String email,
|
||||||
|
dynamic firstName,
|
||||||
|
dynamic lastName,
|
||||||
|
dynamic languageId,
|
||||||
|
dynamic dateOfBirth,
|
||||||
|
dynamic gender,
|
||||||
|
dynamic adminComment,
|
||||||
|
bool isTaxExempt,
|
||||||
|
bool hasShoppingCartItems,
|
||||||
|
bool active,
|
||||||
|
bool deleted,
|
||||||
|
bool isSystemAccount,
|
||||||
|
dynamic systemName,
|
||||||
|
dynamic lastIpAddress,
|
||||||
|
String createdOnUtc,
|
||||||
|
dynamic lastLoginDateUtc,
|
||||||
|
String lastActivityDateUtc,
|
||||||
|
int registeredInStoreId,
|
||||||
|
bool subscribedToNewsletter,
|
||||||
|
List<dynamic> roleIds,
|
||||||
|
int id}){
|
||||||
|
_shoppingCartItems = shoppingCartItems;
|
||||||
|
_billingAddress = billingAddress;
|
||||||
|
_shippingAddress = shippingAddress;
|
||||||
|
_addresses = addresses;
|
||||||
|
_customerGuid = customerGuid;
|
||||||
|
_username = username;
|
||||||
|
_email = email;
|
||||||
|
_firstName = firstName;
|
||||||
|
_lastName = lastName;
|
||||||
|
_languageId = languageId;
|
||||||
|
_dateOfBirth = dateOfBirth;
|
||||||
|
_gender = gender;
|
||||||
|
_adminComment = adminComment;
|
||||||
|
_isTaxExempt = isTaxExempt;
|
||||||
|
_hasShoppingCartItems = hasShoppingCartItems;
|
||||||
|
_active = active;
|
||||||
|
_deleted = deleted;
|
||||||
|
_isSystemAccount = isSystemAccount;
|
||||||
|
_systemName = systemName;
|
||||||
|
_lastIpAddress = lastIpAddress;
|
||||||
|
_createdOnUtc = createdOnUtc;
|
||||||
|
_lastLoginDateUtc = lastLoginDateUtc;
|
||||||
|
_lastActivityDateUtc = lastActivityDateUtc;
|
||||||
|
_registeredInStoreId = registeredInStoreId;
|
||||||
|
_subscribedToNewsletter = subscribedToNewsletter;
|
||||||
|
_roleIds = roleIds;
|
||||||
|
_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackagesCustomerResponseModel.fromJson(dynamic json) {
|
||||||
|
_billingAddress = json["billing_address"];
|
||||||
|
_shippingAddress = json["shipping_address"];
|
||||||
|
if (json["addresses"] != null) {
|
||||||
|
_addresses = [];
|
||||||
|
json["addresses"].forEach((v) {
|
||||||
|
_addresses.add(Addresses.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_customerGuid = json["customer_guid"];
|
||||||
|
_username = json["username"];
|
||||||
|
_email = json["email"];
|
||||||
|
_firstName = json["first_name"];
|
||||||
|
_lastName = json["last_name"];
|
||||||
|
_languageId = json["language_id"];
|
||||||
|
_dateOfBirth = json["date_of_birth"];
|
||||||
|
_gender = json["gender"];
|
||||||
|
_adminComment = json["admin_comment"];
|
||||||
|
_isTaxExempt = json["is_tax_exempt"];
|
||||||
|
_hasShoppingCartItems = json["has_shopping_cart_items"];
|
||||||
|
_active = json["active"];
|
||||||
|
_deleted = json["deleted"];
|
||||||
|
_isSystemAccount = json["is_system_account"];
|
||||||
|
_systemName = json["system_name"];
|
||||||
|
_lastIpAddress = json["last_ip_address"];
|
||||||
|
_createdOnUtc = json["created_on_utc"];
|
||||||
|
_lastLoginDateUtc = json["last_login_date_utc"];
|
||||||
|
_lastActivityDateUtc = json["last_activity_date_utc"];
|
||||||
|
_registeredInStoreId = json["registered_in_store_id"];
|
||||||
|
_subscribedToNewsletter = json["subscribed_to_newsletter"];
|
||||||
|
|
||||||
|
if (json["role_ids"] != null) {
|
||||||
|
_roleIds = [];
|
||||||
|
json["role_ids"].forEach((v) {
|
||||||
|
_roleIds.add(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json["shopping_cart_items"] != null) {
|
||||||
|
_shoppingCartItems = [];
|
||||||
|
json["shopping_cart_items"].forEach((v) {
|
||||||
|
_shoppingCartItems.add(PackagesCartItemsResponseModel.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_id = json["id"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
var map = <String, dynamic>{};
|
||||||
|
if (_shoppingCartItems != null) {
|
||||||
|
map["shopping_cart_items"] = _shoppingCartItems.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
map["billing_address"] = _billingAddress;
|
||||||
|
map["shipping_address"] = _shippingAddress;
|
||||||
|
if (_addresses != null) {
|
||||||
|
map["addresses"] = _addresses.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
map["customer_guid"] = _customerGuid;
|
||||||
|
map["username"] = _username;
|
||||||
|
map["email"] = _email;
|
||||||
|
map["first_name"] = _firstName;
|
||||||
|
map["last_name"] = _lastName;
|
||||||
|
map["language_id"] = _languageId;
|
||||||
|
map["date_of_birth"] = _dateOfBirth;
|
||||||
|
map["gender"] = _gender;
|
||||||
|
map["admin_comment"] = _adminComment;
|
||||||
|
map["is_tax_exempt"] = _isTaxExempt;
|
||||||
|
map["has_shopping_cart_items"] = _hasShoppingCartItems;
|
||||||
|
map["active"] = _active;
|
||||||
|
map["deleted"] = _deleted;
|
||||||
|
map["is_system_account"] = _isSystemAccount;
|
||||||
|
map["system_name"] = _systemName;
|
||||||
|
map["last_ip_address"] = _lastIpAddress;
|
||||||
|
map["created_on_utc"] = _createdOnUtc;
|
||||||
|
map["last_login_date_utc"] = _lastLoginDateUtc;
|
||||||
|
map["last_activity_date_utc"] = _lastActivityDateUtc;
|
||||||
|
map["registered_in_store_id"] = _registeredInStoreId;
|
||||||
|
map["subscribed_to_newsletter"] = _subscribedToNewsletter;
|
||||||
|
if (_roleIds != null) {
|
||||||
|
map["role_ids"] = _roleIds.map((v) => v).toList();
|
||||||
|
}
|
||||||
|
map["id"] = _id;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// first_name : null
|
||||||
|
/// last_name : null
|
||||||
|
/// email : "a2zzuhaib@gmil.com"
|
||||||
|
/// company : null
|
||||||
|
/// country_id : null
|
||||||
|
/// country : null
|
||||||
|
/// state_province_id : null
|
||||||
|
/// city : null
|
||||||
|
/// address1 : null
|
||||||
|
/// address2 : null
|
||||||
|
/// zip_postal_code : null
|
||||||
|
/// phone_number : "0500409598"
|
||||||
|
/// fax_number : null
|
||||||
|
/// customer_attributes : null
|
||||||
|
/// created_on_utc : "2021-03-11T09:40:23.8091261Z"
|
||||||
|
/// province : null
|
||||||
|
/// id : 0
|
||||||
|
|
||||||
|
class Addresses {
|
||||||
|
dynamic _firstName;
|
||||||
|
dynamic _lastName;
|
||||||
|
String _email;
|
||||||
|
dynamic _company;
|
||||||
|
dynamic _countryId;
|
||||||
|
dynamic _country;
|
||||||
|
dynamic _stateProvinceId;
|
||||||
|
dynamic _city;
|
||||||
|
dynamic _address1;
|
||||||
|
dynamic _address2;
|
||||||
|
dynamic _zipPostalCode;
|
||||||
|
String _phoneNumber;
|
||||||
|
dynamic _faxNumber;
|
||||||
|
dynamic _customerAttributes;
|
||||||
|
String _createdOnUtc;
|
||||||
|
dynamic _province;
|
||||||
|
int _id;
|
||||||
|
|
||||||
|
dynamic get firstName => _firstName;
|
||||||
|
dynamic get lastName => _lastName;
|
||||||
|
String get email => _email;
|
||||||
|
dynamic get company => _company;
|
||||||
|
dynamic get countryId => _countryId;
|
||||||
|
dynamic get country => _country;
|
||||||
|
dynamic get stateProvinceId => _stateProvinceId;
|
||||||
|
dynamic get city => _city;
|
||||||
|
dynamic get address1 => _address1;
|
||||||
|
dynamic get address2 => _address2;
|
||||||
|
dynamic get zipPostalCode => _zipPostalCode;
|
||||||
|
String get phoneNumber => _phoneNumber;
|
||||||
|
dynamic get faxNumber => _faxNumber;
|
||||||
|
dynamic get customerAttributes => _customerAttributes;
|
||||||
|
String get createdOnUtc => _createdOnUtc;
|
||||||
|
dynamic get province => _province;
|
||||||
|
int get id => _id;
|
||||||
|
|
||||||
|
Addresses({
|
||||||
|
dynamic firstName,
|
||||||
|
dynamic lastName,
|
||||||
|
String email,
|
||||||
|
dynamic company,
|
||||||
|
dynamic countryId,
|
||||||
|
dynamic country,
|
||||||
|
dynamic stateProvinceId,
|
||||||
|
dynamic city,
|
||||||
|
dynamic address1,
|
||||||
|
dynamic address2,
|
||||||
|
dynamic zipPostalCode,
|
||||||
|
String phoneNumber,
|
||||||
|
dynamic faxNumber,
|
||||||
|
dynamic customerAttributes,
|
||||||
|
String createdOnUtc,
|
||||||
|
dynamic province,
|
||||||
|
int id}){
|
||||||
|
_firstName = firstName;
|
||||||
|
_lastName = lastName;
|
||||||
|
_email = email;
|
||||||
|
_company = company;
|
||||||
|
_countryId = countryId;
|
||||||
|
_country = country;
|
||||||
|
_stateProvinceId = stateProvinceId;
|
||||||
|
_city = city;
|
||||||
|
_address1 = address1;
|
||||||
|
_address2 = address2;
|
||||||
|
_zipPostalCode = zipPostalCode;
|
||||||
|
_phoneNumber = phoneNumber;
|
||||||
|
_faxNumber = faxNumber;
|
||||||
|
_customerAttributes = customerAttributes;
|
||||||
|
_createdOnUtc = createdOnUtc;
|
||||||
|
_province = province;
|
||||||
|
_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Addresses.fromJson(dynamic json) {
|
||||||
|
_firstName = json["first_name"];
|
||||||
|
_lastName = json["last_name"];
|
||||||
|
_email = json["email"];
|
||||||
|
_company = json["company"];
|
||||||
|
_countryId = json["country_id"];
|
||||||
|
_country = json["country"];
|
||||||
|
_stateProvinceId = json["state_province_id"];
|
||||||
|
_city = json["city"];
|
||||||
|
_address1 = json["address1"];
|
||||||
|
_address2 = json["address2"];
|
||||||
|
_zipPostalCode = json["zip_postal_code"];
|
||||||
|
_phoneNumber = json["phone_number"];
|
||||||
|
_faxNumber = json["fax_number"];
|
||||||
|
_customerAttributes = json["customer_attributes"];
|
||||||
|
_createdOnUtc = json["created_on_utc"];
|
||||||
|
_province = json["province"];
|
||||||
|
_id = json["id"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
var map = <String, dynamic>{};
|
||||||
|
map["first_name"] = _firstName;
|
||||||
|
map["last_name"] = _lastName;
|
||||||
|
map["email"] = _email;
|
||||||
|
map["company"] = _company;
|
||||||
|
map["country_id"] = _countryId;
|
||||||
|
map["country"] = _country;
|
||||||
|
map["state_province_id"] = _stateProvinceId;
|
||||||
|
map["city"] = _city;
|
||||||
|
map["address1"] = _address1;
|
||||||
|
map["address2"] = _address2;
|
||||||
|
map["zip_postal_code"] = _zipPostalCode;
|
||||||
|
map["phone_number"] = _phoneNumber;
|
||||||
|
map["fax_number"] = _faxNumber;
|
||||||
|
map["customer_attributes"] = _customerAttributes;
|
||||||
|
map["created_on_utc"] = _createdOnUtc;
|
||||||
|
map["province"] = _province;
|
||||||
|
map["id"] = _id;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,323 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart';
|
||||||
|
import 'PackagesCartItemsResponseModel.dart';
|
||||||
|
|
||||||
|
class PackagesOrderResponseModel {
|
||||||
|
String _customOrderNumber;
|
||||||
|
int _storeId;
|
||||||
|
dynamic _pickUpInStore;
|
||||||
|
String _paymentMethodSystemName;
|
||||||
|
String _customerCurrencyCode;
|
||||||
|
double _currencyRate;
|
||||||
|
int _customerTaxDisplayTypeId;
|
||||||
|
dynamic _vatNumber;
|
||||||
|
double _orderSubtotalInclTax;
|
||||||
|
double _orderSubtotalExclTax;
|
||||||
|
double _orderSubTotalDiscountInclTax;
|
||||||
|
double _orderSubTotalDiscountExclTax;
|
||||||
|
double _orderShippingInclTax;
|
||||||
|
double _orderShippingExclTax;
|
||||||
|
double _paymentMethodAdditionalFeeInclTax;
|
||||||
|
double _paymentMethodAdditionalFeeExclTax;
|
||||||
|
String _taxRates;
|
||||||
|
double _orderTax;
|
||||||
|
double _orderDiscount;
|
||||||
|
double _orderTotal;
|
||||||
|
double _refundedAmount;
|
||||||
|
dynamic _rewardPointsWereAdded;
|
||||||
|
String _checkoutAttributeDescription;
|
||||||
|
int _customerLanguageId;
|
||||||
|
int _affiliateId;
|
||||||
|
String _customerIp;
|
||||||
|
dynamic _authorizationTransactionId;
|
||||||
|
dynamic _authorizationTransactionCode;
|
||||||
|
dynamic _authorizationTransactionResult;
|
||||||
|
dynamic _captureTransactionId;
|
||||||
|
dynamic _captureTransactionResult;
|
||||||
|
dynamic _subscriptionTransactionId;
|
||||||
|
dynamic _paidDateUtc;
|
||||||
|
dynamic _shippingMethod;
|
||||||
|
dynamic _shippingRateComputationMethodSystemName;
|
||||||
|
String _customValuesXml;
|
||||||
|
dynamic _paymentOption;
|
||||||
|
bool _deleted;
|
||||||
|
String _createdOnUtc;
|
||||||
|
PackagesCustomerResponseModel _customer;
|
||||||
|
int _customerId;
|
||||||
|
dynamic _billingAddress;
|
||||||
|
dynamic _shippingAddress;
|
||||||
|
List<PackagesCartItemsResponseModel> _orderItems;
|
||||||
|
String _orderStatus;
|
||||||
|
String _paymentStatus;
|
||||||
|
String _shippingStatus;
|
||||||
|
String _customerTaxDisplayType;
|
||||||
|
int _id;
|
||||||
|
|
||||||
|
String get customOrderNumber => _customOrderNumber;
|
||||||
|
int get storeId => _storeId;
|
||||||
|
dynamic get pickUpInStore => _pickUpInStore;
|
||||||
|
String get paymentMethodSystemName => _paymentMethodSystemName;
|
||||||
|
String get customerCurrencyCode => _customerCurrencyCode;
|
||||||
|
double get currencyRate => _currencyRate;
|
||||||
|
int get customerTaxDisplayTypeId => _customerTaxDisplayTypeId;
|
||||||
|
dynamic get vatNumber => _vatNumber;
|
||||||
|
double get orderSubtotalInclTax => _orderSubtotalInclTax;
|
||||||
|
double get orderSubtotalExclTax => _orderSubtotalExclTax;
|
||||||
|
double get orderSubTotalDiscountInclTax => _orderSubTotalDiscountInclTax;
|
||||||
|
double get orderSubTotalDiscountExclTax => _orderSubTotalDiscountExclTax;
|
||||||
|
double get orderShippingInclTax => _orderShippingInclTax;
|
||||||
|
double get orderShippingExclTax => _orderShippingExclTax;
|
||||||
|
double get paymentMethodAdditionalFeeInclTax => _paymentMethodAdditionalFeeInclTax;
|
||||||
|
double get paymentMethodAdditionalFeeExclTax => _paymentMethodAdditionalFeeExclTax;
|
||||||
|
String get taxRates => _taxRates;
|
||||||
|
double get orderTax => _orderTax;
|
||||||
|
double get orderDiscount => _orderDiscount;
|
||||||
|
double get orderTotal => _orderTotal;
|
||||||
|
double get refundedAmount => _refundedAmount;
|
||||||
|
dynamic get rewardPointsWereAdded => _rewardPointsWereAdded;
|
||||||
|
String get checkoutAttributeDescription => _checkoutAttributeDescription;
|
||||||
|
int get customerLanguageId => _customerLanguageId;
|
||||||
|
int get affiliateId => _affiliateId;
|
||||||
|
String get customerIp => _customerIp;
|
||||||
|
dynamic get authorizationTransactionId => _authorizationTransactionId;
|
||||||
|
dynamic get authorizationTransactionCode => _authorizationTransactionCode;
|
||||||
|
dynamic get authorizationTransactionResult => _authorizationTransactionResult;
|
||||||
|
dynamic get captureTransactionId => _captureTransactionId;
|
||||||
|
dynamic get captureTransactionResult => _captureTransactionResult;
|
||||||
|
dynamic get subscriptionTransactionId => _subscriptionTransactionId;
|
||||||
|
dynamic get paidDateUtc => _paidDateUtc;
|
||||||
|
dynamic get shippingMethod => _shippingMethod;
|
||||||
|
dynamic get shippingRateComputationMethodSystemName => _shippingRateComputationMethodSystemName;
|
||||||
|
String get customValuesXml => _customValuesXml;
|
||||||
|
dynamic get paymentOption => _paymentOption;
|
||||||
|
bool get deleted => _deleted;
|
||||||
|
String get createdOnUtc => _createdOnUtc;
|
||||||
|
PackagesCustomerResponseModel get customer => _customer;
|
||||||
|
int get customerId => _customerId;
|
||||||
|
dynamic get billingAddress => _billingAddress;
|
||||||
|
dynamic get shippingAddress => _shippingAddress;
|
||||||
|
List<PackagesCartItemsResponseModel> get orderItems => _orderItems;
|
||||||
|
String get orderStatus => _orderStatus;
|
||||||
|
String get paymentStatus => _paymentStatus;
|
||||||
|
String get shippingStatus => _shippingStatus;
|
||||||
|
String get customerTaxDisplayType => _customerTaxDisplayType;
|
||||||
|
int get id => _id;
|
||||||
|
|
||||||
|
OrderResponseModel({
|
||||||
|
String customOrderNumber,
|
||||||
|
int storeId,
|
||||||
|
dynamic pickUpInStore,
|
||||||
|
String paymentMethodSystemName,
|
||||||
|
String customerCurrencyCode,
|
||||||
|
double currencyRate,
|
||||||
|
int customerTaxDisplayTypeId,
|
||||||
|
dynamic vatNumber,
|
||||||
|
double orderSubtotalInclTax,
|
||||||
|
double orderSubtotalExclTax,
|
||||||
|
double orderSubTotalDiscountInclTax,
|
||||||
|
double orderSubTotalDiscountExclTax,
|
||||||
|
double orderShippingInclTax,
|
||||||
|
double orderShippingExclTax,
|
||||||
|
double paymentMethodAdditionalFeeInclTax,
|
||||||
|
double paymentMethodAdditionalFeeExclTax,
|
||||||
|
String taxRates,
|
||||||
|
double orderTax,
|
||||||
|
double orderDiscount,
|
||||||
|
double orderTotal,
|
||||||
|
double refundedAmount,
|
||||||
|
dynamic rewardPointsWereAdded,
|
||||||
|
String checkoutAttributeDescription,
|
||||||
|
int customerLanguageId,
|
||||||
|
int affiliateId,
|
||||||
|
String customerIp,
|
||||||
|
dynamic authorizationTransactionId,
|
||||||
|
dynamic authorizationTransactionCode,
|
||||||
|
dynamic authorizationTransactionResult,
|
||||||
|
dynamic captureTransactionId,
|
||||||
|
dynamic captureTransactionResult,
|
||||||
|
dynamic subscriptionTransactionId,
|
||||||
|
dynamic paidDateUtc,
|
||||||
|
dynamic shippingMethod,
|
||||||
|
dynamic shippingRateComputationMethodSystemName,
|
||||||
|
String customValuesXml,
|
||||||
|
dynamic paymentOption,
|
||||||
|
bool deleted,
|
||||||
|
String createdOnUtc,
|
||||||
|
PackagesCustomerResponseModel customer,
|
||||||
|
int customerId,
|
||||||
|
dynamic billingAddress,
|
||||||
|
dynamic shippingAddress,
|
||||||
|
List<PackagesCartItemsResponseModel> orderItems,
|
||||||
|
String orderStatus,
|
||||||
|
String paymentStatus,
|
||||||
|
String shippingStatus,
|
||||||
|
String customerTaxDisplayType,
|
||||||
|
int id}){
|
||||||
|
_customOrderNumber = customOrderNumber;
|
||||||
|
_storeId = storeId;
|
||||||
|
_pickUpInStore = pickUpInStore;
|
||||||
|
_paymentMethodSystemName = paymentMethodSystemName;
|
||||||
|
_customerCurrencyCode = customerCurrencyCode;
|
||||||
|
_currencyRate = currencyRate;
|
||||||
|
_customerTaxDisplayTypeId = customerTaxDisplayTypeId;
|
||||||
|
_vatNumber = vatNumber;
|
||||||
|
_orderSubtotalInclTax = orderSubtotalInclTax;
|
||||||
|
_orderSubtotalExclTax = orderSubtotalExclTax;
|
||||||
|
_orderSubTotalDiscountInclTax = orderSubTotalDiscountInclTax;
|
||||||
|
_orderSubTotalDiscountExclTax = orderSubTotalDiscountExclTax;
|
||||||
|
_orderShippingInclTax = orderShippingInclTax;
|
||||||
|
_orderShippingExclTax = orderShippingExclTax;
|
||||||
|
_paymentMethodAdditionalFeeInclTax = paymentMethodAdditionalFeeInclTax;
|
||||||
|
_paymentMethodAdditionalFeeExclTax = paymentMethodAdditionalFeeExclTax;
|
||||||
|
_taxRates = taxRates;
|
||||||
|
_orderTax = orderTax;
|
||||||
|
_orderDiscount = orderDiscount;
|
||||||
|
_orderTotal = orderTotal;
|
||||||
|
_refundedAmount = refundedAmount;
|
||||||
|
_rewardPointsWereAdded = rewardPointsWereAdded;
|
||||||
|
_checkoutAttributeDescription = checkoutAttributeDescription;
|
||||||
|
_customerLanguageId = customerLanguageId;
|
||||||
|
_affiliateId = affiliateId;
|
||||||
|
_customerIp = customerIp;
|
||||||
|
_authorizationTransactionId = authorizationTransactionId;
|
||||||
|
_authorizationTransactionCode = authorizationTransactionCode;
|
||||||
|
_authorizationTransactionResult = authorizationTransactionResult;
|
||||||
|
_captureTransactionId = captureTransactionId;
|
||||||
|
_captureTransactionResult = captureTransactionResult;
|
||||||
|
_subscriptionTransactionId = subscriptionTransactionId;
|
||||||
|
_paidDateUtc = paidDateUtc;
|
||||||
|
_shippingMethod = shippingMethod;
|
||||||
|
_shippingRateComputationMethodSystemName = shippingRateComputationMethodSystemName;
|
||||||
|
_customValuesXml = customValuesXml;
|
||||||
|
_paymentOption = paymentOption;
|
||||||
|
_deleted = deleted;
|
||||||
|
_createdOnUtc = createdOnUtc;
|
||||||
|
_customer = customer;
|
||||||
|
_customerId = customerId;
|
||||||
|
_billingAddress = billingAddress;
|
||||||
|
_shippingAddress = shippingAddress;
|
||||||
|
_orderItems = orderItems;
|
||||||
|
_orderStatus = orderStatus;
|
||||||
|
_paymentStatus = paymentStatus;
|
||||||
|
_shippingStatus = shippingStatus;
|
||||||
|
_customerTaxDisplayType = customerTaxDisplayType;
|
||||||
|
_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackagesOrderResponseModel.fromJson(dynamic json) {
|
||||||
|
_customOrderNumber = json["custom_order_number"];
|
||||||
|
_storeId = json["store_id"];
|
||||||
|
_pickUpInStore = json["pick_up_in_store"];
|
||||||
|
_paymentMethodSystemName = json["payment_method_system_name"];
|
||||||
|
_customerCurrencyCode = json["customer_currency_code"];
|
||||||
|
_currencyRate = json["currency_rate"];
|
||||||
|
_customerTaxDisplayTypeId = json["customer_tax_display_type_id"];
|
||||||
|
_vatNumber = json["vat_number"];
|
||||||
|
_orderSubtotalInclTax = json["order_subtotal_incl_tax"];
|
||||||
|
_orderSubtotalExclTax = json["order_subtotal_excl_tax"];
|
||||||
|
_orderSubTotalDiscountInclTax = json["order_sub_total_discount_incl_tax"];
|
||||||
|
_orderSubTotalDiscountExclTax = json["order_sub_total_discount_excl_tax"];
|
||||||
|
_orderShippingInclTax = json["order_shipping_incl_tax"];
|
||||||
|
_orderShippingExclTax = json["order_shipping_excl_tax"];
|
||||||
|
_paymentMethodAdditionalFeeInclTax = json["payment_method_additional_fee_incl_tax"];
|
||||||
|
_paymentMethodAdditionalFeeExclTax = json["payment_method_additional_fee_excl_tax"];
|
||||||
|
_taxRates = json["tax_rates"];
|
||||||
|
_orderTax = json["order_tax"];
|
||||||
|
_orderDiscount = json["order_discount"];
|
||||||
|
_orderTotal = json["order_total"];
|
||||||
|
_refundedAmount = json["refunded_amount"];
|
||||||
|
_rewardPointsWereAdded = json["reward_points_were_added"];
|
||||||
|
_checkoutAttributeDescription = json["checkout_attribute_description"];
|
||||||
|
_customerLanguageId = json["customer_language_id"];
|
||||||
|
_affiliateId = json["affiliate_id"];
|
||||||
|
_customerIp = json["customer_ip"];
|
||||||
|
_authorizationTransactionId = json["authorization_transaction_id"];
|
||||||
|
_authorizationTransactionCode = json["authorization_transaction_code"];
|
||||||
|
_authorizationTransactionResult = json["authorization_transaction_result"];
|
||||||
|
_captureTransactionId = json["capture_transaction_id"];
|
||||||
|
_captureTransactionResult = json["capture_transaction_result"];
|
||||||
|
_subscriptionTransactionId = json["subscription_transaction_id"];
|
||||||
|
_paidDateUtc = json["paid_date_utc"];
|
||||||
|
_shippingMethod = json["shipping_method"];
|
||||||
|
_shippingRateComputationMethodSystemName = json["shipping_rate_computation_method_system_name"];
|
||||||
|
_customValuesXml = json["custom_values_xml"];
|
||||||
|
_paymentOption = json["payment_option"];
|
||||||
|
_deleted = json["deleted"];
|
||||||
|
_createdOnUtc = json["created_on_utc"];
|
||||||
|
_customer = json["customer"] != null ? PackagesCustomerResponseModel.fromJson(json["customer"]) : null;
|
||||||
|
_customerId = json["customer_id"];
|
||||||
|
_billingAddress = json["billing_address"];
|
||||||
|
_shippingAddress = json["shipping_address"];
|
||||||
|
if (json["order_items"] != null) {
|
||||||
|
_orderItems = [];
|
||||||
|
json["order_items"].forEach((v) {
|
||||||
|
_orderItems.add(PackagesCartItemsResponseModel.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_orderStatus = json["order_status"];
|
||||||
|
_paymentStatus = json["payment_status"];
|
||||||
|
_shippingStatus = json["shipping_status"];
|
||||||
|
_customerTaxDisplayType = json["customer_tax_display_type"];
|
||||||
|
_id = json["id"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
var map = <String, dynamic>{};
|
||||||
|
map["custom_order_number"] = _customOrderNumber;
|
||||||
|
map["store_id"] = _storeId;
|
||||||
|
map["pick_up_in_store"] = _pickUpInStore;
|
||||||
|
map["payment_method_system_name"] = _paymentMethodSystemName;
|
||||||
|
map["customer_currency_code"] = _customerCurrencyCode;
|
||||||
|
map["currency_rate"] = _currencyRate;
|
||||||
|
map["customer_tax_display_type_id"] = _customerTaxDisplayTypeId;
|
||||||
|
map["vat_number"] = _vatNumber;
|
||||||
|
map["order_subtotal_incl_tax"] = _orderSubtotalInclTax;
|
||||||
|
map["order_subtotal_excl_tax"] = _orderSubtotalExclTax;
|
||||||
|
map["order_sub_total_discount_incl_tax"] = _orderSubTotalDiscountInclTax;
|
||||||
|
map["order_sub_total_discount_excl_tax"] = _orderSubTotalDiscountExclTax;
|
||||||
|
map["order_shipping_incl_tax"] = _orderShippingInclTax;
|
||||||
|
map["order_shipping_excl_tax"] = _orderShippingExclTax;
|
||||||
|
map["payment_method_additional_fee_incl_tax"] = _paymentMethodAdditionalFeeInclTax;
|
||||||
|
map["payment_method_additional_fee_excl_tax"] = _paymentMethodAdditionalFeeExclTax;
|
||||||
|
map["tax_rates"] = _taxRates;
|
||||||
|
map["order_tax"] = _orderTax;
|
||||||
|
map["order_discount"] = _orderDiscount;
|
||||||
|
map["order_total"] = _orderTotal;
|
||||||
|
map["refunded_amount"] = _refundedAmount;
|
||||||
|
map["reward_points_were_added"] = _rewardPointsWereAdded;
|
||||||
|
map["checkout_attribute_description"] = _checkoutAttributeDescription;
|
||||||
|
map["customer_language_id"] = _customerLanguageId;
|
||||||
|
map["affiliate_id"] = _affiliateId;
|
||||||
|
map["customer_ip"] = _customerIp;
|
||||||
|
map["authorization_transaction_id"] = _authorizationTransactionId;
|
||||||
|
map["authorization_transaction_code"] = _authorizationTransactionCode;
|
||||||
|
map["authorization_transaction_result"] = _authorizationTransactionResult;
|
||||||
|
map["capture_transaction_id"] = _captureTransactionId;
|
||||||
|
map["capture_transaction_result"] = _captureTransactionResult;
|
||||||
|
map["subscription_transaction_id"] = _subscriptionTransactionId;
|
||||||
|
map["paid_date_utc"] = _paidDateUtc;
|
||||||
|
map["shipping_method"] = _shippingMethod;
|
||||||
|
map["shipping_rate_computation_method_system_name"] = _shippingRateComputationMethodSystemName;
|
||||||
|
map["custom_values_xml"] = _customValuesXml;
|
||||||
|
map["payment_option"] = _paymentOption;
|
||||||
|
map["deleted"] = _deleted;
|
||||||
|
map["created_on_utc"] = _createdOnUtc;
|
||||||
|
if (_customer != null) {
|
||||||
|
map["customer"] = _customer.toJson();
|
||||||
|
}
|
||||||
|
map["customer_id"] = _customerId;
|
||||||
|
map["billing_address"] = _billingAddress;
|
||||||
|
map["shipping_address"] = _shippingAddress;
|
||||||
|
if (_orderItems != null) {
|
||||||
|
map["order_items"] = _orderItems.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
map["order_status"] = _orderStatus;
|
||||||
|
map["payment_status"] = _paymentStatus;
|
||||||
|
map["shipping_status"] = _shippingStatus;
|
||||||
|
map["customer_tax_display_type"] = _customerTaxDisplayType;
|
||||||
|
map["id"] = _id;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,127 +1,193 @@
|
|||||||
import 'package:diplomaticquarterapp/uitl/date_uitl.dart';
|
import 'package:diplomaticquarterapp/uitl/date_uitl.dart';
|
||||||
|
|
||||||
class PrescriptionReport {
|
class PrescriptionReport {
|
||||||
|
String address;
|
||||||
|
int appointmentNo;
|
||||||
|
String clinic;
|
||||||
|
String companyName;
|
||||||
|
int days;
|
||||||
|
String doctorName;
|
||||||
|
var doseDailyQuantity;
|
||||||
|
String frequency;
|
||||||
|
int frequencyNumber;
|
||||||
|
String image;
|
||||||
|
String imageExtension;
|
||||||
|
String imageSRCUrl;
|
||||||
|
String imageString;
|
||||||
|
String imageThumbUrl;
|
||||||
|
String isCovered;
|
||||||
|
String itemDescription;
|
||||||
|
int itemID;
|
||||||
|
String orderDate;
|
||||||
int patientID;
|
int patientID;
|
||||||
String patientName;
|
String patientName;
|
||||||
|
String phoneOffice1;
|
||||||
|
String prescriptionQR;
|
||||||
|
int prescriptionTimes;
|
||||||
|
String productImage;
|
||||||
|
String productImageBase64;
|
||||||
|
String productImageString;
|
||||||
|
int projectID;
|
||||||
|
String projectName;
|
||||||
|
String remarks;
|
||||||
|
String route;
|
||||||
|
String sKU;
|
||||||
|
int scaleOffset;
|
||||||
|
String startDate;
|
||||||
|
|
||||||
String patientAge;
|
String patientAge;
|
||||||
String patientGender;
|
String patientGender;
|
||||||
String address;
|
|
||||||
String phoneOffice;
|
String phoneOffice;
|
||||||
String itemDescription;
|
|
||||||
int doseTimingID;
|
int doseTimingID;
|
||||||
int frequencyID;
|
int frequencyID;
|
||||||
int routeID;
|
int routeID;
|
||||||
String clinic;
|
|
||||||
String doctorName;
|
|
||||||
String route;
|
|
||||||
String frequency;
|
|
||||||
String remarks;
|
|
||||||
String name;
|
String name;
|
||||||
int days;
|
|
||||||
String startDate;
|
|
||||||
String orderDate;
|
|
||||||
int doseDailyQuantity;
|
|
||||||
int itemID;
|
|
||||||
Null productImage;
|
|
||||||
String sKU;
|
|
||||||
String itemDescriptionN;
|
String itemDescriptionN;
|
||||||
String routeN;
|
String routeN;
|
||||||
String frequencyN;
|
String frequencyN;
|
||||||
String imageSRCUrl;
|
|
||||||
String imageThumbUrl;
|
|
||||||
|
|
||||||
PrescriptionReport(
|
PrescriptionReport({
|
||||||
{this.patientID,
|
this.address,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.clinic,
|
||||||
|
this.companyName,
|
||||||
|
this.days,
|
||||||
|
this.doctorName,
|
||||||
|
this.doseDailyQuantity,
|
||||||
|
this.frequency,
|
||||||
|
this.frequencyNumber,
|
||||||
|
this.image,
|
||||||
|
this.imageExtension,
|
||||||
|
this.imageSRCUrl,
|
||||||
|
this.imageString,
|
||||||
|
this.imageThumbUrl,
|
||||||
|
this.isCovered,
|
||||||
|
this.itemDescription,
|
||||||
|
this.itemID,
|
||||||
|
this.orderDate,
|
||||||
|
this.patientID,
|
||||||
this.patientName,
|
this.patientName,
|
||||||
|
this.phoneOffice1,
|
||||||
|
this.prescriptionQR,
|
||||||
|
this.prescriptionTimes,
|
||||||
|
this.productImage,
|
||||||
|
this.productImageBase64,
|
||||||
|
this.productImageString,
|
||||||
|
this.projectID,
|
||||||
|
this.projectName,
|
||||||
|
this.remarks,
|
||||||
|
this.route,
|
||||||
|
this.sKU,
|
||||||
|
this.scaleOffset,
|
||||||
|
this.startDate,
|
||||||
this.patientAge,
|
this.patientAge,
|
||||||
this.patientGender,
|
this.patientGender,
|
||||||
this.address,
|
|
||||||
this.phoneOffice,
|
this.phoneOffice,
|
||||||
this.itemDescription,
|
|
||||||
this.doseTimingID,
|
this.doseTimingID,
|
||||||
this.frequencyID,
|
this.frequencyID,
|
||||||
this.routeID,
|
this.routeID,
|
||||||
this.clinic,
|
|
||||||
this.doctorName,
|
|
||||||
this.route,
|
|
||||||
this.frequency,
|
|
||||||
this.remarks,
|
|
||||||
this.name,
|
this.name,
|
||||||
this.days,
|
|
||||||
this.startDate,
|
|
||||||
this.orderDate,
|
|
||||||
this.doseDailyQuantity,
|
|
||||||
this.itemID,
|
|
||||||
this.productImage,
|
|
||||||
this.sKU,
|
|
||||||
this.itemDescriptionN,
|
this.itemDescriptionN,
|
||||||
this.routeN,
|
this.routeN,
|
||||||
this.frequencyN,
|
this.frequencyN,
|
||||||
this.imageSRCUrl,
|
});
|
||||||
this.imageThumbUrl});
|
|
||||||
|
|
||||||
PrescriptionReport.fromJson(Map<String, dynamic> json) {
|
PrescriptionReport.fromJson(Map<String, dynamic> json) {
|
||||||
patientID = json['PatientID'];
|
|
||||||
patientName = json['PatientName'];
|
|
||||||
patientAge = json['PatientAge'];
|
|
||||||
patientGender = json['PatientGender'];
|
|
||||||
address = json['Address'];
|
address = json['Address'];
|
||||||
phoneOffice = json['PhoneOffice'];
|
appointmentNo = json['AppointmentNo'];
|
||||||
itemDescription = json['ItemDescription'];
|
|
||||||
doseTimingID = json['DoseTimingID'];
|
|
||||||
frequencyID = json['FrequencyID'];
|
|
||||||
routeID = json['RouteID'];
|
|
||||||
clinic = json['Clinic'];
|
clinic = json['Clinic'];
|
||||||
doctorName = json['DoctorName'];
|
companyName = json['CompanyName'];
|
||||||
route = json['Route'];
|
|
||||||
frequency = json['Frequency'];
|
|
||||||
remarks = json['Remarks'];
|
|
||||||
name = json['Name'];
|
|
||||||
days = json['Days'];
|
days = json['Days'];
|
||||||
startDate = json['StartDate'];
|
doctorName = json['DoctorName'];
|
||||||
orderDate = json['OrderDate'];
|
|
||||||
doseDailyQuantity = json['DoseDailyQuantity'];
|
doseDailyQuantity = json['DoseDailyQuantity'];
|
||||||
|
frequency = json['Frequency'];
|
||||||
|
frequencyNumber = json['FrequencyNumber'];
|
||||||
|
image = json['Image'];
|
||||||
|
imageExtension = json['ImageExtension'];
|
||||||
|
imageSRCUrl = json['ImageSRCUrl'];
|
||||||
|
imageString = json['ImageString'];
|
||||||
|
imageThumbUrl = json['ImageThumbUrl'];
|
||||||
|
isCovered = json['IsCovered'];
|
||||||
|
itemDescription = json['ItemDescription'];
|
||||||
itemID = json['ItemID'];
|
itemID = json['ItemID'];
|
||||||
|
orderDate = json['OrderDate'];
|
||||||
|
patientID = json['PatientID'];
|
||||||
|
patientName = json['PatientName'];
|
||||||
|
phoneOffice1 = json['PhoneOffice1'];
|
||||||
|
prescriptionQR = json['PrescriptionQR'];
|
||||||
|
prescriptionTimes = json['PrescriptionTimes'];
|
||||||
productImage = json['ProductImage'];
|
productImage = json['ProductImage'];
|
||||||
|
productImageBase64 = json['ProductImageBase64'];
|
||||||
|
productImageString = json['ProductImageString'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
projectName = json['ProjectName'];
|
||||||
|
remarks = json['Remarks'];
|
||||||
|
route = json['Route'];
|
||||||
sKU = json['SKU'];
|
sKU = json['SKU'];
|
||||||
itemDescriptionN = json['ItemDescriptionN'];
|
scaleOffset = json['ScaleOffset'];
|
||||||
routeN = json['RouteN'];
|
startDate = json['StartDate'];
|
||||||
frequencyN = json['FrequencyN'];
|
|
||||||
imageSRCUrl = json['ImageSRCUrl'];
|
patientAge = json['patientAge'];
|
||||||
imageThumbUrl = json['ImageThumbUrl'];
|
patientGender = json['patientGender'];
|
||||||
|
phoneOffice = json['phoneOffice'];
|
||||||
|
doseTimingID = json['doseTimingID'];
|
||||||
|
frequencyID = json['frequencyID'];
|
||||||
|
routeID = json['routeID'];
|
||||||
|
name = json['name'];
|
||||||
|
itemDescriptionN = json['itemDescriptionN'];
|
||||||
|
routeN = json['routeN'];
|
||||||
|
frequencyN = json['frequencyN'];
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
|
||||||
|
data['Address'] = this.address;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['Clinic'] = this.clinic;
|
||||||
|
data['CompanyName'] = this.companyName;
|
||||||
|
data['Days'] = this.days;
|
||||||
|
data['DoctorName'] = this.doctorName;
|
||||||
|
data['DoseDailyQuantity'] = this.doseDailyQuantity;
|
||||||
|
data['Frequency'] = this.frequency;
|
||||||
|
data['FrequencyNumber'] = this.frequencyNumber;
|
||||||
|
data['Image'] = this.image;
|
||||||
|
data['ImageExtension'] = this.imageExtension;
|
||||||
|
data['ImageSRCUrl'] = this.imageSRCUrl;
|
||||||
|
data['ImageString'] = this.imageString;
|
||||||
|
data['ImageThumbUrl'] = this.imageThumbUrl;
|
||||||
|
data['IsCovered'] = this.isCovered;
|
||||||
|
data['ItemDescription'] = this.itemDescription;
|
||||||
|
data['ItemID'] = this.itemID;
|
||||||
|
data['OrderDate'] = this.orderDate;
|
||||||
data['PatientID'] = this.patientID;
|
data['PatientID'] = this.patientID;
|
||||||
data['PatientName'] = this.patientName;
|
data['PatientName'] = this.patientName;
|
||||||
|
data['PhoneOffice1'] = this.phoneOffice1;
|
||||||
|
data['PrescriptionQR'] = this.prescriptionQR;
|
||||||
|
data['PrescriptionTimes'] = this.prescriptionTimes;
|
||||||
|
data['ProductImage'] = this.productImage;
|
||||||
|
data['ProductImageBase64'] = this.productImageBase64;
|
||||||
|
data['ProductImageString'] = this.productImageString;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['ProjectName'] = this.projectName;
|
||||||
|
data['Remarks'] = this.remarks;
|
||||||
|
data['Route'] = this.route;
|
||||||
|
data['SKU'] = this.sKU;
|
||||||
|
data['ScaleOffset'] = this.scaleOffset;
|
||||||
|
data['StartDate'] = this.startDate;
|
||||||
|
|
||||||
data['PatientAge'] = this.patientAge;
|
data['PatientAge'] = this.patientAge;
|
||||||
data['PatientGender'] = this.patientGender;
|
data['PatientGender'] = this.patientGender;
|
||||||
data['Address'] = this.address;
|
|
||||||
data['PhoneOffice'] = this.phoneOffice;
|
data['PhoneOffice'] = this.phoneOffice;
|
||||||
data['ItemDescription'] = this.itemDescription;
|
|
||||||
data['DoseTimingID'] = this.doseTimingID;
|
data['DoseTimingID'] = this.doseTimingID;
|
||||||
data['FrequencyID'] = this.frequencyID;
|
data['FrequencyID'] = this.frequencyID;
|
||||||
data['RouteID'] = this.routeID;
|
data['RouteID'] = this.routeID;
|
||||||
data['Clinic'] = this.clinic;
|
|
||||||
data['DoctorName'] = this.doctorName;
|
|
||||||
data['Route'] = this.route;
|
|
||||||
data['Frequency'] = this.frequency;
|
|
||||||
data['Remarks'] = this.remarks;
|
|
||||||
data['Name'] = this.name;
|
data['Name'] = this.name;
|
||||||
data['Days'] = this.days;
|
|
||||||
data['StartDate'] = this.startDate;
|
|
||||||
data['OrderDate'] = this.orderDate;
|
|
||||||
data['DoseDailyQuantity'] = this.doseDailyQuantity;
|
|
||||||
data['ItemID'] = this.itemID;
|
|
||||||
data['ProductImage'] = this.productImage;
|
|
||||||
data['SKU'] = this.sKU;
|
|
||||||
data['ItemDescriptionN'] = this.itemDescriptionN;
|
data['ItemDescriptionN'] = this.itemDescriptionN;
|
||||||
data['RouteN'] = this.routeN;
|
data['RouteN'] = this.routeN;
|
||||||
data['FrequencyN'] = this.frequencyN;
|
data['FrequencyN'] = this.frequencyN;
|
||||||
data['ImageSRCUrl'] = this.imageSRCUrl;
|
|
||||||
data['ImageThumbUrl'] = this.imageThumbUrl;
|
|
||||||
data['hasPlan'] = false;
|
data['hasPlan'] = false;
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/locator.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart' as utils;
|
||||||
|
import 'package:diplomaticquarterapp/widgets/loadings/ShimmerLoading.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesOfferCard.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_material_pickers/flutter_material_pickers.dart';
|
||||||
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
|
|
||||||
|
import 'CreateCustomerDailogPage.dart';
|
||||||
|
|
||||||
|
dynamic languageID;
|
||||||
|
|
||||||
|
class ClinicPackagesPage extends StatefulWidget {
|
||||||
|
List<PackagesResponseModel> products;
|
||||||
|
ClinicPackagesPage({@required this.products});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ClinicPackagesPageState createState() => _ClinicPackagesPageState();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ClinicPackagesPageState extends State<ClinicPackagesPage> with AfterLayoutMixin<ClinicPackagesPage>{
|
||||||
|
AppScaffold appScaffold;
|
||||||
|
|
||||||
|
List<PackagesResponseModel> get _products => widget.products;
|
||||||
|
PackagesViewModel viewModel;
|
||||||
|
|
||||||
|
|
||||||
|
onProductCartClick(PackagesResponseModel product) async {
|
||||||
|
if(viewModel.service.customer == null)
|
||||||
|
viewModel.service.customer = await CreateCustomerDialogPage(context: context).show();
|
||||||
|
|
||||||
|
if(viewModel.service.customer != null) {
|
||||||
|
var request = AddProductToCartRequestModel(product_id: product.id, customer_id: viewModel.service.customer.id);
|
||||||
|
await viewModel.service.addProductToCart(request, context: context).then((response){
|
||||||
|
appScaffold.appBar.badgeUpdater(viewModel.service.cartItemCount);
|
||||||
|
}).catchError((error) {
|
||||||
|
utils.Utils.showErrorToast(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void afterFirstLayout(BuildContext context) async{
|
||||||
|
appScaffold.appBar.badgeUpdater(viewModel.service.cartItemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return BaseView<PackagesViewModel>(
|
||||||
|
allowAny: true,
|
||||||
|
onModelReady: (model){
|
||||||
|
viewModel = model;
|
||||||
|
},
|
||||||
|
builder: (_, model, wi) => appScaffold = AppScaffold(
|
||||||
|
appBarTitle: TranslationBase.of(context).offerAndPackages,
|
||||||
|
isShowAppBar: true,
|
||||||
|
isPharmacy: false,
|
||||||
|
showPharmacyCart: false,
|
||||||
|
showHomeAppBarIcon: false,
|
||||||
|
isOfferPackages: true,
|
||||||
|
showOfferPackagesCart: true,
|
||||||
|
isShowDecPage: false,
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: StaggeredGridView.countBuilder(
|
||||||
|
crossAxisCount:4,
|
||||||
|
itemCount: _products.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) => new Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: PackagesItemCard( itemContentPadding: 10,itemModel: _products[index], onCartClick: onProductCartClick,)
|
||||||
|
),
|
||||||
|
staggeredTileBuilder: (int index) => StaggeredTile.fit(2),
|
||||||
|
mainAxisSpacing: 20,
|
||||||
|
crossAxisSpacing: 10,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,198 @@
|
|||||||
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/config/size_config.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/CreateCustomerRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/locator.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/ClinicOfferAndPackagesPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackagesCartPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart' as utils;
|
||||||
|
import 'package:diplomaticquarterapp/widgets/AnimatedTextFields.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/Loader/gif_loader_container.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/LoadingButton.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/carousel_indicator/carousel_indicator.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/loadings/ShimmerLoading.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesOfferCard.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_material_pickers/flutter_material_pickers.dart';
|
||||||
|
|
||||||
|
dynamic languageID;
|
||||||
|
var emailRegex = RegExp(r'^[^\s@]+@[^\s@]+\.[^\s@]+$');
|
||||||
|
|
||||||
|
class CreateCustomerDialogPage extends StatefulWidget {
|
||||||
|
final BuildContext context;
|
||||||
|
CreateCustomerDialogPage({this.context});
|
||||||
|
PackagesViewModel viewModel;
|
||||||
|
|
||||||
|
Future<PackagesCustomerResponseModel> show() async{
|
||||||
|
await showDialog(context: context, builder: (context ){
|
||||||
|
return AlertDialog(content: this, shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20)
|
||||||
|
), elevation: 5, );
|
||||||
|
});
|
||||||
|
return viewModel.service.customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CreateCustomerDialogPageState createState() => _CreateCustomerDialogPageState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CreateCustomerDialogPageState extends State<CreateCustomerDialogPage> with AfterLayoutMixin<CreateCustomerDialogPage>, TickerProviderStateMixin{
|
||||||
|
AnimationController _loadingController;
|
||||||
|
AnimationController _submitController;
|
||||||
|
bool _enableInput = true;
|
||||||
|
|
||||||
|
Interval _nameTextFieldLoadingAnimationInterval = const Interval(0, .85);
|
||||||
|
|
||||||
|
final _phoneFocusNode = FocusNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_submitController = AnimationController(vsync: this, duration: Duration(milliseconds: 1000),);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void afterFirstLayout(BuildContext context) async{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controllers
|
||||||
|
TextEditingController _emailTextController = TextEditingController();
|
||||||
|
TextEditingController _phoneTextController = TextEditingController();
|
||||||
|
TextEditingController _emailPinTextController = TextEditingController();
|
||||||
|
TextEditingController _phonePinTextController = TextEditingController();
|
||||||
|
|
||||||
|
bool verifyPin = false;
|
||||||
|
|
||||||
|
PackagesViewModel viewModel() => widget.viewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return BaseView<PackagesViewModel>(
|
||||||
|
allowAny: true,
|
||||||
|
onModelReady: (model) => widget.viewModel = model,
|
||||||
|
builder: (_, model, wi) => verifyPin ? verifyPinWidget() : userDetailWidget()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget verifyPinWidget(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Widget userDetailWidget(){
|
||||||
|
return
|
||||||
|
Container(
|
||||||
|
width: SizeConfig.realScreenWidth * 0.8,
|
||||||
|
height: 270,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Texts("Create Guest Customer", textAlign: TextAlign.center, fontSize: 18, fontWeight: FontWeight.bold,)
|
||||||
|
),
|
||||||
|
SizedBox(height: 30,),
|
||||||
|
AnimatedTextFormField(
|
||||||
|
enabled: _enableInput,
|
||||||
|
controller: _emailTextController,
|
||||||
|
width: 100,
|
||||||
|
loadingController: _loadingController,
|
||||||
|
interval: _nameTextFieldLoadingAnimationInterval,
|
||||||
|
labelText: "Email",
|
||||||
|
prefixIcon: Icon(Icons.email),
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
onFieldSubmitted: (value) {
|
||||||
|
FocusScope.of(context).requestFocus(_phoneFocusNode);
|
||||||
|
},
|
||||||
|
validator: (value){
|
||||||
|
return (value.isEmpty || !emailRegex.hasMatch(value))
|
||||||
|
? 'Invalid email!'
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 30,),
|
||||||
|
AnimatedTextFormField(
|
||||||
|
enabled: _enableInput,
|
||||||
|
controller: _phoneTextController,
|
||||||
|
width: 100,
|
||||||
|
loadingController: _loadingController,
|
||||||
|
interval: _nameTextFieldLoadingAnimationInterval,
|
||||||
|
labelText: "Mobile Number",
|
||||||
|
prefixIcon: Icon(Icons.phone_android),
|
||||||
|
keyboardType: TextInputType.phone,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
onFieldSubmitted: (value) {
|
||||||
|
FocusScope.of(context).requestFocus(_phoneFocusNode);
|
||||||
|
},
|
||||||
|
validator: (value){
|
||||||
|
return (value.isEmpty || !emailRegex.hasMatch(value))
|
||||||
|
? 'Invalid email!'
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Spacer(flex: 1,),
|
||||||
|
|
||||||
|
AnimatedButton(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
loadingColor: Theme.of(context).primaryColor,
|
||||||
|
controller: _submitController,
|
||||||
|
text: TranslationBase.of(context).done,
|
||||||
|
onPressed: (){
|
||||||
|
createCustomer();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createCustomer() async{
|
||||||
|
setState(() => _enableInput = false);
|
||||||
|
|
||||||
|
loading(true);
|
||||||
|
var request = PackagesCustomerRequestModel(email: _emailTextController.text, phoneNumber: _phoneTextController.text);
|
||||||
|
viewModel().service
|
||||||
|
.createCustomer(request, context: context, showLoading: false)
|
||||||
|
.then((value) => success())
|
||||||
|
.catchError((error) => showError(error));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
success() async{
|
||||||
|
loading(false);
|
||||||
|
await Future.delayed(Duration(seconds: 2));
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(String errorMessage) async{
|
||||||
|
loading(false);
|
||||||
|
setState(() => _enableInput = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
loading(bool can){
|
||||||
|
can ? _submitController.forward() : _submitController.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,221 @@
|
|||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/pharmacies/ProductCheckTypeWidget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart' as utils;
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/StarRating.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
|
import 'ClinicOfferAndPackagesPage.dart';
|
||||||
|
|
||||||
|
class OfferAndPackagesDetail extends StatefulWidget{
|
||||||
|
final dynamic model;
|
||||||
|
|
||||||
|
const OfferAndPackagesDetail({@required this.model, Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => OfferAndPackagesDetailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class OfferAndPackagesDetailState extends State<OfferAndPackagesDetail>{
|
||||||
|
|
||||||
|
PackagesViewModel viewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
getLanguageID();
|
||||||
|
|
||||||
|
return BaseView<PackagesViewModel>(
|
||||||
|
onModelReady: (model){
|
||||||
|
viewModel = model;
|
||||||
|
},
|
||||||
|
builder: (_, model, wi) => AppScaffold(
|
||||||
|
appBarTitle: TranslationBase.of(context).offerAndPackages,
|
||||||
|
isShowAppBar: true,
|
||||||
|
isPharmacy: false,
|
||||||
|
showPharmacyCart: false,
|
||||||
|
showHomeAppBarIcon: false,
|
||||||
|
isOfferPackages: true,
|
||||||
|
showOfferPackagesCart: true,
|
||||||
|
isShowDecPage: false,
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 60),
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 1/1,
|
||||||
|
child: utils.applyShadow(
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: utils.Utils.loadNetworkImage(url: "https://wallpaperaccess.com/full/30103.jpg",)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/discount_${'en'}.png',
|
||||||
|
height: 70,
|
||||||
|
width: 70,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Texts(
|
||||||
|
"Child Dental Offer",
|
||||||
|
fontSize: 25,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
Texts(
|
||||||
|
"200 SAR",
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
decoration: TextDecoration.lineThrough,
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 12
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 15),
|
||||||
|
child: Texts(
|
||||||
|
"894.99 SAR",
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green,
|
||||||
|
fontSize: 18
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
StarRating(
|
||||||
|
size: 20,
|
||||||
|
totalCount: null,
|
||||||
|
totalAverage: 5,
|
||||||
|
forceStars: true),
|
||||||
|
|
||||||
|
|
||||||
|
SizedBox(height: 20,),
|
||||||
|
|
||||||
|
|
||||||
|
Texts(
|
||||||
|
"Details",
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 20
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 2/1,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Texts("Detail of offers written here"),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
SizedBox(height: 10,),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: RaisedButton.icon(
|
||||||
|
padding: EdgeInsets.only(top: 5, bottom: 5, left: 0, right: 0),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
side: BorderSide(color: Colors.red, width: 0.5)
|
||||||
|
),
|
||||||
|
color: Colors.red,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.add_shopping_cart_outlined,
|
||||||
|
size: 25,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
label: Texts(
|
||||||
|
"Add to Cart",
|
||||||
|
fontSize: 17, color: Colors.white, fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
onPressed: (){},),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(width: 15,),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: OutlineButton.icon(
|
||||||
|
padding: EdgeInsets.only(top: 5, bottom: 5, left: 0, right: 0),
|
||||||
|
borderSide: BorderSide(width: 1.0, color: Colors.red),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
|
color: Colors.white,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.favorite_rounded,
|
||||||
|
size: 25,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
label: Texts(
|
||||||
|
"Add to Favorites",
|
||||||
|
fontSize: 17, color: Colors.red, fontWeight: FontWeight.normal
|
||||||
|
),
|
||||||
|
onPressed: (){
|
||||||
|
|
||||||
|
},),
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,405 @@
|
|||||||
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/enum/PaymentOptions.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/ResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/locator.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/ClinicOfferAndPackagesPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/PackageOrderCompletedPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/pharmacies/screens/pharmacy-terms-conditions-page.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart' as utils;
|
||||||
|
import 'package:diplomaticquarterapp/widgets/Loader/gif_loader_container.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/carousel_indicator/carousel_indicator.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/in_app_browser/InAppBrowser.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/loadings/ShimmerLoading.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesCartItemCard.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesOfferCard.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_material_pickers/flutter_material_pickers.dart';
|
||||||
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
|
dynamic languageID;
|
||||||
|
const _columnCount = 1;
|
||||||
|
|
||||||
|
AnimationController _animationController;
|
||||||
|
|
||||||
|
class PackagesCartPage extends StatefulWidget {
|
||||||
|
PackagesCartPage();
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PackagesCartPageState createState() => _PackagesCartPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PackagesCartPageState extends State<PackagesCartPage> with AfterLayoutMixin<PackagesCartPage>, SingleTickerProviderStateMixin {
|
||||||
|
getLanguageID() async {
|
||||||
|
languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_agreeTerms = false;
|
||||||
|
_selectedPaymentMethod = null;
|
||||||
|
_animationController = AnimationController(vsync: this, duration: Duration(seconds: 500));
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
viewModel.cartItemList.clear();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
PackagesViewModel viewModel;
|
||||||
|
|
||||||
|
bool loadWidgets = false;
|
||||||
|
|
||||||
|
onTermsClick(bool isAgree) {
|
||||||
|
setState(() => _agreeTerms = isAgree);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTermsInfoClick() {
|
||||||
|
Navigator.push(context, FadePage(page: PharmacyTermsConditions()));
|
||||||
|
}
|
||||||
|
|
||||||
|
onPayNowClick() async{
|
||||||
|
await viewModel.service.placeOrder(context: context,paymentOption: _selectedPaymentMethod.toUpperCase()).then((orderId){
|
||||||
|
if(orderId.runtimeType == int){ // result == order_id
|
||||||
|
var browser = MyInAppBrowser(
|
||||||
|
context: context,
|
||||||
|
onExitCallback: (data, isDone) => paymentClosed(orderId: orderId, withStatus: isDone, data: data)
|
||||||
|
);
|
||||||
|
browser.openPackagesPaymentBrowser(customer_id: viewModel.service.customer.id, order_id: orderId);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
utils.Utils.showErrorToast('Failed to place order, please try again later');
|
||||||
|
}
|
||||||
|
|
||||||
|
}).catchError((error){
|
||||||
|
utils.Utils.showErrorToast(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void afterFirstLayout(BuildContext context) {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BaseView<PackagesViewModel>(
|
||||||
|
allowAny: true,
|
||||||
|
onModelReady: (model) => viewModel = model,
|
||||||
|
builder: (_, model, wi) {
|
||||||
|
return AppScaffold(
|
||||||
|
appBarTitle: TranslationBase.of(context).offerAndPackages,
|
||||||
|
isShowAppBar: true,
|
||||||
|
isPharmacy: false,
|
||||||
|
showPharmacyCart: false,
|
||||||
|
showHomeAppBarIcon: false,
|
||||||
|
isOfferPackages: true,
|
||||||
|
showOfferPackagesCart: false,
|
||||||
|
isShowDecPage: false,
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: StaggeredGridView.countBuilder(
|
||||||
|
crossAxisCount: (_columnCount * _columnCount),
|
||||||
|
itemCount: viewModel.cartItemList.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
|
||||||
|
var item = viewModel.cartItemList[index];
|
||||||
|
return Dismissible(
|
||||||
|
key: Key(index.toString()),
|
||||||
|
direction: DismissDirection.startToEnd,
|
||||||
|
background: _cartItemDeleteContainer(),
|
||||||
|
secondaryBackground: _cartItemDeleteContainer(),
|
||||||
|
confirmDismiss: (direction) async {
|
||||||
|
bool status = await viewModel.service.deleteProductFromCart(item.id, context: context, showLoading: false);
|
||||||
|
return status;
|
||||||
|
},
|
||||||
|
onDismissed: (direction) {
|
||||||
|
debugPrint('Index: $index');
|
||||||
|
viewModel.cartItemList.removeAt(index);
|
||||||
|
},
|
||||||
|
child: PackagesCartItemCard(
|
||||||
|
itemModel: item,
|
||||||
|
shouldStepperChangeApply: (apply,total) async{
|
||||||
|
var request = AddProductToCartRequestModel(product_id: item.productId, quantity: apply);
|
||||||
|
ResponseModel response = await viewModel.service.addProductToCart(request, context: context, showLoading: false).catchError((error){
|
||||||
|
utils.Utils.showErrorToast(error);
|
||||||
|
});
|
||||||
|
return response.status ?? false;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
staggeredTileBuilder: (int index) => StaggeredTile.fit(_columnCount),
|
||||||
|
mainAxisSpacing: 0,
|
||||||
|
crossAxisSpacing: 10,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 0.25,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Texts(
|
||||||
|
TranslationBase.of(context).selectPaymentOption,
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.bold
|
||||||
|
),
|
||||||
|
|
||||||
|
Container(height: 0.25, width: 100, color: Colors.grey[300],),
|
||||||
|
|
||||||
|
_paymentOptions(context, (paymentMethod) {
|
||||||
|
setState(() => _selectedPaymentMethod = paymentMethod);
|
||||||
|
}),
|
||||||
|
|
||||||
|
Container(height: 0.25, color: Colors.grey[300],),
|
||||||
|
|
||||||
|
Container(height: 40,
|
||||||
|
child: _termsAndCondition(context, onSelected: onTermsClick, onInfoClick: onTermsInfoClick)
|
||||||
|
),
|
||||||
|
|
||||||
|
Container(height: 0.25, color: Colors.grey[300],),
|
||||||
|
_payNow(context, onPayNowClick: onPayNowClick)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchData() async {
|
||||||
|
await viewModel.service.cartItems(context: context).catchError((error) {});
|
||||||
|
setState((){});
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentClosed({@required int orderId, @required bool withStatus, dynamic data}) async{
|
||||||
|
viewModel.service.getOrderById(orderId, context: context).then((value){
|
||||||
|
var heading = withStatus ? "Success" : "Failed";
|
||||||
|
var title = "Your order has been placed successfully";
|
||||||
|
var subTitle = "Order# ${value.data.customOrderNumber}";
|
||||||
|
Navigator.of(context).pushReplacement(
|
||||||
|
MaterialPageRoute(builder: (context) => PackageOrderCompletedPage(heading: heading, title: title, subTitle: subTitle))
|
||||||
|
);
|
||||||
|
|
||||||
|
}).catchError((error){
|
||||||
|
debugPrint(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /* Payment Footer Widgets */
|
||||||
|
// ---------------------------
|
||||||
|
String _selectedPaymentMethod;
|
||||||
|
Widget _paymentOptions(BuildContext context, Function(String) onSelected) {
|
||||||
|
double height = 30;
|
||||||
|
|
||||||
|
Widget buttonContent(bool isSelected, String imageName) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: isSelected ? Colors.green[50] : Colors.grey[200],
|
||||||
|
blurRadius: 1,
|
||||||
|
spreadRadius: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
border: Border.all(color: isSelected ? Colors.green : Colors.grey, width: isSelected ? 1 : 0.5)
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Image.asset('assets/images/new-design/$imageName'),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: Container(
|
||||||
|
height: height,
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
child: buttonContent(_selectedPaymentMethod == "mada", 'mada.png'),
|
||||||
|
onTap: () {
|
||||||
|
onSelected("mada");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
child: buttonContent(_selectedPaymentMethod == "visa", 'visa.png'),
|
||||||
|
onTap: () {
|
||||||
|
onSelected("visa");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
child: buttonContent(_selectedPaymentMethod == "mastercard", 'mastercard.png'),
|
||||||
|
onTap: () {
|
||||||
|
onSelected("mastercard");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
child: buttonContent(_selectedPaymentMethod == "installment", 'installment.png'),
|
||||||
|
onTap: () {
|
||||||
|
onSelected("installment");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _agreeTerms = false;
|
||||||
|
Widget _termsAndCondition(BuildContext context, {@required Function(bool) onSelected, @required VoidCallback onInfoClick}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
child: Icon(
|
||||||
|
_agreeTerms ? Icons.check_circle : Icons.radio_button_unchecked_sharp,
|
||||||
|
size: 20,
|
||||||
|
color: _agreeTerms ? Colors.green[600] : Colors.grey[400],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
onSelected(!_agreeTerms);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: Texts(
|
||||||
|
TranslationBase.of(context).pharmacyServiceTermsCondition,
|
||||||
|
fontWeight: FontWeight.normal, fontSize: 13,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
InkWell(
|
||||||
|
child: Icon(
|
||||||
|
Icons.info,
|
||||||
|
size: 20,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
onInfoClick();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _payNow(BuildContext context, {@required VoidCallback onPayNowClick}) {
|
||||||
|
bool isPayNowAQctive = (_agreeTerms && (_selectedPaymentMethod != null));
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Texts('${TranslationBase.of(context).subtotal}: ${'999.9'} ${TranslationBase.of(context).sar}',
|
||||||
|
heightFactor: 1.5, fontWeight: FontWeight.bold, color: Colors.grey, fontSize: 8),
|
||||||
|
Texts('${TranslationBase.of(context).vat}: ${'14.9'} ${TranslationBase.of(context).sar}', heightFactor: 1.5, fontWeight: FontWeight.bold, color: Colors.grey, fontSize: 8),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(3),
|
||||||
|
child: Container(
|
||||||
|
height: 0.25,
|
||||||
|
width: 120,
|
||||||
|
color: Colors.grey[300],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Texts('${TranslationBase.of(context).total}: 999.99 ${TranslationBase.of(context).sar}',
|
||||||
|
heightFactor: 1.5, fontWeight: FontWeight.bold, color: Colors.black54, fontSize: 15)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
RaisedButton(
|
||||||
|
child: Texts(
|
||||||
|
TranslationBase.of(context).payNow,
|
||||||
|
fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.only(top: 5, bottom: 5, left: 0, right: 0),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5), side: BorderSide(color: Theme.of(context).primaryColor, width: 0.5)),
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
onPressed: isPayNowAQctive ? onPayNowClick : null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// -------------------
|
||||||
|
|
||||||
|
Widget _cartItemDeleteContainer() {
|
||||||
|
_animationController.duration = Duration(milliseconds: 500);
|
||||||
|
_animationController.repeat(reverse: true);
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: _animationController,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
blurRadius: 2,
|
||||||
|
spreadRadius: 1,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Texts(
|
||||||
|
"Deleting...",
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.white,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,418 @@
|
|||||||
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/CreateCustomerRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/locator.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/ClinicOfferAndPackagesPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/CreateCustomerDailogPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackagesCartPage.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart' as utils;
|
||||||
|
import 'package:diplomaticquarterapp/widgets/Loader/gif_loader_container.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/carousel_indicator/carousel_indicator.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/loadings/ShimmerLoading.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesOfferCard.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_material_pickers/flutter_material_pickers.dart';
|
||||||
|
|
||||||
|
dynamic languageID;
|
||||||
|
|
||||||
|
class PackagesHomePage extends StatefulWidget {
|
||||||
|
dynamic offersModel;
|
||||||
|
PackagesHomePage({@required this.offersModel});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PackagesHomePageState createState() => _PackagesHomePageState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PackagesHomePageState extends State<PackagesHomePage> with AfterLayoutMixin<PackagesHomePage>{
|
||||||
|
getLanguageID() async {
|
||||||
|
languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
getLanguageID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void afterFirstLayout(BuildContext context) async{
|
||||||
|
viewModel.service.loadOffersPackagesDataForMainPage(context: context, completion: (){
|
||||||
|
setState((){});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controllers
|
||||||
|
var _searchTextController = TextEditingController();
|
||||||
|
var _filterTextController = TextEditingController();
|
||||||
|
var _carouselController = CarouselController();
|
||||||
|
|
||||||
|
|
||||||
|
int carouselIndicatorIndex = 0;
|
||||||
|
CarouselSlider _bannerCarousel;
|
||||||
|
TextField _textFieldSearch;
|
||||||
|
TextField _textFieldFilterSelection;
|
||||||
|
|
||||||
|
ListView _listViewLatestOffers;
|
||||||
|
ListView _listViewBestSeller;
|
||||||
|
|
||||||
|
PackagesViewModel viewModel;
|
||||||
|
|
||||||
|
onCartClick(){
|
||||||
|
if (viewModel.service.customer == null){
|
||||||
|
utils.Utils.showErrorToast("Cart is empty for your current session");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) => PackagesCartPage()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onProductCartClick(PackagesResponseModel product) async {
|
||||||
|
if(viewModel.service.customer == null)
|
||||||
|
viewModel.service.customer = await CreateCustomerDialogPage(context: context).show();
|
||||||
|
|
||||||
|
if(viewModel.service.customer != null) {
|
||||||
|
var request = AddProductToCartRequestModel(product_id: product.id, customer_id: viewModel.service.customer.id);
|
||||||
|
await viewModel.service.addProductToCart(request, context: context).then((response){
|
||||||
|
appScaffold.appBar.badgeUpdater(viewModel.service.cartItemCount);
|
||||||
|
}).catchError((error) {
|
||||||
|
utils.Utils.showErrorToast(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppScaffold appScaffold;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BaseView<PackagesViewModel>(
|
||||||
|
allowAny: true,
|
||||||
|
onModelReady: (model) => viewModel = model,
|
||||||
|
builder: (_, model, wi){
|
||||||
|
return
|
||||||
|
appScaffold =
|
||||||
|
AppScaffold(
|
||||||
|
appBarTitle: TranslationBase.of(context).offerAndPackages,
|
||||||
|
isShowAppBar: true,
|
||||||
|
isPharmacy: false,
|
||||||
|
showPharmacyCart: false,
|
||||||
|
showHomeAppBarIcon: false,
|
||||||
|
isOfferPackages: true,
|
||||||
|
showOfferPackagesCart: true,
|
||||||
|
isShowDecPage: false,
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
|
||||||
|
// Top Banner Carousel
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 2.2/1,
|
||||||
|
child: bannerCarousel()
|
||||||
|
),
|
||||||
|
|
||||||
|
Center(
|
||||||
|
child: CarouselIndicator(
|
||||||
|
activeColor: Theme.of(context).appBarTheme.color,
|
||||||
|
color: Colors.grey[300],
|
||||||
|
cornerRadius: 15,
|
||||||
|
width: 15, height: 15,
|
||||||
|
count: _bannerCarousel.itemCount,
|
||||||
|
index: carouselIndicatorIndex,
|
||||||
|
onClick: (index){
|
||||||
|
debugPrint('onClick at ${index}');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 10,),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// Search Textfield
|
||||||
|
searchTextField(),
|
||||||
|
|
||||||
|
SizedBox(height: 10,),
|
||||||
|
|
||||||
|
// Filter Selection
|
||||||
|
filterOptionSelection(),
|
||||||
|
|
||||||
|
SizedBox(height: 20,),
|
||||||
|
|
||||||
|
// Horizontal Scrollable Cards
|
||||||
|
Texts(
|
||||||
|
"Latest offers",
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
fontSize: 20
|
||||||
|
),
|
||||||
|
|
||||||
|
// Latest Offers Horizontal Scrollable List
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 1.3/1,
|
||||||
|
child: LayoutBuilder(builder: (context, constraints){
|
||||||
|
double itemContentPadding = 10;
|
||||||
|
double itemWidth = (constraints.maxWidth/2) - (itemContentPadding*2);
|
||||||
|
return latestOfferListView(itemWidth: itemWidth, itemContentPadding: itemContentPadding);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 10,),
|
||||||
|
|
||||||
|
Texts(
|
||||||
|
"Best sellers",
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
fontSize: 20
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
// Best Seller Horizontal Scrollable List
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 1.3/1,
|
||||||
|
child: LayoutBuilder(builder: (context, constraints){
|
||||||
|
double itemContentPadding = 10; // 10 is content padding in each item
|
||||||
|
double itemWidth = (constraints.maxWidth/2) - (itemContentPadding*2 /* 2 = LeftRight */);
|
||||||
|
return bestSellerListView(itemWidth: itemWidth, itemContentPadding: itemContentPadding);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
],),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.setOnAppBarCartClick(onCartClick);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
showClinicSelectionList() async {
|
||||||
|
var clinics = viewModel.service.categoryList;
|
||||||
|
if(clinics.isEmpty) {
|
||||||
|
GifLoaderDialogUtils.showMyDialog(context);
|
||||||
|
clinics = await viewModel.service.getAllCategories(OffersCategoriesRequestModel());
|
||||||
|
GifLoaderDialogUtils.hideDialog(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> options = clinics.map((e) => e.toString()).toList();
|
||||||
|
|
||||||
|
showMaterialSelectionPicker(
|
||||||
|
context: context,
|
||||||
|
title: "Select Clinic",
|
||||||
|
items: options,
|
||||||
|
selectedItem: options.first,
|
||||||
|
onChanged: (value) async {
|
||||||
|
var selectedClinic = clinics.firstWhere((element) => element.toString() == value);
|
||||||
|
var clinicProducts = await viewModel.service.getAllProducts(request: OffersProductsRequestModel(categoryId: selectedClinic.id), context: context, showLoading: true);
|
||||||
|
if(clinicProducts.isNotEmpty)
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) => ClinicPackagesPage(products: clinicProducts)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
else
|
||||||
|
utils.Utils.showErrorToast("No offers available for this clinic");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------
|
||||||
|
// Main Widgets of Page
|
||||||
|
//----------------------------------
|
||||||
|
|
||||||
|
CarouselSlider bannerCarousel(){
|
||||||
|
_bannerCarousel = CarouselSlider.builder(
|
||||||
|
carouselController: _carouselController,
|
||||||
|
itemCount: 10,
|
||||||
|
itemBuilder: (BuildContext context, int itemIndex) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 10, bottom: 10, left: 15, right: 15),
|
||||||
|
child: FractionallySizedBox(
|
||||||
|
widthFactor: 1,
|
||||||
|
heightFactor: 1,
|
||||||
|
child: utils.applyShadow(
|
||||||
|
spreadRadius: 1,
|
||||||
|
blurRadius: 5,
|
||||||
|
child: InkWell(
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: utils.Utils.loadNetworkImage(url: "https://wallpaperaccess.com/full/30103.jpg",)
|
||||||
|
),
|
||||||
|
onTap: (){
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) => OfferAndPackagesDetail(model: "",)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
options: CarouselOptions(
|
||||||
|
autoPlayInterval: Duration(milliseconds: 3500),
|
||||||
|
enlargeStrategy: CenterPageEnlargeStrategy.scale,
|
||||||
|
enlargeCenterPage: true,
|
||||||
|
autoPlay: false,
|
||||||
|
autoPlayCurve: Curves.fastOutSlowIn,
|
||||||
|
enableInfiniteScroll: true,
|
||||||
|
autoPlayAnimationDuration: Duration(milliseconds: 1500),
|
||||||
|
viewportFraction: 1,
|
||||||
|
onPageChanged: (page, reason){
|
||||||
|
setState(() {
|
||||||
|
carouselIndicatorIndex = page;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return _bannerCarousel;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField searchTextField(){
|
||||||
|
return _textFieldSearch =
|
||||||
|
TextField(
|
||||||
|
controller: _searchTextController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: EdgeInsets.only(top: 0.0, bottom: 0.0, left: 10, right: 10),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide( width: 0.5, color: Colors.grey),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
const Radius.circular(10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide( width: 1, color: Colors.grey),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
const Radius.circular(10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
hintText: "Search",
|
||||||
|
hintStyle: TextStyle(color: Colors.grey[350], fontWeight: FontWeight.bold),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
onPressed: (){
|
||||||
|
// viewModel.search(text: _searchTextController.text);
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.search_rounded, size: 35,),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget filterOptionSelection(){
|
||||||
|
_textFieldFilterSelection =
|
||||||
|
TextField(
|
||||||
|
enabled: false,
|
||||||
|
controller: _searchTextController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: EdgeInsets.only(top: 0.0, bottom: 0.0, left: 10, right: 10),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
const Radius.circular(10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
disabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide( width: 0.5, color: Colors.grey),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
const Radius.circular(10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
const Radius.circular(10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
hintText: "Browse offers by Clinic",
|
||||||
|
hintStyle: TextStyle(color: Colors.grey[350], fontWeight: FontWeight.bold),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
onPressed: (){
|
||||||
|
showClinicSelectionList();
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.keyboard_arrow_down_rounded, size: 35, color: Colors.grey,),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
child: _textFieldFilterSelection,
|
||||||
|
onTap: (){
|
||||||
|
showClinicSelectionList();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget latestOfferListView({@required double itemWidth, @required double itemContentPadding}){
|
||||||
|
return _listViewLatestOffers =
|
||||||
|
ListView.separated(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: viewModel.bestSellerList.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return PackagesItemCard(itemWidth: itemWidth, itemContentPadding: itemContentPadding, itemModel: viewModel.bestSellerList[index], onCartClick: onProductCartClick,);
|
||||||
|
},
|
||||||
|
separatorBuilder: separator,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget bestSellerListView({@required double itemWidth, @required double itemContentPadding}){
|
||||||
|
return _listViewLatestOffers =
|
||||||
|
ListView.separated(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: viewModel.bestSellerList.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return PackagesItemCard(itemWidth: itemWidth, itemContentPadding: itemContentPadding, itemModel: viewModel.bestSellerList[index], onCartClick: onProductCartClick,);
|
||||||
|
},
|
||||||
|
separatorBuilder: separator,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Widget separator(BuildContext context, int index){
|
||||||
|
return Container(
|
||||||
|
width: 1,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment(-1.0, -2.0),
|
||||||
|
end: Alignment(1.0, 4.0),
|
||||||
|
colors: [
|
||||||
|
Colors.grey,
|
||||||
|
Colors.grey[100],
|
||||||
|
Colors.grey[200],
|
||||||
|
Colors.grey[300],
|
||||||
|
Colors.grey[400],
|
||||||
|
Colors.grey[500]
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
|
dynamic languageID;
|
||||||
|
|
||||||
|
class PackageOrderCompletedPage extends StatelessWidget{
|
||||||
|
double buttonHeight;
|
||||||
|
double buttonWidth;
|
||||||
|
Widget icon;
|
||||||
|
String heading;
|
||||||
|
String title;
|
||||||
|
String subTitle;
|
||||||
|
String actionTitle;
|
||||||
|
|
||||||
|
PackageOrderCompletedPage({this.buttonWidth, this.buttonHeight, @required this.heading, @required this.title, @required this.subTitle, this.actionTitle });
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
assert((heading != null || title != null || subTitle != null), "Data missing in properties");
|
||||||
|
|
||||||
|
buttonWidth = buttonWidth ?? MediaQuery.of(context).size.width/2;
|
||||||
|
buttonHeight = buttonHeight ?? 40;
|
||||||
|
actionTitle = actionTitle ?? TranslationBase.of(context).done;
|
||||||
|
|
||||||
|
return BaseView<PackagesViewModel>(
|
||||||
|
allowAny: true,
|
||||||
|
onModelReady: (model){},
|
||||||
|
builder: (_, model, wi){
|
||||||
|
return Container(
|
||||||
|
color: Colors.white,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 1.2/1,
|
||||||
|
child:
|
||||||
|
iconWidget(context),
|
||||||
|
),
|
||||||
|
|
||||||
|
headingWidget(context),
|
||||||
|
|
||||||
|
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 1/1,
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
titleWidget(context),
|
||||||
|
SizedBox(height: 20,),
|
||||||
|
subTitleWidget(context),
|
||||||
|
SizedBox(height: 50,),
|
||||||
|
actionWidget(context)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget iconWidget(BuildContext context){
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(50),
|
||||||
|
child: icon ?? SvgPicture.asset(
|
||||||
|
"assets/images/svg/success.svg",
|
||||||
|
semanticsLabel: 'icon'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget headingWidget(BuildContext context) => Texts(
|
||||||
|
heading,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 1,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontSize: 35.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget titleWidget(BuildContext context) => Texts(
|
||||||
|
title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 2,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontSize: 25.0,
|
||||||
|
fontWeight: FontWeight.w200,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget subTitleWidget(BuildContext context) => Texts(
|
||||||
|
subTitle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 2,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontSize: 15.0,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Widget actionWidget(BuildContext context) => Container(
|
||||||
|
height: buttonHeight,
|
||||||
|
width: buttonWidth,
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
|
||||||
|
shape:RoundedRectangleBorder(
|
||||||
|
borderRadius: new BorderRadius.circular(buttonHeight/2),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
child: Texts(
|
||||||
|
actionTitle,
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18.0,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
onPressed: (){
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,347 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum TextFieldInertiaDirection {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
}
|
||||||
|
|
||||||
|
Interval _getInternalInterval(
|
||||||
|
double start,
|
||||||
|
double end,
|
||||||
|
double externalStart,
|
||||||
|
double externalEnd, [
|
||||||
|
Curve curve = Curves.linear,
|
||||||
|
]) {
|
||||||
|
return Interval(
|
||||||
|
start + (end - start) * externalStart,
|
||||||
|
start + (end - start) * externalEnd,
|
||||||
|
curve: curve,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimatedTextFormField extends StatefulWidget {
|
||||||
|
AnimatedTextFormField({
|
||||||
|
Key key,
|
||||||
|
this.interval = const Interval(0.0, 1.0),
|
||||||
|
@required this.width,
|
||||||
|
this.loadingController,
|
||||||
|
this.inertiaController,
|
||||||
|
this.inertiaDirection,
|
||||||
|
this.enabled = true,
|
||||||
|
this.labelText,
|
||||||
|
this.prefixIcon,
|
||||||
|
this.suffixIcon,
|
||||||
|
this.keyboardType,
|
||||||
|
this.textInputAction,
|
||||||
|
this.obscureText = false,
|
||||||
|
this.controller,
|
||||||
|
this.focusNode,
|
||||||
|
this.validator,
|
||||||
|
this.onFieldSubmitted,
|
||||||
|
this.onSaved,
|
||||||
|
}) : assert((inertiaController == null && inertiaDirection == null) ||
|
||||||
|
(inertiaController != null && inertiaDirection != null)),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final Interval interval;
|
||||||
|
final AnimationController loadingController;
|
||||||
|
final AnimationController inertiaController;
|
||||||
|
final double width;
|
||||||
|
final bool enabled;
|
||||||
|
final String labelText;
|
||||||
|
final Widget prefixIcon;
|
||||||
|
final Widget suffixIcon;
|
||||||
|
final TextInputType keyboardType;
|
||||||
|
final TextInputAction textInputAction;
|
||||||
|
final bool obscureText;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final FocusNode focusNode;
|
||||||
|
final FormFieldValidator<String> validator;
|
||||||
|
final ValueChanged<String> onFieldSubmitted;
|
||||||
|
final FormFieldSetter<String> onSaved;
|
||||||
|
final TextFieldInertiaDirection inertiaDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AnimatedTextFormFieldState createState() => _AnimatedTextFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnimatedTextFormFieldState extends State<AnimatedTextFormField> {
|
||||||
|
Animation<double> scaleAnimation;
|
||||||
|
Animation<double> sizeAnimation;
|
||||||
|
Animation<double> suffixIconOpacityAnimation;
|
||||||
|
|
||||||
|
Animation<double> fieldTranslateAnimation;
|
||||||
|
Animation<double> iconRotationAnimation;
|
||||||
|
Animation<double> iconTranslateAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
widget.inertiaController?.addStatusListener(handleAnimationStatus);
|
||||||
|
|
||||||
|
final interval = widget.interval;
|
||||||
|
final loadingController = widget.loadingController;
|
||||||
|
|
||||||
|
if (loadingController != null) {
|
||||||
|
scaleAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: loadingController,
|
||||||
|
curve: _getInternalInterval(
|
||||||
|
0, .2, interval.begin, interval.end, Curves.easeOutBack),
|
||||||
|
));
|
||||||
|
suffixIconOpacityAnimation =
|
||||||
|
Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(
|
||||||
|
parent: loadingController,
|
||||||
|
curve: _getInternalInterval(.65, 1.0, interval.begin, interval.end),
|
||||||
|
));
|
||||||
|
_updateSizeAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
final inertiaController = widget.inertiaController;
|
||||||
|
final inertiaDirection = widget.inertiaDirection;
|
||||||
|
final sign = inertiaDirection == TextFieldInertiaDirection.right ? 1 : -1;
|
||||||
|
|
||||||
|
if (inertiaController != null) {
|
||||||
|
fieldTranslateAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: sign * 15.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: inertiaController,
|
||||||
|
curve: Interval(0, .5, curve: Curves.easeOut),
|
||||||
|
reverseCurve: Curves.easeIn,
|
||||||
|
));
|
||||||
|
iconRotationAnimation =
|
||||||
|
Tween<double>(begin: 0.0, end: sign * pi / 12 /* ~15deg */)
|
||||||
|
.animate(CurvedAnimation(
|
||||||
|
parent: inertiaController,
|
||||||
|
curve: Interval(.5, 1.0, curve: Curves.easeOut),
|
||||||
|
reverseCurve: Curves.easeIn,
|
||||||
|
));
|
||||||
|
iconTranslateAnimation =
|
||||||
|
Tween<double>(begin: 0.0, end: 8.0).animate(CurvedAnimation(
|
||||||
|
parent: inertiaController,
|
||||||
|
curve: Interval(.5, 1.0, curve: Curves.easeOut),
|
||||||
|
reverseCurve: Curves.easeIn,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateSizeAnimation() {
|
||||||
|
final interval = widget.interval;
|
||||||
|
final loadingController = widget.loadingController;
|
||||||
|
|
||||||
|
sizeAnimation = Tween<double>(
|
||||||
|
begin: 48.0,
|
||||||
|
end: widget.width,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: loadingController,
|
||||||
|
curve: _getInternalInterval(
|
||||||
|
.2, 1.0, interval.begin, interval.end, Curves.linearToEaseOut),
|
||||||
|
reverseCurve: Curves.easeInExpo,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(AnimatedTextFormField oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (oldWidget.width != widget.width) {
|
||||||
|
_updateSizeAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
widget.inertiaController?.removeStatusListener(handleAnimationStatus);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAnimationStatus(status) {
|
||||||
|
if (status == AnimationStatus.completed) {
|
||||||
|
widget.inertiaController?.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInertiaAnimation(Widget child) {
|
||||||
|
if (widget.inertiaController == null) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: iconTranslateAnimation,
|
||||||
|
builder: (context, child) => Transform(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
transform: Matrix4.identity()
|
||||||
|
..translate(iconTranslateAnimation.value)
|
||||||
|
..rotateZ(iconRotationAnimation.value),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDecoration _getInputDecoration(ThemeData theme) {
|
||||||
|
return InputDecoration(
|
||||||
|
contentPadding: EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: new BorderRadius.circular(10.0),
|
||||||
|
borderSide: new BorderSide(),
|
||||||
|
),
|
||||||
|
labelText: widget.labelText,
|
||||||
|
prefixIcon: _buildInertiaAnimation(widget.prefixIcon),
|
||||||
|
suffixIcon: _buildInertiaAnimation(widget.loadingController != null
|
||||||
|
? FadeTransition(
|
||||||
|
opacity: suffixIconOpacityAnimation,
|
||||||
|
child: widget.suffixIcon,
|
||||||
|
)
|
||||||
|
: widget.suffixIcon),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
Widget textField = TextFormField(
|
||||||
|
controller: widget.controller,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
|
decoration: _getInputDecoration(theme),
|
||||||
|
keyboardType: widget.keyboardType,
|
||||||
|
textInputAction: widget.textInputAction,
|
||||||
|
obscureText: widget.obscureText,
|
||||||
|
onFieldSubmitted: widget.onFieldSubmitted,
|
||||||
|
onSaved: widget.onSaved,
|
||||||
|
validator: widget.validator,
|
||||||
|
enabled: widget.enabled,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (widget.loadingController != null) {
|
||||||
|
textField = ScaleTransition(
|
||||||
|
scale: scaleAnimation,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: sizeAnimation,
|
||||||
|
builder: (context, child) => ConstrainedBox(
|
||||||
|
constraints: BoxConstraints.tightFor(width: sizeAnimation.value),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: textField,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.inertiaController != null) {
|
||||||
|
textField = AnimatedBuilder(
|
||||||
|
animation: fieldTranslateAnimation,
|
||||||
|
builder: (context, child) => Transform.translate(
|
||||||
|
offset: Offset(fieldTranslateAnimation.value, 0),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: textField,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return textField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimatedPasswordTextFormField extends StatefulWidget {
|
||||||
|
AnimatedPasswordTextFormField({
|
||||||
|
Key key,
|
||||||
|
this.interval = const Interval(0.0, 1.0),
|
||||||
|
@required this.animatedWidth,
|
||||||
|
this.loadingController,
|
||||||
|
this.inertiaController,
|
||||||
|
this.inertiaDirection,
|
||||||
|
this.enabled = true,
|
||||||
|
this.labelText,
|
||||||
|
this.keyboardType,
|
||||||
|
this.textInputAction,
|
||||||
|
this.controller,
|
||||||
|
this.focusNode,
|
||||||
|
this.validator,
|
||||||
|
this.onFieldSubmitted,
|
||||||
|
this.onSaved,
|
||||||
|
}) : assert((inertiaController == null && inertiaDirection == null) ||
|
||||||
|
(inertiaController != null && inertiaDirection != null)),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final Interval interval;
|
||||||
|
final AnimationController loadingController;
|
||||||
|
final AnimationController inertiaController;
|
||||||
|
final double animatedWidth;
|
||||||
|
final bool enabled;
|
||||||
|
final String labelText;
|
||||||
|
final TextInputType keyboardType;
|
||||||
|
final TextInputAction textInputAction;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final FocusNode focusNode;
|
||||||
|
final FormFieldValidator<String> validator;
|
||||||
|
final ValueChanged<String> onFieldSubmitted;
|
||||||
|
final FormFieldSetter<String> onSaved;
|
||||||
|
final TextFieldInertiaDirection inertiaDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AnimatedPasswordTextFormFieldState createState() =>
|
||||||
|
_AnimatedPasswordTextFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnimatedPasswordTextFormFieldState
|
||||||
|
extends State<AnimatedPasswordTextFormField> {
|
||||||
|
var _obscureText = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedTextFormField(
|
||||||
|
interval: widget.interval,
|
||||||
|
loadingController: widget.loadingController,
|
||||||
|
inertiaController: widget.inertiaController,
|
||||||
|
width: widget.animatedWidth,
|
||||||
|
enabled: widget.enabled,
|
||||||
|
labelText: widget.labelText,
|
||||||
|
prefixIcon: Icon(Icons.lock, size: 20),
|
||||||
|
suffixIcon: GestureDetector(
|
||||||
|
onTap: () => setState(() => _obscureText = !_obscureText),
|
||||||
|
dragStartBehavior: DragStartBehavior.down,
|
||||||
|
child: AnimatedCrossFade(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
firstCurve: Curves.easeInOutSine,
|
||||||
|
secondCurve: Curves.easeInOutSine,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
layoutBuilder: (Widget topChild, _, Widget bottomChild, __) {
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: <Widget>[bottomChild, topChild],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
firstChild: Icon(
|
||||||
|
Icons.visibility,
|
||||||
|
size: 25.0,
|
||||||
|
semanticLabel: 'show password',
|
||||||
|
),
|
||||||
|
secondChild: Icon(
|
||||||
|
Icons.visibility_off,
|
||||||
|
size: 25.0,
|
||||||
|
semanticLabel: 'hide password',
|
||||||
|
),
|
||||||
|
crossFadeState: _obscureText
|
||||||
|
? CrossFadeState.showFirst
|
||||||
|
: CrossFadeState.showSecond,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
obscureText: _obscureText,
|
||||||
|
keyboardType: widget.keyboardType,
|
||||||
|
textInputAction: widget.textInputAction,
|
||||||
|
controller: widget.controller,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
|
validator: widget.validator,
|
||||||
|
onFieldSubmitted: widget.onFieldSubmitted,
|
||||||
|
onSaved: widget.onSaved,
|
||||||
|
inertiaDirection: widget.inertiaDirection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
typedef StepperCallbackFuture = Future<bool> Function(int apply, int total);
|
||||||
|
|
||||||
|
class StepperView extends StatefulWidget {
|
||||||
|
final double height;
|
||||||
|
final Color foregroundColor;
|
||||||
|
final Color backgroundColor;
|
||||||
|
final double buttonPadding;
|
||||||
|
|
||||||
|
final int initialNumber;
|
||||||
|
final int maxNumber;
|
||||||
|
final int minNumber;
|
||||||
|
final StepperCallbackFuture counterCallback;
|
||||||
|
final Function increaseCallback;
|
||||||
|
final Function decreaseCallback;
|
||||||
|
|
||||||
|
StepperView({this.initialNumber = 1, this.minNumber = 1, this.maxNumber, @required this.counterCallback, this.increaseCallback, this.decreaseCallback, this.height = 25, @required this.foregroundColor, @required this.backgroundColor, this.buttonPadding = 1}){
|
||||||
|
assert((this.initialNumber >= this.minNumber && this.initialNumber <= this.maxNumber));
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
_StepperViewState createState() => _StepperViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StepperViewState extends State<StepperView> {
|
||||||
|
int _currentCount;
|
||||||
|
StepperCallbackFuture _counterCallback;
|
||||||
|
Function _increaseCallback;
|
||||||
|
Function _decreaseCallback;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_currentCount = widget.initialNumber ?? 1;
|
||||||
|
_counterCallback = widget.counterCallback;
|
||||||
|
_increaseCallback = widget.increaseCallback ?? () {};
|
||||||
|
_decreaseCallback = widget.decreaseCallback ?? () {};
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadingInc = false;
|
||||||
|
bool loadingDec = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(widget.buttonPadding),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular((widget.height/2) + (widget.buttonPadding*2)),
|
||||||
|
color: widget.backgroundColor,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
_createDecrementButton(
|
||||||
|
(_currentCount > widget.minNumber) ? () => _decrement() : null,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 25,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
_currentCount.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
height: 1.5,
|
||||||
|
color: Colors.black,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
_createIncrementButton(
|
||||||
|
(_currentCount < widget.maxNumber) ? () => _increment() : null
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _increment() async{
|
||||||
|
doInc({@required bool can}){
|
||||||
|
if(can)
|
||||||
|
setState(() {
|
||||||
|
_currentCount++;
|
||||||
|
_increaseCallback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentCount < widget.maxNumber){
|
||||||
|
if(_counterCallback == null)
|
||||||
|
doInc(can: true);
|
||||||
|
else {
|
||||||
|
setState(() => loadingInc = true);
|
||||||
|
var result = (await _counterCallback(1,_currentCount));
|
||||||
|
doInc(can: result);
|
||||||
|
setState(() => loadingInc = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _decrement() async{
|
||||||
|
doDec({@required bool can}){
|
||||||
|
if(can)
|
||||||
|
setState(() {
|
||||||
|
_currentCount--;
|
||||||
|
_decreaseCallback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (_currentCount > widget.minNumber) {
|
||||||
|
if(_counterCallback == null)
|
||||||
|
doDec(can: true);
|
||||||
|
else {
|
||||||
|
setState(() => loadingDec = true);
|
||||||
|
var result = (await _counterCallback(-1,_currentCount));
|
||||||
|
doDec(can: result);
|
||||||
|
setState(() => loadingDec = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createIncrementButton(Function onPressed,) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
RawMaterialButton(
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
constraints: BoxConstraints(minWidth: widget.height, minHeight: widget.height),
|
||||||
|
onPressed: onPressed,
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: widget.foregroundColor,
|
||||||
|
child:
|
||||||
|
Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: (_currentCount < widget.maxNumber) ? Colors.grey[700] : Colors.grey[400],
|
||||||
|
size: 12.0,
|
||||||
|
),
|
||||||
|
shape: CircleBorder(),
|
||||||
|
),
|
||||||
|
|
||||||
|
if(loadingInc)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
borderRadius: BorderRadius.circular(widget.height/2),
|
||||||
|
),
|
||||||
|
height: widget.height,
|
||||||
|
width: widget.height,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 3,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.green)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createDecrementButton(Function onPressed) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
RawMaterialButton(
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
constraints: BoxConstraints(minWidth: widget.height, minHeight: widget.height),
|
||||||
|
onPressed: onPressed,
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: widget.foregroundColor,
|
||||||
|
child: Icon(
|
||||||
|
Icons.remove,
|
||||||
|
color: (_currentCount > widget.minNumber) ? Colors.grey[700] : Colors.grey[400],
|
||||||
|
size: 12.0,
|
||||||
|
),
|
||||||
|
shape: CircleBorder(),
|
||||||
|
),
|
||||||
|
|
||||||
|
if(loadingDec)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
borderRadius: BorderRadius.circular(widget.height/2),
|
||||||
|
),
|
||||||
|
height: widget.height,
|
||||||
|
width: widget.height,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 3,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.red)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,463 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'data_display/text.dart';
|
||||||
|
|
||||||
|
class AnimatedButton extends StatefulWidget {
|
||||||
|
AnimatedButton({
|
||||||
|
Key key,
|
||||||
|
@required this.text,
|
||||||
|
@required this.onPressed,
|
||||||
|
@required this.controller,
|
||||||
|
this.textColor,
|
||||||
|
this.loadingColor,
|
||||||
|
this.color,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
final Color color;
|
||||||
|
final Color textColor;
|
||||||
|
final Color loadingColor;
|
||||||
|
final Function onPressed;
|
||||||
|
final AnimationController controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AnimatedButtonState createState() => _AnimatedButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnimatedButtonState extends State<AnimatedButton>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
Animation<double> _sizeAnimation;
|
||||||
|
Animation<double> _textOpacityAnimation;
|
||||||
|
Animation<double> _buttonOpacityAnimation;
|
||||||
|
Animation<double> _ringThicknessAnimation;
|
||||||
|
Animation<double> _ringOpacityAnimation;
|
||||||
|
Animation<Color> _colorAnimation;
|
||||||
|
var _isLoading = false;
|
||||||
|
var _hover = false;
|
||||||
|
var _width = 120.0;
|
||||||
|
|
||||||
|
Color _color;
|
||||||
|
Color _loadingColor;
|
||||||
|
|
||||||
|
static const _height = 40.0;
|
||||||
|
static const _loadingCircleRadius = _height / 2;
|
||||||
|
static const _loadingCircleThickness = 4.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
_textOpacityAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: widget.controller,
|
||||||
|
curve: Interval(0.0, .25),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// _colorAnimation
|
||||||
|
// _width, _sizeAnimation
|
||||||
|
|
||||||
|
_buttonOpacityAnimation =
|
||||||
|
Tween<double>(begin: 1.0, end: 0.0).animate(CurvedAnimation(
|
||||||
|
parent: widget.controller,
|
||||||
|
curve: Threshold(.65),
|
||||||
|
));
|
||||||
|
|
||||||
|
_ringThicknessAnimation =
|
||||||
|
Tween<double>(begin: _loadingCircleRadius, end: _loadingCircleThickness)
|
||||||
|
.animate(CurvedAnimation(
|
||||||
|
parent: widget.controller,
|
||||||
|
curve: Interval(.65, .85),
|
||||||
|
));
|
||||||
|
_ringOpacityAnimation =
|
||||||
|
Tween<double>(begin: 1.0, end: 0.0).animate(CurvedAnimation(
|
||||||
|
parent: widget.controller,
|
||||||
|
curve: Interval(.85, 1.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
widget.controller.addStatusListener(handleStatusChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
_updateColorAnimation();
|
||||||
|
_updateWidth();
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateColorAnimation() {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final buttonTheme = theme.floatingActionButtonTheme;
|
||||||
|
|
||||||
|
_color = widget.color ?? buttonTheme.backgroundColor;
|
||||||
|
_loadingColor = widget.loadingColor ?? theme.accentColor;
|
||||||
|
|
||||||
|
_colorAnimation = ColorTween(
|
||||||
|
begin: _color,
|
||||||
|
end: _loadingColor,
|
||||||
|
).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: widget.controller,
|
||||||
|
curve: const Interval(0.0, .65, curve: Curves.fastOutSlowIn),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(AnimatedButton oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (oldWidget.color != widget.color ||
|
||||||
|
oldWidget.loadingColor != widget.loadingColor) {
|
||||||
|
_updateColorAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldWidget.text != widget.text) {
|
||||||
|
_updateWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
widget.controller.removeStatusListener(handleStatusChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleStatusChanged(status) {
|
||||||
|
if (status == AnimationStatus.forward) {
|
||||||
|
setState(() => _isLoading = true);
|
||||||
|
}
|
||||||
|
if (status == AnimationStatus.dismissed) {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sets width and size animation
|
||||||
|
void _updateWidth() {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final fontSize = theme.textTheme.button.fontSize;
|
||||||
|
final renderParagraph = RenderParagraph(
|
||||||
|
TextSpan(
|
||||||
|
text: widget.text,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize,
|
||||||
|
fontWeight: theme.textTheme.button.fontWeight,
|
||||||
|
letterSpacing: theme.textTheme.button.letterSpacing,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
maxLines: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
renderParagraph.layout(BoxConstraints(minWidth: 120.0));
|
||||||
|
|
||||||
|
// text width based on fontSize, plus 45.0 for padding
|
||||||
|
var textWidth =
|
||||||
|
renderParagraph.getMinIntrinsicWidth(fontSize).ceilToDouble() + 45.0;
|
||||||
|
|
||||||
|
// button width is min 120.0 and max 240.0
|
||||||
|
_width = textWidth > 120.0 && textWidth < 240.0
|
||||||
|
? textWidth
|
||||||
|
: textWidth >= 240.0 ? 240.0 : 120.0;
|
||||||
|
|
||||||
|
_sizeAnimation = Tween<double>(begin: 1.0, end: _height / _width)
|
||||||
|
.animate(CurvedAnimation(
|
||||||
|
parent: widget.controller,
|
||||||
|
curve: Interval(0.0, .65, curve: Curves.fastOutSlowIn),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildButtonText(ThemeData theme) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: _textOpacityAnimation,
|
||||||
|
child: AnimatedText(
|
||||||
|
text: widget.text,
|
||||||
|
style: TextStyle(color: widget.textColor ?? Colors.white),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildButton(ThemeData theme) {
|
||||||
|
final buttonTheme = theme.floatingActionButtonTheme;
|
||||||
|
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: _buttonOpacityAnimation,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: Duration(milliseconds: 300),
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _colorAnimation,
|
||||||
|
builder: (context, child) => Material(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(_height/2)
|
||||||
|
),
|
||||||
|
color: _colorAnimation.value,
|
||||||
|
child: child,
|
||||||
|
shadowColor: _color,
|
||||||
|
elevation: !_isLoading
|
||||||
|
? (_hover ? buttonTheme.highlightElevation : buttonTheme.elevation)
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: !_isLoading ? widget.onPressed : null,
|
||||||
|
splashColor: buttonTheme.splashColor,
|
||||||
|
customBorder: buttonTheme.shape,
|
||||||
|
onHighlightChanged: (value) => setState(() => _hover = value),
|
||||||
|
child: SizeTransition(
|
||||||
|
sizeFactor: _sizeAnimation,
|
||||||
|
axis: Axis.horizontal,
|
||||||
|
child: Container(
|
||||||
|
width: _width,
|
||||||
|
height: _height,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: _buildButtonText(theme),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
FadeTransition(
|
||||||
|
opacity: _ringOpacityAnimation,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _ringThicknessAnimation,
|
||||||
|
builder: (context, child) => Ring(
|
||||||
|
color: widget.loadingColor,
|
||||||
|
size: _height,
|
||||||
|
thickness: _ringThicknessAnimation.value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_isLoading)
|
||||||
|
SizedBox(
|
||||||
|
width: _height - _loadingCircleThickness,
|
||||||
|
height: _height - _loadingCircleThickness,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(widget.loadingColor),
|
||||||
|
// backgroundColor: Colors.red,
|
||||||
|
strokeWidth: _loadingCircleThickness,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildButton(theme),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Ring extends StatelessWidget {
|
||||||
|
Ring({
|
||||||
|
Key key,
|
||||||
|
this.color,
|
||||||
|
this.size = 40.0,
|
||||||
|
this.thickness = 2.0,
|
||||||
|
this.value = 1.0,
|
||||||
|
}) : assert(size - thickness > 0),
|
||||||
|
assert(thickness >= 0),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final Color color;
|
||||||
|
final double size;
|
||||||
|
final double thickness;
|
||||||
|
final double value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: size - thickness,
|
||||||
|
height: size - thickness,
|
||||||
|
child: thickness == 0
|
||||||
|
? null
|
||||||
|
: CircularProgressIndicator(
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(color),
|
||||||
|
strokeWidth: thickness,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum AnimatedTextRotation { up, down }
|
||||||
|
|
||||||
|
/// https://medium.com/flutter-community/flutter-challenge-3d-bottom-navigation-bar-48952a5fd996
|
||||||
|
class AnimatedText extends StatefulWidget {
|
||||||
|
AnimatedText({
|
||||||
|
Key key,
|
||||||
|
@required this.text,
|
||||||
|
this.style,
|
||||||
|
this.textRotation = AnimatedTextRotation.up,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
final TextStyle style;
|
||||||
|
final AnimatedTextRotation textRotation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AnimatedTextState createState() => _AnimatedTextState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnimatedTextState extends State<AnimatedText>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
var _newText = '';
|
||||||
|
var _oldText = '';
|
||||||
|
var _layoutHeight = 0.0;
|
||||||
|
final _textKey = GlobalKey();
|
||||||
|
|
||||||
|
Animation<double> _animation;
|
||||||
|
AnimationController _controller;
|
||||||
|
|
||||||
|
double get radius => _layoutHeight / 2;
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
_controller = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
);
|
||||||
|
|
||||||
|
_animation = Tween<double>(begin: 0.0, end: pi / 2).animate(CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeOutBack,
|
||||||
|
));
|
||||||
|
|
||||||
|
_oldText = widget.text;
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
setState(() => _layoutHeight = getWidgetSize(_textKey)?.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(AnimatedText oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (widget.text != oldWidget.text) {
|
||||||
|
_oldText = oldWidget.text;
|
||||||
|
_newText = widget.text;
|
||||||
|
_controller.forward().then((_) {
|
||||||
|
setState(() {
|
||||||
|
final t = _oldText;
|
||||||
|
_oldText = _newText;
|
||||||
|
_newText = t;
|
||||||
|
});
|
||||||
|
_controller.reset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_controller.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4 get _matrix {
|
||||||
|
// Fix: The text is not centered after applying perspective effect in the web build. Idk why
|
||||||
|
if (kIsWeb) {
|
||||||
|
return Matrix4.identity();
|
||||||
|
}
|
||||||
|
return Matrix4.identity()..setEntry(3, 2, .006);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4 _getFrontSideUp(double value) {
|
||||||
|
return _matrix
|
||||||
|
..translate(
|
||||||
|
0.0,
|
||||||
|
-radius * sin(_animation.value),
|
||||||
|
-radius * cos(_animation.value),
|
||||||
|
)
|
||||||
|
..rotateX(-_animation.value); // 0 -> -pi/2
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4 _getBackSideUp(double value) {
|
||||||
|
return _matrix
|
||||||
|
..translate(
|
||||||
|
0.0,
|
||||||
|
radius * cos(_animation.value),
|
||||||
|
-radius * sin(_animation.value),
|
||||||
|
)
|
||||||
|
..rotateX((pi / 2) - _animation.value); // pi/2 -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4 _getFrontSideDown(double value) {
|
||||||
|
return _matrix
|
||||||
|
..translate(
|
||||||
|
0.0,
|
||||||
|
radius * sin(_animation.value),
|
||||||
|
-radius * cos(_animation.value),
|
||||||
|
)
|
||||||
|
..rotateX(_animation.value); // 0 -> pi/2
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4 _getBackSideDown(double value) {
|
||||||
|
return _matrix
|
||||||
|
..translate(
|
||||||
|
0.0,
|
||||||
|
-radius * cos(_animation.value),
|
||||||
|
-radius * sin(_animation.value),
|
||||||
|
)
|
||||||
|
..rotateX(_animation.value - pi / 2); // -pi/2 -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final rollUp = widget.textRotation == AnimatedTextRotation.up;
|
||||||
|
final oldText = Text(
|
||||||
|
_oldText,
|
||||||
|
key: _textKey,
|
||||||
|
style: widget.style,
|
||||||
|
);
|
||||||
|
final newText = Text(
|
||||||
|
_newText,
|
||||||
|
style: widget.style,
|
||||||
|
);
|
||||||
|
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: _animation,
|
||||||
|
builder: (context, child) => Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
if (_animation.value <= toRadian(85))
|
||||||
|
Transform(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
transform: rollUp
|
||||||
|
? _getFrontSideUp(_animation.value)
|
||||||
|
: _getFrontSideDown(_animation.value),
|
||||||
|
child: oldText,
|
||||||
|
),
|
||||||
|
if (_animation.value >= toRadian(5))
|
||||||
|
Transform(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
transform: rollUp
|
||||||
|
? _getBackSideUp(_animation.value)
|
||||||
|
: _getBackSideDown(_animation.value),
|
||||||
|
child: newText,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
double toRadian(double degree) => degree * pi / 180;
|
||||||
|
double lerp(double start, double end, double percent) => (start + percent * (end - start));
|
||||||
|
Size getWidgetSize(GlobalKey key) {
|
||||||
|
final RenderBox renderBox = key.currentContext?.findRenderObject();
|
||||||
|
return renderBox?.size;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,343 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
|
enum TextFieldInertiaDirection {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
}
|
||||||
|
|
||||||
|
Interval _getInternalInterval(
|
||||||
|
double start,
|
||||||
|
double end,
|
||||||
|
double externalStart,
|
||||||
|
double externalEnd, [
|
||||||
|
Curve curve = Curves.linear,
|
||||||
|
]) {
|
||||||
|
return Interval(
|
||||||
|
start + (end - start) * externalStart,
|
||||||
|
start + (end - start) * externalEnd,
|
||||||
|
curve: curve,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimatedTextFormField extends StatefulWidget {
|
||||||
|
AnimatedTextFormField({
|
||||||
|
Key key,
|
||||||
|
this.interval = const Interval(0.0, 1.0),
|
||||||
|
@required this.width,
|
||||||
|
this.loadingController,
|
||||||
|
this.inertiaController,
|
||||||
|
this.inertiaDirection,
|
||||||
|
this.enabled = true,
|
||||||
|
this.labelText,
|
||||||
|
this.prefixIcon,
|
||||||
|
this.suffixIcon,
|
||||||
|
this.keyboardType,
|
||||||
|
this.textInputAction,
|
||||||
|
this.obscureText = false,
|
||||||
|
this.controller,
|
||||||
|
this.focusNode,
|
||||||
|
this.validator,
|
||||||
|
this.onFieldSubmitted,
|
||||||
|
this.onSaved,
|
||||||
|
}) : assert((inertiaController == null && inertiaDirection == null) ||
|
||||||
|
(inertiaController != null && inertiaDirection != null)),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final Interval interval;
|
||||||
|
final AnimationController loadingController;
|
||||||
|
final AnimationController inertiaController;
|
||||||
|
final double width;
|
||||||
|
final bool enabled;
|
||||||
|
final String labelText;
|
||||||
|
final Widget prefixIcon;
|
||||||
|
final Widget suffixIcon;
|
||||||
|
final TextInputType keyboardType;
|
||||||
|
final TextInputAction textInputAction;
|
||||||
|
final bool obscureText;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final FocusNode focusNode;
|
||||||
|
final FormFieldValidator<String> validator;
|
||||||
|
final ValueChanged<String> onFieldSubmitted;
|
||||||
|
final FormFieldSetter<String> onSaved;
|
||||||
|
final TextFieldInertiaDirection inertiaDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AnimatedTextFormFieldState createState() => _AnimatedTextFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnimatedTextFormFieldState extends State<AnimatedTextFormField> {
|
||||||
|
Animation<double> scaleAnimation;
|
||||||
|
Animation<double> sizeAnimation;
|
||||||
|
Animation<double> suffixIconOpacityAnimation;
|
||||||
|
|
||||||
|
Animation<double> fieldTranslateAnimation;
|
||||||
|
Animation<double> iconRotationAnimation;
|
||||||
|
Animation<double> iconTranslateAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
widget.inertiaController?.addStatusListener(handleAnimationStatus);
|
||||||
|
|
||||||
|
final interval = widget.interval;
|
||||||
|
final loadingController = widget.loadingController;
|
||||||
|
|
||||||
|
if (loadingController != null) {
|
||||||
|
scaleAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: loadingController,
|
||||||
|
curve: _getInternalInterval(
|
||||||
|
0, .2, interval.begin, interval.end, Curves.easeOutBack),
|
||||||
|
));
|
||||||
|
suffixIconOpacityAnimation =
|
||||||
|
Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(
|
||||||
|
parent: loadingController,
|
||||||
|
curve: _getInternalInterval(.65, 1.0, interval.begin, interval.end),
|
||||||
|
));
|
||||||
|
_updateSizeAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
final inertiaController = widget.inertiaController;
|
||||||
|
final inertiaDirection = widget.inertiaDirection;
|
||||||
|
final sign = inertiaDirection == TextFieldInertiaDirection.right ? 1 : -1;
|
||||||
|
|
||||||
|
if (inertiaController != null) {
|
||||||
|
fieldTranslateAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: sign * 15.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: inertiaController,
|
||||||
|
curve: Interval(0, .5, curve: Curves.easeOut),
|
||||||
|
reverseCurve: Curves.easeIn,
|
||||||
|
));
|
||||||
|
iconRotationAnimation =
|
||||||
|
Tween<double>(begin: 0.0, end: sign * pi / 12 /* ~15deg */)
|
||||||
|
.animate(CurvedAnimation(
|
||||||
|
parent: inertiaController,
|
||||||
|
curve: Interval(.5, 1.0, curve: Curves.easeOut),
|
||||||
|
reverseCurve: Curves.easeIn,
|
||||||
|
));
|
||||||
|
iconTranslateAnimation =
|
||||||
|
Tween<double>(begin: 0.0, end: 8.0).animate(CurvedAnimation(
|
||||||
|
parent: inertiaController,
|
||||||
|
curve: Interval(.5, 1.0, curve: Curves.easeOut),
|
||||||
|
reverseCurve: Curves.easeIn,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateSizeAnimation() {
|
||||||
|
final interval = widget.interval;
|
||||||
|
final loadingController = widget.loadingController;
|
||||||
|
|
||||||
|
sizeAnimation = Tween<double>(
|
||||||
|
begin: 48.0,
|
||||||
|
end: widget.width,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: loadingController,
|
||||||
|
curve: _getInternalInterval(
|
||||||
|
.2, 1.0, interval.begin, interval.end, Curves.linearToEaseOut),
|
||||||
|
reverseCurve: Curves.easeInExpo,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(AnimatedTextFormField oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (oldWidget.width != widget.width) {
|
||||||
|
_updateSizeAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
widget.inertiaController?.removeStatusListener(handleAnimationStatus);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAnimationStatus(status) {
|
||||||
|
if (status == AnimationStatus.completed) {
|
||||||
|
widget.inertiaController?.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInertiaAnimation(Widget child) {
|
||||||
|
if (widget.inertiaController == null) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: iconTranslateAnimation,
|
||||||
|
builder: (context, child) => Transform(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
transform: Matrix4.identity()
|
||||||
|
..translate(iconTranslateAnimation.value)
|
||||||
|
..rotateZ(iconRotationAnimation.value),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDecoration _getInputDecoration(ThemeData theme) {
|
||||||
|
return InputDecoration(
|
||||||
|
labelText: widget.labelText,
|
||||||
|
prefixIcon: _buildInertiaAnimation(widget.prefixIcon),
|
||||||
|
suffixIcon: _buildInertiaAnimation(widget.loadingController != null
|
||||||
|
? FadeTransition(
|
||||||
|
opacity: suffixIconOpacityAnimation,
|
||||||
|
child: widget.suffixIcon,
|
||||||
|
)
|
||||||
|
: widget.suffixIcon),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
Widget textField = TextFormField(
|
||||||
|
controller: widget.controller,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
|
decoration: _getInputDecoration(theme),
|
||||||
|
keyboardType: widget.keyboardType,
|
||||||
|
textInputAction: widget.textInputAction,
|
||||||
|
obscureText: widget.obscureText,
|
||||||
|
onFieldSubmitted: widget.onFieldSubmitted,
|
||||||
|
onSaved: widget.onSaved,
|
||||||
|
validator: widget.validator,
|
||||||
|
enabled: widget.enabled,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (widget.loadingController != null) {
|
||||||
|
textField = ScaleTransition(
|
||||||
|
scale: scaleAnimation,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: sizeAnimation,
|
||||||
|
builder: (context, child) => ConstrainedBox(
|
||||||
|
constraints: BoxConstraints.tightFor(width: sizeAnimation.value),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: textField,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.inertiaController != null) {
|
||||||
|
textField = AnimatedBuilder(
|
||||||
|
animation: fieldTranslateAnimation,
|
||||||
|
builder: (context, child) => Transform.translate(
|
||||||
|
offset: Offset(fieldTranslateAnimation.value, 0),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: textField,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return textField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimatedPasswordTextFormField extends StatefulWidget {
|
||||||
|
AnimatedPasswordTextFormField({
|
||||||
|
Key key,
|
||||||
|
this.interval = const Interval(0.0, 1.0),
|
||||||
|
@required this.animatedWidth,
|
||||||
|
this.loadingController,
|
||||||
|
this.inertiaController,
|
||||||
|
this.inertiaDirection,
|
||||||
|
this.enabled = true,
|
||||||
|
this.labelText,
|
||||||
|
this.keyboardType,
|
||||||
|
this.textInputAction,
|
||||||
|
this.controller,
|
||||||
|
this.focusNode,
|
||||||
|
this.validator,
|
||||||
|
this.onFieldSubmitted,
|
||||||
|
this.onSaved,
|
||||||
|
}) : assert((inertiaController == null && inertiaDirection == null) ||
|
||||||
|
(inertiaController != null && inertiaDirection != null)),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final Interval interval;
|
||||||
|
final AnimationController loadingController;
|
||||||
|
final AnimationController inertiaController;
|
||||||
|
final double animatedWidth;
|
||||||
|
final bool enabled;
|
||||||
|
final String labelText;
|
||||||
|
final TextInputType keyboardType;
|
||||||
|
final TextInputAction textInputAction;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final FocusNode focusNode;
|
||||||
|
final FormFieldValidator<String> validator;
|
||||||
|
final ValueChanged<String> onFieldSubmitted;
|
||||||
|
final FormFieldSetter<String> onSaved;
|
||||||
|
final TextFieldInertiaDirection inertiaDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AnimatedPasswordTextFormFieldState createState() =>
|
||||||
|
_AnimatedPasswordTextFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnimatedPasswordTextFormFieldState
|
||||||
|
extends State<AnimatedPasswordTextFormField> {
|
||||||
|
var _obscureText = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedTextFormField(
|
||||||
|
interval: widget.interval,
|
||||||
|
loadingController: widget.loadingController,
|
||||||
|
inertiaController: widget.inertiaController,
|
||||||
|
width: widget.animatedWidth,
|
||||||
|
enabled: widget.enabled,
|
||||||
|
labelText: widget.labelText,
|
||||||
|
prefixIcon: Icon(FontAwesomeIcons.lock, size: 20),
|
||||||
|
suffixIcon: GestureDetector(
|
||||||
|
onTap: () => setState(() => _obscureText = !_obscureText),
|
||||||
|
dragStartBehavior: DragStartBehavior.down,
|
||||||
|
child: AnimatedCrossFade(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
firstCurve: Curves.easeInOutSine,
|
||||||
|
secondCurve: Curves.easeInOutSine,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
layoutBuilder: (Widget topChild, _, Widget bottomChild, __) {
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: <Widget>[bottomChild, topChild],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
firstChild: Icon(
|
||||||
|
Icons.visibility,
|
||||||
|
size: 25.0,
|
||||||
|
semanticLabel: 'show password',
|
||||||
|
),
|
||||||
|
secondChild: Icon(
|
||||||
|
Icons.visibility_off,
|
||||||
|
size: 25.0,
|
||||||
|
semanticLabel: 'hide password',
|
||||||
|
),
|
||||||
|
crossFadeState: _obscureText
|
||||||
|
? CrossFadeState.showFirst
|
||||||
|
: CrossFadeState.showSecond,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
obscureText: _obscureText,
|
||||||
|
keyboardType: widget.keyboardType,
|
||||||
|
textInputAction: widget.textInputAction,
|
||||||
|
controller: widget.controller,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
|
validator: widget.validator,
|
||||||
|
onFieldSubmitted: widget.onFieldSubmitted,
|
||||||
|
onSaved: widget.onSaved,
|
||||||
|
inertiaDirection: widget.inertiaDirection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,197 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class CarouselIndicator extends StatefulWidget {
|
||||||
|
/// width of the indicator
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
/// height of the indicator
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
/// space between indicators.
|
||||||
|
final double space;
|
||||||
|
|
||||||
|
/// count of indicator
|
||||||
|
final int count;
|
||||||
|
|
||||||
|
/// active color
|
||||||
|
final Color activeColor;
|
||||||
|
|
||||||
|
/// normal color
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// use this to give some radius to the corner indicator
|
||||||
|
final double cornerRadius;
|
||||||
|
|
||||||
|
/// duration for slide animation
|
||||||
|
final int animationDuration;
|
||||||
|
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
final Function(int index) onClick;
|
||||||
|
|
||||||
|
CarouselIndicator({
|
||||||
|
Key key,
|
||||||
|
this.width: 20.0,
|
||||||
|
this.height: 6,
|
||||||
|
this.space: 5.0,
|
||||||
|
this.count,
|
||||||
|
this.cornerRadius: 6,
|
||||||
|
this.animationDuration: 300,
|
||||||
|
this.color: Colors.white30,
|
||||||
|
this.index,
|
||||||
|
this.activeColor: Colors.white,
|
||||||
|
this.onClick
|
||||||
|
}) : assert(count != null && count != 0),
|
||||||
|
assert(index != null && index >= 0),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return new _CarouselIndicatorState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CarouselIndicatorState extends State<CarouselIndicator>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
/// [Tween] object of type double
|
||||||
|
Tween<double> _tween;
|
||||||
|
|
||||||
|
/// [AnimationController] object
|
||||||
|
AnimationController _animationController;
|
||||||
|
|
||||||
|
/// [Aniamtion] object
|
||||||
|
Animation _animation;
|
||||||
|
|
||||||
|
/// [Paint] object to paint our indicator
|
||||||
|
Paint _paint = new Paint();
|
||||||
|
|
||||||
|
/// Method to initilize [BasePainter] to paint indicators.
|
||||||
|
BasePainter _createPainer() {
|
||||||
|
return SlidePainter(widget, _animation.value, _paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget child = new SizedBox(
|
||||||
|
width: widget.count * widget.width + (widget.count - 1) * widget.space,
|
||||||
|
height: widget.height,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: _createPainer(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
child: child,
|
||||||
|
onTap: (){
|
||||||
|
if(widget.onClick != null)
|
||||||
|
widget.onClick(0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
/// for initial index=0 we do not want to change any value so setting [_tween] to (0.0,0.0),
|
||||||
|
createAnimation(0.0, 0.0);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CarouselIndicator oldWidget) {
|
||||||
|
if (widget.index != oldWidget.index) {
|
||||||
|
if (widget.index != 0) {
|
||||||
|
_animationController.reset();
|
||||||
|
|
||||||
|
/// for each new index we want to change value so setting [_tween] to (oldWidget.index,widget.index) so animation tween from old position to new position rather not start from 0.0 again and again.
|
||||||
|
createAnimation(oldWidget.index.toDouble(), widget.index.toDouble());
|
||||||
|
_animationController.forward();
|
||||||
|
} else {
|
||||||
|
_animationController.reset();
|
||||||
|
createAnimation(oldWidget.index.toDouble(), 0.0);
|
||||||
|
_animationController.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createAnimation(double begin, double end) {
|
||||||
|
_tween = Tween(begin: begin, end: end);
|
||||||
|
_animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: Duration(milliseconds: widget.animationDuration));
|
||||||
|
_animation = _tween.animate(_animationController)
|
||||||
|
..addListener(() {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Base Painter class to draw indicator
|
||||||
|
abstract class BasePainter extends CustomPainter {
|
||||||
|
final CarouselIndicator widget;
|
||||||
|
final double page;
|
||||||
|
final Paint _paint;
|
||||||
|
|
||||||
|
BasePainter(this.widget, this.page, this._paint);
|
||||||
|
|
||||||
|
/// This method will get body to class extending [BasePainter] and this method will draw the sliding indicator which slide over changing index.
|
||||||
|
void draw(Canvas canvas, double space, double width, double height,
|
||||||
|
double radius, double cornerRadius);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
_paint.color = widget.color;
|
||||||
|
double space = widget.space;
|
||||||
|
double width = widget.width;
|
||||||
|
double height = widget.height;
|
||||||
|
double distance = width + space;
|
||||||
|
double radius = width / 2;
|
||||||
|
for (int i = 0, c = widget.count; i < c; ++i) {
|
||||||
|
canvas.drawRRect(
|
||||||
|
RRect.fromRectAndRadius(
|
||||||
|
Rect.fromCenter(
|
||||||
|
center: Offset((i * distance) + radius, radius),
|
||||||
|
width: width,
|
||||||
|
height: height),
|
||||||
|
Radius.circular(widget.cornerRadius)),
|
||||||
|
_paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
_paint.color = widget.activeColor;
|
||||||
|
draw(canvas, space, width, height, radius, widget.cornerRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(BasePainter oldDelegate) {
|
||||||
|
return oldDelegate.page != page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This class we draw the indicator which slides.
|
||||||
|
class SlidePainter extends BasePainter {
|
||||||
|
SlidePainter(CarouselIndicator widget, double page, Paint paint)
|
||||||
|
: super(widget, page, paint);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void draw(Canvas canvas, double space, double width, double height,
|
||||||
|
double radius, double cornerRadius) {
|
||||||
|
canvas.drawRRect(
|
||||||
|
RRect.fromRectAndRadius(
|
||||||
|
Rect.fromCenter(
|
||||||
|
center: Offset(radius + (page * (width + space)), radius),
|
||||||
|
width: width,
|
||||||
|
height: height),
|
||||||
|
Radius.circular(cornerRadius)),
|
||||||
|
_paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/CounterView.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/StarRating.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
|
bool wide = true;
|
||||||
|
|
||||||
|
class PackagesCartItemCard extends StatefulWidget {
|
||||||
|
final PackagesCartItemsResponseModel itemModel;
|
||||||
|
final StepperCallbackFuture shouldStepperChangeApply ;
|
||||||
|
|
||||||
|
const PackagesCartItemCard(
|
||||||
|
{
|
||||||
|
@required this.itemModel,
|
||||||
|
@required this.shouldStepperChangeApply,
|
||||||
|
Key key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => PackagesCartItemCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class PackagesCartItemCardState extends State<PackagesCartItemCard> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
wide = !wide;
|
||||||
|
return Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Card(
|
||||||
|
elevation: 3,
|
||||||
|
shadowColor: Colors.grey[100],
|
||||||
|
color: Colors.white,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 100,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_image(widget.itemModel.product),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_itemName(widget.itemModel.product.getName()),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_itemPrice(widget.itemModel.product.price, context: context),
|
||||||
|
_priceSeperator(),
|
||||||
|
_itemOldPrice(widget.itemModel.product.oldPrice, context: context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_itemCounter(
|
||||||
|
widget.itemModel.quantity,
|
||||||
|
minQuantity: widget.itemModel.product.orderMinimumQuantity,
|
||||||
|
maxQuantity: widget.itemModel.product.orderMaximumQuantity,
|
||||||
|
shouldStepperChangeApply: (apply,total) async{
|
||||||
|
bool success = await widget.shouldStepperChangeApply(apply,total);
|
||||||
|
if(success == true)
|
||||||
|
setState(() => widget.itemModel.quantity = total);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
bottom: 8,
|
||||||
|
left: 10,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_totalPrice((widget.itemModel.product.price * widget.itemModel.quantity), context: context),
|
||||||
|
_totalLabel(context: context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// Product Image
|
||||||
|
// --------------------
|
||||||
|
Widget _image(PackagesResponseModel model) => AspectRatio(
|
||||||
|
aspectRatio: 1/1,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.grey[300], width: 0.25),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(color: Colors.grey[200], blurRadius: 2.0, spreadRadius: 1, offset: Offset(1,1.5))
|
||||||
|
],
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: (model.images.isNotEmpty)
|
||||||
|
? Utils.loadNetworkImage(url: model.images.first.src, fitting:BoxFit.fill)
|
||||||
|
: Container(color: Colors.grey[200])
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// Product Name
|
||||||
|
// --------------------
|
||||||
|
Widget _itemName(String name) => Padding(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: Texts(
|
||||||
|
name,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 15
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Widget _itemPrice(double price, {@required BuildContext context}) => Padding(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: Texts(
|
||||||
|
'${price} ${TranslationBase.of(context).sar}',
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green,
|
||||||
|
fontSize: 15
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// Price Seperator
|
||||||
|
// --------------------
|
||||||
|
Widget _priceSeperator() => Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 3, right: 3),
|
||||||
|
child: Container(height: 0.5, width: 5, color: Colors.grey[100],),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// Product Price
|
||||||
|
// --------------------
|
||||||
|
Widget _itemOldPrice(double oldPrice, {@required BuildContext context}) => Padding(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: Texts(
|
||||||
|
'${oldPrice} ${TranslationBase.of(context).sar}',
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
decoration: TextDecoration.lineThrough,
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 10
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// Product Price
|
||||||
|
// --------------------
|
||||||
|
Widget _itemCounter(int quantity, {int minQuantity, int maxQuantity, StepperCallbackFuture shouldStepperChangeApply}) => Padding(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: StepperView(
|
||||||
|
height: 25,
|
||||||
|
backgroundColor: Colors.grey[300],
|
||||||
|
foregroundColor: Colors.grey[200],
|
||||||
|
initialNumber: quantity,
|
||||||
|
minNumber: minQuantity,
|
||||||
|
maxNumber: maxQuantity,
|
||||||
|
counterCallback: shouldStepperChangeApply,
|
||||||
|
decreaseCallback: (){},
|
||||||
|
increaseCallback: (){},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Widget _totalLabel({@required BuildContext context}) => Padding(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: Texts(
|
||||||
|
'${TranslationBase.of(context).totalWithColonRight}',
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontSize: 13
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Widget _totalPrice(double totalPrice, {@required BuildContext context}) => Padding(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
child: Texts(
|
||||||
|
'${totalPrice.toStringAsFixed(2)} ${TranslationBase.of(context).sar}',
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.green,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,158 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/StarRating.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
|
bool wide = true;
|
||||||
|
|
||||||
|
class PackagesItemCard extends StatefulWidget {
|
||||||
|
final double itemWidth;
|
||||||
|
final double itemHeight;
|
||||||
|
final double itemContentPadding;
|
||||||
|
final PackagesResponseModel itemModel;
|
||||||
|
final Function(PackagesResponseModel product) onCartClick;
|
||||||
|
|
||||||
|
const PackagesItemCard(
|
||||||
|
{
|
||||||
|
this.itemWidth,
|
||||||
|
this.itemHeight,
|
||||||
|
@required this.itemModel,
|
||||||
|
@required this.itemContentPadding,
|
||||||
|
@required this.onCartClick,
|
||||||
|
Key key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => PackagesItemCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class PackagesItemCardState extends State<PackagesItemCard> {
|
||||||
|
imageUrl() => widget.itemModel.images.isNotEmpty ? widget.itemModel.images.first.src : "https://wallpaperaccess.com/full/30103.jpg";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
wide = !wide;
|
||||||
|
return Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: widget.itemContentPadding,
|
||||||
|
right: widget.itemContentPadding,
|
||||||
|
top: widget.itemContentPadding + 5),
|
||||||
|
child: Container(
|
||||||
|
width: widget.itemWidth,
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio:1 / 1,
|
||||||
|
child: applyShadow(
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: Utils.loadNetworkImage(
|
||||||
|
url: imageUrl(),
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
Texts(
|
||||||
|
widget.itemModel.getName(),
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 15
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 10, right: 10),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
Texts(
|
||||||
|
'${widget.itemModel.oldPrice} ${'SAR'}',
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
decoration: TextDecoration.lineThrough,
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 12
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: Texts(
|
||||||
|
'${widget.itemModel.price} ${'SAR'}',
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green,
|
||||||
|
fontSize: 18
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 35),
|
||||||
|
child: StarRating(
|
||||||
|
size: 15,
|
||||||
|
totalCount: null,
|
||||||
|
totalAverage: widget.itemModel.approvedRatingSum.toDouble(),
|
||||||
|
forceStars: true),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Spacer(
|
||||||
|
flex: 1,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
child: Icon(
|
||||||
|
Icons.add_shopping_cart_rounded,
|
||||||
|
size: 30.0,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
widget.onCartClick(widget.itemModel);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Visibility(
|
||||||
|
visible: false,
|
||||||
|
child: InkWell(
|
||||||
|
child: Icon(
|
||||||
|
Icons.favorite,
|
||||||
|
size: 40.0,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
top: 7,
|
||||||
|
left: 2,
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/discount_${'en'}.png',
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|