Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Hoàng Văn Đạt
mypoint_flutter_app
Commits
c8abf95b
Commit
c8abf95b
authored
Jun 20, 2025
by
DatHV
Browse files
update screen logic
parent
fda33894
Changes
89
Expand all
Hide whitespace changes
Inline
Side-by-side
lib/screen/membership/models/membership_info_response.g.dart
0 → 100644
View file @
c8abf95b
// GENERATED CODE - DO NOT MODIFY BY HAND
part of
'membership_info_response.dart'
;
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MembershipInfoResponse
_$MembershipInfoResponseFromJson
(
Map
<
String
,
dynamic
>
json
,
)
=>
MembershipInfoResponse
(
levels:
(
json
[
'levels'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
MembershipLevelModel
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
(),
membershipRule:
json
[
'membership_rule'
]
as
String
?,
);
Map
<
String
,
dynamic
>
_$MembershipInfoResponseToJson
(
MembershipInfoResponse
instance
,
)
=>
<
String
,
dynamic
>{
'levels'
:
instance
.
levels
,
'membership_rule'
:
instance
.
membershipRule
,
};
lib/screen/membership/models/membership_level_model.dart
0 → 100644
View file @
c8abf95b
import
'package:json_annotation/json_annotation.dart'
;
import
'package:mypoint_flutter_app/screen/home/models/image_model.dart'
;
import
'accumulated_counter_model.dart'
;
import
'membership_level_term_and_condition_model.dart'
;
part
'membership_level_model.g.dart'
;
@JsonSerializable
()
class
MembershipLevelModel
{
final
String
?
id
;
@JsonKey
(
name:
'membership_level_rank'
)
final
String
?
rank
;
@JsonKey
(
name:
'membership_level_name'
)
final
String
?
levelName
;
@JsonKey
(
name:
'membership_level_description'
)
final
String
?
description
;
@JsonKey
(
name:
'membership_level_content'
)
final
String
?
content
;
@JsonKey
(
name:
'level_text_color'
)
final
String
?
levelTextColor
;
@JsonKey
(
name:
'logo'
)
final
String
?
logo
;
@JsonKey
(
name:
'level_start_at_date'
)
final
String
?
levelStartAtDate
;
@JsonKey
(
name:
'level_end_at_date'
)
final
String
?
levelEndAtDate
;
@JsonKey
(
name:
'refresh_level_after_months_from_start_date'
)
final
String
?
refreshAfterMonths
;
@JsonKey
(
name:
'upgrade_when_counter_is_greater_or_equal'
)
final
String
?
upgradePointThreshold
;
@JsonKey
(
name:
'upgrade_when_counter_gmv_is_greater_or_equal'
)
final
String
?
upgradeGmvThreshold
;
@JsonKey
(
name:
'downgrade_level_when_counter_is_less_than'
)
final
String
?
downgradePointThreshold
;
@JsonKey
(
name:
'downgrade_level_when_counter_gmv_is_less_than'
)
final
String
?
downgradeGmvThreshold
;
@JsonKey
(
name:
'upgrade_to_membership_level_id'
)
final
String
?
upgradeToLevelId
;
@JsonKey
(
name:
'downgrade_to_membership_level_id'
)
final
String
?
downgradeToLevelId
;
@JsonKey
(
name:
'membership_level_term_and_conditions'
)
final
List
<
MembershipLevelTermAndConditionModel
>?
conditions
;
@JsonKey
(
name:
'accumulated_counter'
)
final
AccumulatedCounter
?
accumulatedCounter
;
final
List
<
ImageModel
>?
images
;
MembershipLevelModel
({
this
.
id
,
this
.
rank
,
this
.
levelName
,
this
.
description
,
this
.
content
,
this
.
levelTextColor
,
this
.
logo
,
this
.
levelStartAtDate
,
this
.
levelEndAtDate
,
this
.
refreshAfterMonths
,
this
.
upgradePointThreshold
,
this
.
upgradeGmvThreshold
,
this
.
downgradePointThreshold
,
this
.
downgradeGmvThreshold
,
this
.
upgradeToLevelId
,
this
.
downgradeToLevelId
,
this
.
conditions
,
this
.
accumulatedCounter
,
this
.
images
,
});
factory
MembershipLevelModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$MembershipLevelModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$MembershipLevelModelToJson
(
this
);
}
\ No newline at end of file
lib/screen/membership/models/membership_level_model.g.dart
0 → 100644
View file @
c8abf95b
// GENERATED CODE - DO NOT MODIFY BY HAND
part of
'membership_level_model.dart'
;
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MembershipLevelModel
_$MembershipLevelModelFromJson
(
Map
<
String
,
dynamic
>
json
,
)
=>
MembershipLevelModel
(
id:
json
[
'id'
]
as
String
?,
rank:
json
[
'membership_level_rank'
]
as
String
?,
levelName:
json
[
'membership_level_name'
]
as
String
?,
description:
json
[
'membership_level_description'
]
as
String
?,
content:
json
[
'membership_level_content'
]
as
String
?,
levelTextColor:
json
[
'level_text_color'
]
as
String
?,
logo:
json
[
'logo'
]
as
String
?,
levelStartAtDate:
json
[
'level_start_at_date'
]
as
String
?,
levelEndAtDate:
json
[
'level_end_at_date'
]
as
String
?,
refreshAfterMonths:
json
[
'refresh_level_after_months_from_start_date'
]
as
String
?,
upgradePointThreshold:
json
[
'upgrade_when_counter_is_greater_or_equal'
]
as
String
?,
upgradeGmvThreshold:
json
[
'upgrade_when_counter_gmv_is_greater_or_equal'
]
as
String
?,
downgradePointThreshold:
json
[
'downgrade_level_when_counter_is_less_than'
]
as
String
?,
downgradeGmvThreshold:
json
[
'downgrade_level_when_counter_gmv_is_less_than'
]
as
String
?,
upgradeToLevelId:
json
[
'upgrade_to_membership_level_id'
]
as
String
?,
downgradeToLevelId:
json
[
'downgrade_to_membership_level_id'
]
as
String
?,
conditions:
(
json
[
'membership_level_term_and_conditions'
]
as
List
<
dynamic
>?)
?.
map
(
(
e
)
=>
MembershipLevelTermAndConditionModel
.
fromJson
(
e
as
Map
<
String
,
dynamic
>,
),
)
.
toList
(),
accumulatedCounter:
json
[
'accumulated_counter'
]
==
null
?
null
:
AccumulatedCounter
.
fromJson
(
json
[
'accumulated_counter'
]
as
Map
<
String
,
dynamic
>,
),
images:
(
json
[
'images'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
ImageModel
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
(),
);
Map
<
String
,
dynamic
>
_$MembershipLevelModelToJson
(
MembershipLevelModel
instance
,
)
=>
<
String
,
dynamic
>{
'id'
:
instance
.
id
,
'membership_level_rank'
:
instance
.
rank
,
'membership_level_name'
:
instance
.
levelName
,
'membership_level_description'
:
instance
.
description
,
'membership_level_content'
:
instance
.
content
,
'level_text_color'
:
instance
.
levelTextColor
,
'logo'
:
instance
.
logo
,
'level_start_at_date'
:
instance
.
levelStartAtDate
,
'level_end_at_date'
:
instance
.
levelEndAtDate
,
'refresh_level_after_months_from_start_date'
:
instance
.
refreshAfterMonths
,
'upgrade_when_counter_is_greater_or_equal'
:
instance
.
upgradePointThreshold
,
'upgrade_when_counter_gmv_is_greater_or_equal'
:
instance
.
upgradeGmvThreshold
,
'downgrade_level_when_counter_is_less_than'
:
instance
.
downgradePointThreshold
,
'downgrade_level_when_counter_gmv_is_less_than'
:
instance
.
downgradeGmvThreshold
,
'upgrade_to_membership_level_id'
:
instance
.
upgradeToLevelId
,
'downgrade_to_membership_level_id'
:
instance
.
downgradeToLevelId
,
'membership_level_term_and_conditions'
:
instance
.
conditions
,
'accumulated_counter'
:
instance
.
accumulatedCounter
,
'images'
:
instance
.
images
,
};
lib/screen/membership/models/membership_level_term_and_condition_model.dart
0 → 100644
View file @
c8abf95b
import
'package:json_annotation/json_annotation.dart'
;
part
'membership_level_term_and_condition_model.g.dart'
;
@JsonSerializable
()
class
MembershipLevelTermAndConditionModel
{
final
String
?
icon
;
final
String
?
title
;
final
String
?
content
;
MembershipLevelTermAndConditionModel
({
this
.
icon
,
this
.
title
,
this
.
content
,
});
factory
MembershipLevelTermAndConditionModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$MembershipLevelTermAndConditionModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$MembershipLevelTermAndConditionModelToJson
(
this
);
}
\ No newline at end of file
lib/screen/membership/models/membership_level_term_and_condition_model.g.dart
0 → 100644
View file @
c8abf95b
// GENERATED CODE - DO NOT MODIFY BY HAND
part of
'membership_level_term_and_condition_model.dart'
;
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MembershipLevelTermAndConditionModel
_$MembershipLevelTermAndConditionModelFromJson
(
Map
<
String
,
dynamic
>
json
)
=>
MembershipLevelTermAndConditionModel
(
icon:
json
[
'icon'
]
as
String
?,
title:
json
[
'title'
]
as
String
?,
content:
json
[
'content'
]
as
String
?,
);
Map
<
String
,
dynamic
>
_$MembershipLevelTermAndConditionModelToJson
(
MembershipLevelTermAndConditionModel
instance
,
)
=>
<
String
,
dynamic
>{
'icon'
:
instance
.
icon
,
'title'
:
instance
.
title
,
'content'
:
instance
.
content
,
};
lib/screen/order_menu/order_menu_screen.dart
0 → 100644
View file @
c8abf95b
import
'package:flutter/material.dart'
;
import
'package:mypoint_flutter_app/widgets/custom_app_bar.dart'
;
import
'../../widgets/custom_navigation_bar.dart'
;
class
_OrderMenuItem
{
final
String
title
;
final
IconData
icon
;
_OrderMenuItem
({
required
this
.
title
,
required
this
.
icon
});
}
class
OrderMenuScreen
extends
StatelessWidget
{
OrderMenuScreen
({
super
.
key
});
final
List
<
_OrderMenuItem
>
items
=
[
_OrderMenuItem
(
title:
'Thẻ nạp của tôi'
,
icon:
Icons
.
credit_card
),
_OrderMenuItem
(
title:
'Sổ sức khỏe điện tử'
,
icon:
Icons
.
medical_services_outlined
),
_OrderMenuItem
(
title:
'Dịch vụ giao thông'
,
icon:
Icons
.
traffic_outlined
),
];
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
CustomNavigationBar
(
title:
"Đơn mua"
,),
body:
Container
(
color:
Colors
.
white
,
child:
ListView
.
separated
(
itemCount:
items
.
length
,
separatorBuilder:
(
_
,
__
)
=>
const
Divider
(
height:
1
),
itemBuilder:
(
context
,
index
)
{
final
item
=
items
[
index
];
return
InkWell
(
onTap:
()
{
print
(
"Tapped on
${item.title}
"
);
// TODO: handle tap
},
child:
Container
(
height:
48
,
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
),
child:
Row
(
children:
[
Icon
(
item
.
icon
,
color:
Colors
.
black54
),
const
SizedBox
(
width:
12
),
Expanded
(
child:
Text
(
item
.
title
,
style:
const
TextStyle
(
fontSize:
16
,
color:
Colors
.
black87
),
),
),
const
Icon
(
Icons
.
chevron_right
,
color:
Colors
.
black54
),
],
),
),
);
}
),
),
);
}
}
lib/screen/pageDetail/campaign_detail_screen.dart
View file @
c8abf95b
...
...
@@ -176,7 +176,22 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
}
}
else
if
(
mediaType
==
MediaTypeItemCampaign
.
text
.
key
)
{
if
(
item
.
contentText
!=
null
&&
item
.
contentText
!.
isNotEmpty
)
{
widgets
.
add
(
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
16
),
child:
HtmlWidget
(
item
.
contentText
!)));
widgets
.
add
(
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
16
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
if
((
item
.
contentCaption
??
""
).
isNotEmpty
)
Text
(
item
.
contentCaption
!,
style:
const
TextStyle
(
fontSize:
16
,
fontWeight:
FontWeight
.
bold
),
),
const
SizedBox
(
height:
4
),
HtmlWidget
(
item
.
contentText
!),
],
)
)
);
}
}
else
if
(
mediaType
==
MediaTypeItemCampaign
.
pageLink
.
key
)
{
if
(
item
.
pages
?.
isNotEmpty
==
true
)
{
...
...
lib/screen/pageDetail/campaign_detail_viewmodel.dart
View file @
c8abf95b
...
...
@@ -21,6 +21,22 @@ class CampaignDetailViewModel extends RestfulApiViewModel {
fetchWebsitePage
(
type
!);
return
;
}
fetchFAQItems
();
}
Future
<
void
>
fetchFAQItems
()
async
{
showLoading
();
isLoading
(
true
);
client
.
websiteFolderGetPageList
({
"folder_uri"
:
"ABOUT"
}).
then
((
value
)
{
hideLoading
();
isLoading
(
false
);
final
pageId
=
(
value
.
data
?.
items
??
[]).
first
.
pageId
??
""
;
if
(
pageId
.
isEmpty
)
{
errorMessage
.
value
=
Constants
.
commonError
;
}
else
{
fetchWebsitePageGetDetail
(
pageId
);
}
});
}
void
fetchWebsitePage
(
DetailPageRuleType
type
)
{
...
...
lib/screen/personal/personal_edit_item_model.dart
0 → 100644
View file @
c8abf95b
import
'package:flutter/cupertino.dart'
;
import
'package:mypoint_flutter_app/extensions/datetime_extensions.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'package:mypoint_flutter_app/screen/personal/personal_gender.dart'
;
import
'../../networking/model_maker.dart'
;
import
'../location_address/location_address_viewmodel.dart'
;
enum
SectionPersonalEditType
{
name
,
nickname
,
phone
,
email
,
identificationNumber
,
birthday
,
gender
,
address
,
province
,
district
,
}
class
PersonalEditDataModel
{
String
?
name
;
String
?
nickname
;
String
?
phone
;
String
?
email
;
String
?
identificationNumber
;
DateTime
?
birthday
;
PersonalGender
?
gender
;
String
?
address
;
AddressBaseModel
?
province
;
AddressBaseModel
?
district
;
String
?
avatar
;
PersonalEditDataModel
({
this
.
name
,
this
.
nickname
,
this
.
phone
,
this
.
email
,
this
.
identificationNumber
,
this
.
birthday
,
this
.
gender
,
this
.
address
,
this
.
province
,
this
.
district
,
this
.
avatar
,
});
Json
get
body
=>
<
String
,
dynamic
>
{
"worker_site_id"
:
DataPreference
.
instance
.
profile
?.
workerSite
?.
id
,
"fullname"
:
name
,
"nickname"
:
nickname
,
"date_of_birth"
:
birthday
?.
toFormattedString
(
format:
"yyyy-MM-dd"
),
"sex"
:
gender
?.
value
??
"U"
,
"address_full"
:
address
,
"address_district_code"
:
district
?.
code
??
""
,
"address_province_code"
:
province
?.
code
??
""
,
"identification_number"
:
identificationNumber
,
"email"
:
email
,
"avatar"
:
""
,
"avatar_2"
:
""
,
};
}
class
PersonalEditItemModel
{
String
?
title
;
String
?
value
;
String
?
hintText
;
bool
?
isRequired
;
bool
?
showDropIcon
;
String
?
warningText
;
bool
?
isEditable
=
true
;
TextInputType
?
keyboardType
;
SectionPersonalEditType
?
sectionType
;
PersonalEditItemModel
({
this
.
title
,
this
.
value
,
this
.
hintText
,
this
.
isRequired
=
false
,
this
.
showDropIcon
=
false
,
this
.
warningText
,
this
.
isEditable
=
true
,
this
.
keyboardType
,
this
.
sectionType
,
});
static
List
<
PersonalEditItemModel
>
personalEditFields
(
PersonalEditDataModel
data
)
{
return
[
PersonalEditItemModel
(
title:
"Họ và tên"
,
value:
data
.
name
,
hintText:
"Chưa cập nhật"
,
isRequired:
true
,
showDropIcon:
false
,
sectionType:
SectionPersonalEditType
.
name
,
),
PersonalEditItemModel
(
title:
"Biệt danh"
,
value:
data
.
nickname
,
hintText:
"Chưa cập nhật"
,
isRequired:
false
,
showDropIcon:
false
,
sectionType:
SectionPersonalEditType
.
nickname
,
),
PersonalEditItemModel
(
title:
"Số điện thoại"
,
value:
data
.
phone
,
isRequired:
false
,
showDropIcon:
false
,
isEditable:
false
,
keyboardType:
TextInputType
.
phone
,
sectionType:
SectionPersonalEditType
.
phone
,
),
PersonalEditItemModel
(
title:
"Email"
,
value:
data
.
email
,
hintText:
"Chưa cập nhật"
,
isRequired:
false
,
showDropIcon:
false
,
keyboardType:
TextInputType
.
emailAddress
,
sectionType:
SectionPersonalEditType
.
email
,
),
PersonalEditItemModel
(
title:
"Số CMND/CCCD"
,
value:
data
.
identificationNumber
,
hintText:
"Chưa cập nhật"
,
isRequired:
false
,
showDropIcon:
false
,
keyboardType:
TextInputType
.
number
,
sectionType:
SectionPersonalEditType
.
identificationNumber
,
),
PersonalEditItemModel
(
title:
"Ngày sinh"
,
value:
data
.
birthday
?.
toFormattedString
()
??
""
,
hintText:
"Ngày/Tháng/Năm"
,
isRequired:
true
,
showDropIcon:
true
,
warningText:
"Ngày sinh chỉ được cập nhật 1 lần duy nhất"
,
sectionType:
SectionPersonalEditType
.
birthday
,
),
PersonalEditItemModel
(
title:
"Giới tính"
,
value:
data
.
gender
?.
display
??
""
,
hintText:
"Khác"
,
isRequired:
true
,
showDropIcon:
true
,
sectionType:
SectionPersonalEditType
.
gender
,
),
PersonalEditItemModel
(
title:
"Địa chỉ"
,
value:
data
.
address
,
hintText:
"Số nhà, đường, phường/xã"
,
isRequired:
false
,
showDropIcon:
false
,
sectionType:
SectionPersonalEditType
.
address
,
),
PersonalEditItemModel
(
title:
"Tỉnh, Thành phố"
,
value:
data
.
province
?.
name
??
""
,
hintText:
"Chọn tỉnh thành"
,
isRequired:
false
,
showDropIcon:
true
,
sectionType:
SectionPersonalEditType
.
province
,
),
PersonalEditItemModel
(
title:
"Quận, Huyện"
,
value:
data
.
district
?.
name
??
""
,
hintText:
"Chọn quận/huyện"
,
isRequired:
false
,
showDropIcon:
true
,
sectionType:
SectionPersonalEditType
.
district
,
),
];
}
}
lib/screen/personal/personal_edit_screen.dart
0 → 100644
View file @
c8abf95b
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'package:mypoint_flutter_app/screen/personal/personal_edit_item_model.dart'
;
import
'package:mypoint_flutter_app/screen/personal/personal_edit_viewmodel.dart'
;
import
'package:mypoint_flutter_app/screen/personal/personal_gender.dart'
;
import
'package:mypoint_flutter_app/widgets/custom_app_bar.dart'
;
import
'../../base/base_screen.dart'
;
import
'../../base/basic_state.dart'
;
import
'../../resouce/base_color.dart'
;
import
'../../shared/router_gage.dart'
;
import
'../../widgets/alert/data_alert_model.dart'
;
import
'../../widgets/bottom_sheet_helper.dart'
;
import
'../../widgets/time_picker_widget.dart'
;
class
PersonalEditScreen
extends
BaseScreen
{
const
PersonalEditScreen
({
super
.
key
});
@override
State
<
PersonalEditScreen
>
createState
()
=>
_PersonalEditScreenState
();
}
class
_PersonalEditScreenState
extends
BaseState
<
PersonalEditScreen
>
with
BasicState
{
final
viewModel
=
Get
.
put
(
PersonalEditViewModel
());
@override
initState
()
{
super
.
initState
();
viewModel
.
onShowAlertError
=
(
message
)
{
showAlertError
(
content:
message
,
barrierDismissible:
true
,
onConfirmed:
null
);
};
viewModel
.
updateProfileResponseSuccess
=
()
{
DataAlertModel
alertData
=
DataAlertModel
(
localHeaderImage:
"assets/images/ic_pipi_05.png"
,
title:
"Thông báo"
,
description:
"Cập nhật thông tin cá nhân thành công!"
,
buttons:
[
AlertButton
(
text:
"Đã hiểu"
,
onPressed:
()
=>
Get
.
back
(),
bgColor:
BaseColor
.
primary500
,
textColor:
Colors
.
white
,
),
],
);
showAlert
(
data:
alertData
);
};
}
@override
Widget
createBody
()
{
return
GestureDetector
(
onTap:
()
=>
FocusScope
.
of
(
context
).
unfocus
(),
behavior:
HitTestBehavior
.
translucent
,
child:
Scaffold
(
appBar:
CustomAppBar
.
back
(
title:
"Chỉnh sửa thông tin cá nhân"
),
body:
Obx
(()
{
List
<
PersonalEditItemModel
>
items
;
final
editDataModel
=
viewModel
.
editDataModel
.
value
;
if
(
editDataModel
==
null
)
{
return
const
SizedBox
.
shrink
();
}
items
=
PersonalEditItemModel
.
personalEditFields
(
editDataModel
);
return
CustomScrollView
(
physics:
const
AlwaysScrollableScrollPhysics
(),
slivers:
[
SliverToBoxAdapter
(
child:
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
32
,
vertical:
12
),
child:
_buildAvatarItem
(),
),
),
SliverList
(
delegate:
SliverChildBuilderDelegate
((
context
,
index
)
{
final
item
=
items
[
index
];
return
_editPersonalItem
(
item
);
},
childCount:
items
.
length
),
),
],
);
}),
bottomNavigationBar:
_buildContinueButton
(),
),
);
}
Widget
_buildContinueButton
()
{
return
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
16
),
decoration:
const
BoxDecoration
(
color:
Colors
.
white
,
boxShadow:
[
BoxShadow
(
color:
Colors
.
black54
,
blurRadius:
8
,
offset:
Offset
(
0
,
4
))],
),
child:
SafeArea
(
top:
false
,
child:
Obx
(()
{
return
SizedBox
(
width:
double
.
infinity
,
height:
48
,
child:
ElevatedButton
(
onPressed:
viewModel
.
isValidate
.
value
?
()
{
viewModel
.
updateProfile
();
}
:
null
,
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
viewModel
.
isValidate
.
value
?
BaseColor
.
primary500
:
Colors
.
grey
,
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
)),
),
child:
const
Text
(
'Cập Nhật Ngay'
,
style:
TextStyle
(
fontSize:
16
,
color:
Colors
.
white
,
fontWeight:
FontWeight
.
bold
),
),
),
);
}),
),
);
}
Widget
_buildAvatarItem
()
{
return
Center
(
child:
Stack
(
alignment:
Alignment
.
bottomRight
,
children:
[
ClipOval
(
child:
Image
.
asset
(
"assets/images/bg_default_11.png"
,
width:
100
,
height:
100
,
fit:
BoxFit
.
cover
)),
Positioned
(
bottom:
4
,
right:
4
,
child:
GestureDetector
(
onTap:
()
{
print
(
"Change avatar tapped"
);
},
child:
Container
(
padding:
const
EdgeInsets
.
all
(
4
),
decoration:
const
BoxDecoration
(
color:
Colors
.
red
,
shape:
BoxShape
.
circle
),
child:
const
Icon
(
Icons
.
camera_alt
,
color:
Colors
.
white
,
size:
18
),
),
),
),
],
),
);
}
_onTapItemChangeValue
(
PersonalEditItemModel
item
)
async
{
if
(
item
.
sectionType
==
SectionPersonalEditType
.
province
||
item
.
sectionType
==
SectionPersonalEditType
.
district
)
{
viewModel
.
navigateToLocationScreen
(
item
);
}
else
if
(
item
.
sectionType
==
SectionPersonalEditType
.
birthday
)
{
if
((
DataPreference
.
instance
.
profile
?.
workerSite
?.
birthday
??
""
).
isNotEmpty
)
return
;
final
now
=
DateTime
.
now
();
final
picked
=
await
showDatePicker
(
context:
context
,
initialDate:
viewModel
.
birthday
??
now
,
firstDate:
DateTime
(
1900
),
lastDate:
DateTime
(
2100
),
);
if
(
picked
!=
null
)
{
setState
(()
{
viewModel
.
birthday
=
picked
;
viewModel
.
editDataModel
.
value
?.
birthday
=
picked
;
viewModel
.
isValidate
.
value
=
viewModel
.
validate
();
});
}
}
else
if
(
item
.
sectionType
==
SectionPersonalEditType
.
gender
)
{
showGenderPicker
(
context:
context
,
selected:
viewModel
.
gender
??
PersonalGender
.
unknown
,
onSelected:
(
gender
)
{
setState
(()
{
viewModel
.
gender
=
gender
;
viewModel
.
editDataModel
.
value
?.
gender
=
gender
;
});
},
);
}
}
Widget
_editPersonalItem
(
PersonalEditItemModel
item
)
{
final
isTapField
=
item
.
sectionType
==
SectionPersonalEditType
.
province
||
item
.
sectionType
==
SectionPersonalEditType
.
district
||
item
.
sectionType
==
SectionPersonalEditType
.
gender
;
final
isDate
=
item
.
sectionType
==
SectionPersonalEditType
.
birthday
;
final
isTappableItem
=
isTapField
||
isDate
;
return
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
8
,
bottom:
8
,
left:
16
,
right:
16
),
// all(16.0),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
// const SizedBox(height: 8),
RichText
(
text:
TextSpan
(
text:
item
.
title
,
style:
const
TextStyle
(
fontSize:
14
,
color:
Colors
.
black
,
fontWeight:
FontWeight
.
w600
),
children:
[
if
(
item
.
isRequired
==
true
)
const
TextSpan
(
text:
' (*)'
,
style:
TextStyle
(
color:
Colors
.
red
,
fontWeight:
FontWeight
.
bold
)),
],
),
),
const
SizedBox
(
height:
6
),
GestureDetector
(
onTap:
isTappableItem
?
()
=>
_onTapItemChangeValue
(
item
)
:
null
,
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
12
),
decoration:
BoxDecoration
(
color:
item
.
isEditable
==
true
?
Colors
.
white
:
Colors
.
grey
.
shade100
,
border:
Border
.
all
(
color:
Colors
.
grey
.
shade300
),
borderRadius:
BorderRadius
.
circular
(
8
),
// color: Colors.grey.shade50,
),
child:
Row
(
children:
[
Expanded
(
child:
TextField
(
keyboardType:
item
.
keyboardType
??
TextInputType
.
text
,
controller:
TextEditingController
(
text:
item
.
value
??
""
),
enabled:
isTappableItem
?
false
:
(
item
.
isEditable
??
true
),
decoration:
InputDecoration
.
collapsed
(
hintText:
item
.
hintText
??
""
,
hintStyle:
TextStyle
(
color:
Colors
.
grey
,
fontSize:
14
),
),
style:
TextStyle
(
color:
item
.
isEditable
??
true
?
Colors
.
black87
:
Colors
.
grey
,
fontSize:
16
),
onChanged:
(
value
)
{
viewModel
.
updateItemEditData
(
item
,
value
);
viewModel
.
isValidate
.
value
=
viewModel
.
validate
();
},
),
),
if
(
item
.
showDropIcon
==
true
)
const
Icon
(
Icons
.
expand_more
,
size:
20
,
color:
Colors
.
grey
),
],
),
),
),
const
SizedBox
(
height:
6
),
if
(
item
.
warningText
!=
null
)
Row
(
children:
[
const
Icon
(
Icons
.
warning_amber_rounded
,
color:
Colors
.
orange
,
size:
14
),
const
SizedBox
(
width:
4
),
Text
(
item
.
warningText
??
''
,
style:
const
TextStyle
(
fontSize:
12
,
color:
Colors
.
orange
)),
],
),
// const SizedBox(height: 12),
],
),
);
}
void
showGenderPicker
({
required
BuildContext
context
,
required
PersonalGender
selected
,
required
Function
(
PersonalGender
gender
)
onSelected
,
})
{
showModalBottomSheet
(
context:
context
,
backgroundColor:
Colors
.
white
,
shape:
const
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
vertical
(
top:
Radius
.
circular
(
16
))),
builder:
(
_
)
{
final
genderList
=
PersonalGender
.
values
.
toList
();
return
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
12
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
end
,
children:
[
Expanded
(
child:
Center
(
child:
const
Text
(
'Giới tính'
,
style:
TextStyle
(
fontWeight:
FontWeight
.
bold
,
fontSize:
16
)),
),
),
GestureDetector
(
onTap:
()
=>
Navigator
.
of
(
context
).
pop
(),
child:
const
Icon
(
Icons
.
close
,
size:
20
)),
],
),
),
const
Divider
(
height:
1
),
...
genderList
.
map
((
gender
)
{
final
isSelected
=
selected
==
gender
;
return
ListTile
(
title:
Text
(
gender
.
display
,
style:
TextStyle
(
fontWeight:
isSelected
?
FontWeight
.
bold
:
FontWeight
.
normal
,
color:
isSelected
?
Colors
.
blue
:
Colors
.
black
,
),
),
trailing:
isSelected
?
const
Icon
(
Icons
.
check
,
color:
Colors
.
blue
)
:
null
,
onTap:
()
{
Navigator
.
of
(
context
).
pop
();
onSelected
(
gender
);
},
);
}).
toList
(),
const
Divider
(
height:
100
),
],
);
},
);
}
}
lib/screen/personal/personal_edit_viewmodel.dart
0 → 100644
View file @
c8abf95b
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/extensions/string_extension.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_request.dart'
;
import
'package:mypoint_flutter_app/screen/personal/personal_edit_item_model.dart'
;
import
'package:mypoint_flutter_app/screen/personal/personal_gender.dart'
;
import
'../../base/restful_api_viewmodel.dart'
;
import
'../../configs/constants.dart'
;
import
'../../preference/data_preference.dart'
;
import
'../../shared/router_gage.dart'
;
import
'../location_address/location_address_viewmodel.dart'
;
class
PersonalEditViewModel
extends
RestfulApiViewModel
{
var
editDataModel
=
Rxn
<
PersonalEditDataModel
>();
var
province
=
Rxn
<
AddressBaseModel
>();
var
district
=
Rxn
<
AddressBaseModel
>();
DateTime
?
birthday
;
PersonalGender
?
gender
;
RxBool
isValidate
=
false
.
obs
;
void
Function
(
String
message
)?
onShowAlertError
;
void
Function
()?
updateProfileResponseSuccess
;
@override
void
onInit
()
{
super
.
onInit
();
final
profile
=
DataPreference
.
instance
.
profile
;
if
(
profile
==
null
)
return
;
province
.
value
=
AddressBaseModel
(
code:
profile
?.
workerSite
?.
locationProvinceCode
,
name:
profile
?.
workerSite
?.
locationProvinceName
,
);
district
.
value
=
AddressBaseModel
(
code:
profile
?.
workerSite
?.
locationDistrictCode
,
name:
profile
?.
workerSite
?.
locationDistrictName
,
);
birthday
=
profile
?.
workerSite
?.
birthday
?.
toDateFormat
(
'yyyy-MM-dd'
);
gender
=
PersonalGender
.
from
(
profile
.
workerSite
?.
sex
??
"U"
);
editDataModel
.
value
=
PersonalEditDataModel
(
name:
DataPreference
.
instance
.
fullName
,
nickname:
profile
?.
workerSite
?.
nickname
,
phone:
profile
?.
workerSite
?.
phoneNumber
,
email:
profile
?.
workerSite
?.
email
,
identificationNumber:
profile
?.
workerSite
?.
identificationNumber
,
birthday:
birthday
,
gender:
gender
,
address:
profile
?.
workerSite
?.
addressFull
,
province:
province
.
value
,
district:
district
.
value
,
);
isValidate
.
value
=
validate
();
}
updateItemEditData
(
PersonalEditItemModel
item
,
String
value
)
{
if
(
editDataModel
.
value
==
null
)
return
;
switch
(
item
.
sectionType
??
SectionPersonalEditType
.
nickname
)
{
case
SectionPersonalEditType
.
name
:
editDataModel
.
value
?.
name
=
value
;
break
;
case
SectionPersonalEditType
.
nickname
:
editDataModel
.
value
?.
nickname
=
value
;
break
;
case
SectionPersonalEditType
.
phone
:
editDataModel
.
value
?.
phone
=
value
;
break
;
case
SectionPersonalEditType
.
email
:
editDataModel
.
value
?.
email
=
value
;
break
;
case
SectionPersonalEditType
.
identificationNumber
:
editDataModel
.
value
?.
identificationNumber
=
value
;
break
;
case
SectionPersonalEditType
.
address
:
editDataModel
.
value
?.
address
=
value
;
break
;
default
:
break
;
}
}
Future
<
void
>
updateProfile
()
async
{
showLoading
();
try
{
final
body
=
editDataModel
.
value
?.
body
??
{};
final
response
=
await
client
.
updateWorkerSiteProfile
(
body
);
hideLoading
();
if
(
response
.
status
?.
toLowerCase
()
==
"success"
)
{
updateProfileResponseSuccess
?.
call
();
_getUserProfile
();
}
else
{
onShowAlertError
?.
call
(
response
.
errorMessage
??
Constants
.
commonError
);
}
}
catch
(
error
)
{
hideLoading
();
onShowAlertError
?.
call
(
Constants
.
commonError
);
}
}
void
_getUserProfile
()
{
client
.
getUserProfile
().
then
((
value
)
async
{
final
userProfile
=
value
.
data
;
if
(
value
.
isSuccess
&&
userProfile
!=
null
)
{
DataPreference
.
instance
.
saveUserProfile
(
userProfile
);
}
});
}
navigateToLocationScreen
(
PersonalEditItemModel
item
)
async
{
if
(
item
.
sectionType
==
null
)
return
;
if
(
item
.
sectionType
==
SectionPersonalEditType
.
province
)
{
final
result
=
await
Get
.
toNamed
(
locationAddressScreen
,
arguments:
{
"type"
:
"province"
,
"selectedCode"
:
province
.
value
?.
code
??
""
},
);
if
(
result
is
AddressBaseModel
&&
result
.
code
!=
province
.
value
?.
code
)
{
province
.
value
=
result
;
district
.
value
=
null
;
editDataModel
.
value
?.
district
=
null
;
editDataModel
.
value
?.
province
=
result
;
editDataModel
.
refresh
();
}
}
else
if
(
item
.
sectionType
==
SectionPersonalEditType
.
district
&&
(
province
.
value
?.
code
??
""
).
isNotEmpty
)
{
final
result
=
await
Get
.
toNamed
(
locationAddressScreen
,
arguments:
{
"type"
:
"district"
,
"selectedCode"
:
district
.
value
?.
code
??
""
,
"provinceCode"
:
province
.
value
?.
code
??
""
,
},
);
if
(
result
is
AddressBaseModel
)
{
district
.
value
=
result
;
editDataModel
.
value
?.
district
=
result
;
editDataModel
.
refresh
();
}
}
isValidate
.
value
=
validate
();
}
bool
validate
()
{
final
model
=
editDataModel
.
value
;
if
(
model
==
null
)
return
false
;
if
((
model
.
name
??
''
).
isEmpty
)
{
return
false
;
}
if
(
model
.
birthday
==
null
)
{
return
false
;
}
if
(
model
.
gender
==
null
||
model
.
gender
==
'notAllowed'
)
{
return
false
;
}
if
((
model
.
email
??
''
).
isNotEmpty
&&
!
isValidEmail
(
model
.
email
!))
{
return
false
;
}
return
true
;
}
bool
isValidEmail
(
String
email
)
{
final
emailRegex
=
RegExp
(
r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'
);
return
emailRegex
.
hasMatch
(
email
);
}
}
lib/screen/personal/personal_gender.dart
0 → 100644
View file @
c8abf95b
enum
PersonalGender
{
female
,
male
,
unknown
;
static
PersonalGender
from
(
String
gender
)
{
switch
(
gender
)
{
case
'F'
:
return
PersonalGender
.
female
;
case
'M'
:
return
PersonalGender
.
male
;
default
:
return
PersonalGender
.
unknown
;
}
}
String
get
value
{
switch
(
this
)
{
case
PersonalGender
.
female
:
return
'F'
;
case
PersonalGender
.
male
:
return
'M'
;
case
PersonalGender
.
unknown
:
return
'U'
;
}
}
String
get
display
{
switch
(
this
)
{
case
PersonalGender
.
female
:
return
'Nữ'
;
case
PersonalGender
.
male
:
return
'Nam'
;
case
PersonalGender
.
unknown
:
return
'Khác'
;
}
}
}
lib/screen/personal/personal_screen.dart
View file @
c8abf95b
This diff is collapsed.
Click to expand it.
lib/screen/shopping/affiliate_tab_screen.dart
View file @
c8abf95b
...
...
@@ -8,6 +8,7 @@ import '../../base/basic_state.dart';
import
'../../resouce/base_color.dart'
;
import
'../../widgets/bottom_sheet_helper.dart'
;
import
'../../widgets/custom_navigation_bar.dart'
;
import
'../home/header_home_viewmodel.dart'
;
import
'affiliate_overview.dart'
;
import
'affiliate_tab_viewmodel.dart'
;
...
...
@@ -18,30 +19,32 @@ class AffiliateTabScreen extends BaseScreen {
State
<
AffiliateTabScreen
>
createState
()
=>
_AffiliateTabScreenState
();
}
class
_AffiliateTabScreenState
extends
BaseState
<
AffiliateTabScreen
>
with
BasicState
{
class
_AffiliateTabScreenState
extends
BaseState
<
AffiliateTabScreen
>
with
BasicState
{
final
AffiliateTabViewModel
viewModel
=
Get
.
put
(
AffiliateTabViewModel
());
final
_headerHomeVM
=
Get
.
find
<
HeaderHomeViewModel
>();
@override
Widget
createBody
()
{
return
Scaffold
(
backgroundColor:
Colors
.
grey
.
shade50
,
appBar:
CustomNavigationBar
(
title:
"Mua sắm"
,
showBackButton:
false
,
rightButtons:
[
IconButton
(
icon:
const
Icon
(
Icons
.
info
,
color:
Colors
.
white
),
onPressed:
()
{
final
descriptions
=
viewModel
.
overview
.
value
?.
descriptions
??
[];
if
(
descriptions
.
isNotEmpty
)
{
BottomSheetHelper
.
showBottomSheetPopup
(
child:
AffiliateOverviewPopup
(
descriptions:
viewModel
.
overview
.
value
?.
descriptions
??
[]),
);
}
},
),
],
),
title:
"Mua sắm"
,
showBackButton:
false
,
backgroundImage:
_headerHomeVM
.
headerData
.
background
??
"assets/images/bg_header_navi.png"
,
rightButtons:
[
IconButton
(
icon:
const
Icon
(
Icons
.
info
,
color:
Colors
.
white
),
onPressed:
()
{
final
descriptions
=
viewModel
.
overview
.
value
?.
descriptions
??
[];
if
(
descriptions
.
isNotEmpty
)
{
BottomSheetHelper
.
showBottomSheetPopup
(
child:
AffiliateOverviewPopup
(
descriptions:
viewModel
.
overview
.
value
?.
descriptions
??
[]),
);
}
},
),
],
),
body:
Obx
(()
{
if
(
viewModel
.
isLoading
.
value
)
{
return
const
Center
(
child:
CircularProgressIndicator
());
...
...
@@ -56,10 +59,7 @@ class _AffiliateTabScreenState extends BaseState<AffiliateTabScreen> with Basic
children:
[
Container
(
padding:
const
EdgeInsets
.
all
(
12
),
decoration:
BoxDecoration
(
color:
Colors
.
amber
.
shade100
,
borderRadius:
BorderRadius
.
circular
(
12
),
),
decoration:
BoxDecoration
(
color:
Colors
.
amber
.
shade100
,
borderRadius:
BorderRadius
.
circular
(
12
)),
child:
Row
(
children:
[
const
Text
(
"Điểm hoàn:"
,
style:
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
normal
)),
...
...
@@ -67,7 +67,9 @@ class _AffiliateTabScreenState extends BaseState<AffiliateTabScreen> with Basic
Image
.
asset
(
'assets/images/ic_point.png'
,
width:
20
,
height:
20
),
const
SizedBox
(
width:
4
),
const
Text
(
"0"
,
style:
TextStyle
(
fontSize:
20
,
color:
BaseColor
.
primary400
,
fontWeight:
FontWeight
.
bold
)),
"0"
,
style:
TextStyle
(
fontSize:
20
,
color:
BaseColor
.
primary400
,
fontWeight:
FontWeight
.
bold
),
),
const
Spacer
(),
Icon
(
Icons
.
arrow_forward_ios
,
color:
BaseColor
.
primary400
,
size:
16
),
],
...
...
@@ -89,9 +91,9 @@ class _AffiliateTabScreenState extends BaseState<AffiliateTabScreen> with Basic
],
),
),
AffiliateBrand
(
brands:
viewModel
.
affiliateBrands
,
),
AffiliateCategory
(
categories:
viewModel
.
affiliateCategories
,
),
AffiliateProductTopSale
(
products:
viewModel
.
affiliateProducts
,
),
AffiliateBrand
(
brands:
viewModel
.
affiliateBrands
),
AffiliateCategory
(
categories:
viewModel
.
affiliateCategories
),
AffiliateProductTopSale
(
products:
viewModel
.
affiliateProducts
),
],
),
),
...
...
lib/screen/shopping/sub_widget/build_affiliate_brand.dart
View file @
c8abf95b
...
...
@@ -12,6 +12,8 @@ class AffiliateBrand extends StatelessWidget {
if
(
brands
.
isEmpty
)
{
return
const
SizedBox
.
shrink
();
}
final
space
=
8.0
;
final
width
=
(
MediaQuery
.
of
(
context
).
size
.
width
-
space
*
2
-
32
)/
3
;
return
Column
(
children:
[
const
SizedBox
(
height:
24
),
...
...
@@ -29,9 +31,9 @@ class AffiliateBrand extends StatelessWidget {
shrinkWrap:
true
,
physics:
const
NeverScrollableScrollPhysics
(),
itemCount:
brands
.
length
,
gridDelegate:
const
SliverGridDelegateWithFixedCrossAxisCount
(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount
(
crossAxisCount:
3
,
childAspectRatio:
3
/
3.2
,
childAspectRatio:
width
/(
width
+
30
)
,
crossAxisSpacing:
8
,
mainAxisSpacing:
8
,
),
...
...
lib/screen/shopping/sub_widget/build_affiliate_category.dart
View file @
c8abf95b
...
...
@@ -11,6 +11,7 @@ class AffiliateCategory extends StatelessWidget {
if
(
categories
.
isEmpty
)
{
return
const
SizedBox
.
shrink
();
}
final
width
=
(
MediaQuery
.
of
(
context
).
size
.
width
-
32
)/
4
;
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
...
...
@@ -28,7 +29,7 @@ class AffiliateCategory extends StatelessWidget {
shrinkWrap:
true
,
physics:
const
NeverScrollableScrollPhysics
(),
crossAxisCount:
4
,
childAspectRatio:
3
/
3.8
,
childAspectRatio:
width
/
(
width
+
30
)
,
children:
categories
.
map
((
category
)
=>
_buildAffiliateCategoryItem
(
category
)).
toList
(),
),
),
...
...
lib/screen/shopping/sub_widget/build_affiliate_product_topsale.dart
View file @
c8abf95b
...
...
@@ -58,13 +58,7 @@ class AffiliateProductTopSale extends StatelessWidget {
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
),
boxShadow:
[
BoxShadow
(
color:
Colors
.
black
.
withOpacity
(
0.05
),
blurRadius:
4
,
offset:
const
Offset
(
0
,
2
),
)
],
boxShadow:
[
BoxShadow
(
color:
Colors
.
black
.
withOpacity
(
0.05
),
blurRadius:
4
,
offset:
const
Offset
(
0
,
2
))],
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
...
...
@@ -76,27 +70,19 @@ class AffiliateProductTopSale extends StatelessWidget {
scale:
1
,
width:
double
.
infinity
,
fit:
BoxFit
.
cover
,
errorBuilder:
(
_
,
__
,
___
)
=>
Image
.
asset
(
'assets/images/ic_logo.png'
,
),
errorBuilder:
(
_
,
__
,
___
)
=>
Image
.
asset
(
'assets/images/ic_logo.png'
),
),
),
const
SizedBox
(
height:
6
),
Text
(
title
,
maxLines:
2
,
overflow:
TextOverflow
.
ellipsis
,
style:
const
TextStyle
(
fontSize:
13
),
),
Text
(
title
,
maxLines:
2
,
overflow:
TextOverflow
.
ellipsis
,
style:
const
TextStyle
(
fontSize:
13
)),
const
SizedBox
(
height:
4
),
Text
(
formattedPrice
,
style:
const
TextStyle
(
color:
Colors
.
blueAccent
,
fontSize:
14
,
fontWeight:
FontWeight
.
bold
,
),
style:
const
TextStyle
(
color:
Colors
.
blueAccent
,
fontSize:
14
,
fontWeight:
FontWeight
.
bold
),
),
const
SizedBox
(
height:
2
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
RichText
(
text:
TextSpan
(
...
...
@@ -110,12 +96,18 @@ class AffiliateProductTopSale extends StatelessWidget {
],
),
),
const
Spacer
(),
Text
(
"
${sold.toNum()?.formatCompactNumber()}
đã bán"
,
style:
TextStyle
(
fontSize:
12
,
color:
Colors
.
grey
)),
Flexible
(
child:
Text
(
"
${sold.toNum()?.formatCompactNumber()}
đã bán"
,
style:
TextStyle
(
fontSize:
12
,
color:
Colors
.
grey
),
overflow:
TextOverflow
.
ellipsis
,
maxLines:
1
,
),
),
],
),
],
),
);
}
}
\ No newline at end of file
}
lib/screen/transaction/transaction_detail_screen.dart
View file @
c8abf95b
...
...
@@ -117,10 +117,21 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w
return
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
8
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
s
paceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
s
tart
,
children:
[
Text
(
item
.
name
??
''
,
style:
TextStyle
(
fontSize:
16
,
color:
Colors
.
grey
.
shade700
)),
Text
(
item
.
value
??
''
,
style:
const
TextStyle
(
fontSize:
16
,
fontWeight:
FontWeight
.
w500
)),
Expanded
(
flex:
2
,
child:
Text
(
item
.
name
??
''
,
style:
TextStyle
(
fontSize:
16
,
color:
Colors
.
grey
.
shade700
)),
),
Expanded
(
flex:
3
,
child:
Text
(
item
.
value
??
''
,
style:
const
TextStyle
(
fontSize:
16
,
fontWeight:
FontWeight
.
w500
),
textAlign:
TextAlign
.
right
,
softWrap:
true
,
),
),
],
),
);
...
...
@@ -461,7 +472,8 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w
ElevatedButton
(
onPressed:
_viewModel
.
finalTotal
>
0
&&
_viewModel
.
selectedPaymentMethodIndex
<
0
?
null
:
_viewModel
.
requestPaymentProduct
,
?
null
:
_viewModel
.
requestPaymentProduct
,
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
BaseColor
.
primary500
,
foregroundColor:
Colors
.
white
,
...
...
lib/screen/voucher/models/product_model.dart
View file @
c8abf95b
...
...
@@ -61,6 +61,13 @@ class ProductModel {
return
content
?.
name
;
}
int
?
get
percentDiscount
{
if
(
previewFlashSale
?.
isFlashSalePrice
==
true
)
{
return
previewFlashSale
?.
percentTag
;
}
return
null
;
}
ProductType
get
productType
{
return
ProductTypeExt
.
from
(
type
)
??
ProductType
.
voucher
;
}
...
...
@@ -101,6 +108,34 @@ class ProductModel {
return
media
!.
firstWhere
((
item
)
=>
item
.
type
==
MediaType
.
banner16_9
,
orElse:
()
=>
media
!.
first
);
}
double
get
progress
{
if
(
previewFlashSale
?.
fsQuantityTotal
!=
null
&&
previewFlashSale
?.
fsQuantitySold
!=
null
&&
previewFlashSale
!.
fsQuantityTotal
!
>
0
)
{
return
previewFlashSale
!.
fsQuantitySold
!
/
previewFlashSale
!.
fsQuantityTotal
!;
}
return
0.0
;
}
bool
get
isShowProsessSoldItem
{
return
previewFlashSale
?.
isFlashSale
==
true
&&
(
previewFlashSale
?.
fsQuantityTotal
??
0
)
>
0
;
}
double
get
extendSpaceFlashSaleItem
{
double
heightPrice
=
24
;
double
heightName
=
36
;
double
space
=
4
;
double
extend
=
heightPrice
+
heightName
+
space
*
5
;
if
(
previewFlashSale
?.
rewardContent
!=
null
)
{
extend
+=
space
;
extend
+=
30
;
}
if
(
isShowProsessSoldItem
)
{
extend
+=
space
;
extend
+=
18
;
}
print
(
"extendSpaceFlashSaleItem
$extend
"
);
return
extend
;
}
factory
ProductModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$ProductModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$ProductModelToJson
(
this
);
...
...
lib/screen/voucher/my_voucher/my_product_list_viewmodel.dart
0 → 100644
View file @
c8abf95b
import
'package:get/get_rx/src/rx_types/rx_types.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_request.dart'
;
import
'../../../base/restful_api_viewmodel.dart'
;
import
'../../home/models/my_product_model.dart'
;
class
MyProductListViewModel
extends
RestfulApiViewModel
{
final
RxInt
selectedTabIndex
=
0
.
obs
;
var
myProducts
=
<
MyProductModel
>[].
obs
;
@override
void
onInit
()
{
super
.
onInit
();
freshData
(
isRefresh:
true
);
}
void
selectTab
(
int
index
)
{
selectedTabIndex
.
value
=
index
;
freshData
(
isRefresh:
true
);
}
void
freshData
({
bool
isRefresh
=
false
})
{
final
body
=
{
"index"
:
isRefresh
?
0
:
myProducts
.
length
,
"size"
:
20
,
"status"
:
selectedTabIndex
.
value
,
};
client
.
getCustomerProducts
(
body
).
then
((
response
)
{
final
result
=
response
.
data
??
[];
if
(
isRefresh
)
{
myProducts
.
clear
();
}
myProducts
.
addAll
(
result
);
}).
catchError
((
error
)
{
myProducts
.
clear
();
print
(
'Error fetching products:
$error
'
);
});
}
}
\ No newline at end of file
Prev
1
2
3
4
5
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment