Merge branch 'development' of https://gitlab.com/Cloud_Solution/diplomatic-quarter into sultan-patientapp
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 {
|
||||
final int categoryId;
|
||||
final int limit;
|
||||
final int page;
|
||||
final int sinceId;
|
||||
// final int page;
|
||||
int sinceId;
|
||||
|
||||
OffersProductsRequestModel({this.categoryId, this.limit, this.page, this.sinceId});
|
||||
OffersProductsRequestModel({this.categoryId, this.limit = 50});
|
||||
|
||||
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';
|
||||
|
||||
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;
|
||||
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 patientGender;
|
||||
String address;
|
||||
String phoneOffice;
|
||||
String itemDescription;
|
||||
int doseTimingID;
|
||||
int frequencyID;
|
||||
int routeID;
|
||||
String clinic;
|
||||
String doctorName;
|
||||
String route;
|
||||
String frequency;
|
||||
String remarks;
|
||||
String name;
|
||||
int days;
|
||||
String startDate;
|
||||
String orderDate;
|
||||
int doseDailyQuantity;
|
||||
int itemID;
|
||||
Null productImage;
|
||||
String sKU;
|
||||
String itemDescriptionN;
|
||||
String routeN;
|
||||
String frequencyN;
|
||||
String imageSRCUrl;
|
||||
String imageThumbUrl;
|
||||
|
||||
PrescriptionReport(
|
||||
{this.patientID,
|
||||
PrescriptionReport({
|
||||
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.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.patientGender,
|
||||
this.address,
|
||||
this.phoneOffice,
|
||||
this.itemDescription,
|
||||
this.doseTimingID,
|
||||
this.frequencyID,
|
||||
this.routeID,
|
||||
this.clinic,
|
||||
this.doctorName,
|
||||
this.route,
|
||||
this.frequency,
|
||||
this.remarks,
|
||||
this.name,
|
||||
this.days,
|
||||
this.startDate,
|
||||
this.orderDate,
|
||||
this.doseDailyQuantity,
|
||||
this.itemID,
|
||||
this.productImage,
|
||||
this.sKU,
|
||||
this.itemDescriptionN,
|
||||
this.routeN,
|
||||
this.frequencyN,
|
||||
this.imageSRCUrl,
|
||||
this.imageThumbUrl});
|
||||
});
|
||||
|
||||
PrescriptionReport.fromJson(Map<String, dynamic> json) {
|
||||
patientID = json['PatientID'];
|
||||
patientName = json['PatientName'];
|
||||
patientAge = json['PatientAge'];
|
||||
patientGender = json['PatientGender'];
|
||||
address = json['Address'];
|
||||
phoneOffice = json['PhoneOffice'];
|
||||
itemDescription = json['ItemDescription'];
|
||||
doseTimingID = json['DoseTimingID'];
|
||||
frequencyID = json['FrequencyID'];
|
||||
routeID = json['RouteID'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
clinic = json['Clinic'];
|
||||
doctorName = json['DoctorName'];
|
||||
route = json['Route'];
|
||||
frequency = json['Frequency'];
|
||||
remarks = json['Remarks'];
|
||||
name = json['Name'];
|
||||
companyName = json['CompanyName'];
|
||||
days = json['Days'];
|
||||
startDate = json['StartDate'];
|
||||
orderDate = json['OrderDate'];
|
||||
doctorName = json['DoctorName'];
|
||||
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'];
|
||||
orderDate = json['OrderDate'];
|
||||
patientID = json['PatientID'];
|
||||
patientName = json['PatientName'];
|
||||
phoneOffice1 = json['PhoneOffice1'];
|
||||
prescriptionQR = json['PrescriptionQR'];
|
||||
prescriptionTimes = json['PrescriptionTimes'];
|
||||
productImage = json['ProductImage'];
|
||||
productImageBase64 = json['ProductImageBase64'];
|
||||
productImageString = json['ProductImageString'];
|
||||
projectID = json['ProjectID'];
|
||||
projectName = json['ProjectName'];
|
||||
remarks = json['Remarks'];
|
||||
route = json['Route'];
|
||||
sKU = json['SKU'];
|
||||
itemDescriptionN = json['ItemDescriptionN'];
|
||||
routeN = json['RouteN'];
|
||||
frequencyN = json['FrequencyN'];
|
||||
imageSRCUrl = json['ImageSRCUrl'];
|
||||
imageThumbUrl = json['ImageThumbUrl'];
|
||||
scaleOffset = json['ScaleOffset'];
|
||||
startDate = json['StartDate'];
|
||||
|
||||
patientAge = json['patientAge'];
|
||||
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() {
|
||||
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['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['PatientGender'] = this.patientGender;
|
||||
data['Address'] = this.address;
|
||||
data['PhoneOffice'] = this.phoneOffice;
|
||||
data['ItemDescription'] = this.itemDescription;
|
||||
data['DoseTimingID'] = this.doseTimingID;
|
||||
data['FrequencyID'] = this.frequencyID;
|
||||
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['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['RouteN'] = this.routeN;
|
||||
data['FrequencyN'] = this.frequencyN;
|
||||
data['ImageSRCUrl'] = this.imageSRCUrl;
|
||||
data['ImageThumbUrl'] = this.imageThumbUrl;
|
||||
data['hasPlan'] = false;
|
||||
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|