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
e9cf8244
Commit
e9cf8244
authored
Oct 28, 2025
by
DatHV
Browse files
update fix bug, handle error.
parent
a0bcdab2
Changes
104
Show whitespace changes
Inline
Side-by-side
lib/networking/restful_api_client_all_request.dart
View file @
e9cf8244
...
...
@@ -14,6 +14,7 @@ import '../directional/directional_screen.dart';
import
'../model/auth/biometric_register_response_model.dart'
;
import
'../model/auth/login_token_response_model.dart'
;
import
'../model/auth/profile_response_model.dart'
;
import
'../preference/package_info.dart'
;
import
'../screen/health_book/health_book_model.dart'
;
import
'../screen/history_point/models/history_point_models.dart'
;
import
'../screen/history_point/models/transaction_summary_by_date_model.dart'
;
...
...
@@ -82,9 +83,10 @@ import '../screen/voucher/models/search_product_response_model.dart';
extension
RestfulAPIClientAllRequest
on
RestfulAPIClient
{
Future
<
BaseResponseModel
<
UpdateResponseModel
>>
checkUpdateApp
()
async
{
final
operatingSystem
=
kIsWeb
?
"web"
:
Platform
.
operatingSystem
;
final
version
=
kIsWeb
?
"1.0.0"
:
Platform
.
version
;
final
body
=
{
"operating_system"
:
operatingSystem
,
"software_model"
:
"MyPoint"
,
"version"
:
version
,
"build_number"
:
"1"
};
final
operatingSystem
=
Platform
.
operatingSystem
;
final
version
=
await
AppInfoHelper
.
version
;
final
buildNumber
=
await
AppInfoHelper
.
buildNumber
;
final
body
=
{
"operating_system"
:
operatingSystem
,
"software_model"
:
"MyPoint"
,
"version"
:
version
,
"build_number"
:
buildNumber
};
return
requestNormal
(
APIPaths
.
checkUpdate
,
Method
.
POST
,
body
,
(
data
)
=>
UpdateResponseModel
.
fromJson
(
data
as
Json
));
}
...
...
lib/networking/restful_api_viewmodel.dart
View file @
e9cf8244
...
...
@@ -5,13 +5,20 @@ import '../base/base_response_model.dart';
import
'../base/base_view_model.dart'
;
import
'../configs/constants.dart'
;
import
'../base/app_navigator.dart'
;
import
'dio_extra_keys.dart'
;
import
'dio_http_service.dart'
;
import
'error_mapper.dart'
;
import
'interceptor/network_error_gate.dart'
;
typedef
ApiCall
<
T
>
=
Future
<
BaseResponseModel
<
T
>>
Function
();
typedef
OnSuccess
<
T
>
=
FutureOr
<
void
>
Function
(
T
data
,
BaseResponseModel
<
T
>
res
);
typedef
OnFailure
<
T
>
=
FutureOr
<
void
>
Function
(
String
message
,
BaseResponseModel
<
T
>?
res
,
Object
?
error
);
typedef
OnSuccess
<
T
>
=
FutureOr
<
void
>
Function
(
T
data
,
BaseResponseModel
<
T
>
res
);
typedef
OnFailure
<
T
>
=
FutureOr
<
void
>
Function
(
String
message
,
BaseResponseModel
<
T
>?
res
,
Object
?
error
,
);
typedef
OnComplete
=
FutureOr
<
void
>
Function
();
typedef
ApiTask
=
Future
<
void
>
Function
();
...
...
@@ -39,40 +46,44 @@ class RestfulApiViewModel extends BaseViewModel {
final
msg
=
res
.
errorMessage
??
defaultError
;
final
hasInternet
=
await
NetworkConnectivity
().
hasInternet
();
if
(
showAppNavigatorDialog
)
{
AppNavigator
.
showAlertError
(
content:
hasInternet
?
msg
:
ErrorCodes
.
networkError
);
AppNavigator
.
showAlertError
(
content:
hasInternet
?
msg
:
ErrorCodes
.
networkError
,
);
}
else
{
await
onFailure
?.
call
(
hasInternet
?
msg
:
ErrorCodes
.
networkError
,
res
,
null
);
await
onFailure
?.
call
(
hasInternet
?
msg
:
ErrorCodes
.
networkError
,
res
,
null
,
);
}
}
}
catch
(
e
)
{
if
(
e
is
DioException
&&
e
.
requestOptions
.
extra
[
kExtraSkipApiErrorHandling
]
==
true
)
{
return
;
}
String
msg
=
defaultError
;
if
(
e
is
DioException
)
{
final
mapped
=
e
.
requestOptions
.
extra
[
'mapped_error'
];
msg
=
(
mapped
is
String
&&
mapped
.
isNotEmpty
)
?
mapped
:
ErrorMapper
.
map
(
e
);
msg
=
(
mapped
is
String
&&
mapped
.
isNotEmpty
)
?
mapped
:
ErrorMapper
.
map
(
e
);
}
else
{
msg
=
ErrorMapper
.
map
(
e
);
}
final
hasInternet
=
await
NetworkConnectivity
().
hasInternet
();
if
(
showAppNavigatorDialog
)
{
AppNavigator
.
showAlertError
(
content:
hasInternet
?
msg
:
ErrorCodes
.
networkError
);
AppNavigator
.
showAlertError
(
content:
hasInternet
?
msg
:
ErrorCodes
.
networkError
,
);
}
else
{
await
onFailure
?.
call
(
hasInternet
?
msg
:
ErrorCodes
.
networkError
,
res
,
null
);
}
}
finally
{
if
(
withLoading
)
hideLoading
();
onComplete
?.
call
();
}
await
onFailure
?.
call
(
hasInternet
?
msg
:
ErrorCodes
.
networkError
,
res
,
null
,
);
}
Future
<
void
>
runAllApis
({
required
List
<
ApiTask
>
tasks
,
OnComplete
?
onComplete
,
bool
withLoading
=
true
,
})
async
{
if
(
withLoading
)
showLoading
();
try
{
final
futures
=
tasks
.
map
((
t
)
=>
t
()).
toList
();
await
Future
.
wait
(
futures
);
}
finally
{
if
(
withLoading
)
hideLoading
();
onComplete
?.
call
();
...
...
lib/preference/point/point_manager.dart
View file @
e9cf8244
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'
;
import
'../../networking/restful_api_viewmodel.dart'
;
import
'../data_preference.dart'
;
import
'header_home_model.dart'
;
class
UserPointManager
extends
RestfulApiViewModel
{
static
final
UserPointManager
_instance
=
UserPointManager
.
_internal
();
import
'../../screen/home/header_home_viewmodel.dart'
;
class
UserPointManager
{
UserPointManager
.
_
();
static
final
UserPointManager
_instance
=
UserPointManager
.
_
();
factory
UserPointManager
()
=>
_instance
;
UserPointManager
.
_internal
();
final
RxInt
_userPoint
=
0
.
obs
;
HeaderHomeModel
?
_headerInfo
;
final
HeaderHomeRepository
_repository
=
HeaderHomeRepository
()
;
final
RxInt
_point
=
0
.
obs
;
int
get
point
=>
_userPoint
.
value
;
RxInt
get
pointStream
=>
_point
;
int
get
point
=>
_point
.
value
;
Future
<
int
?>
fetchUserPoint
()
async
{
if
(!
DataPreference
.
instance
.
logged
)
return
null
;
try
{
final
response
=
await
client
.
getHomeHeaderData
();
if
(
response
.
isSuccess
&&
response
.
data
!=
null
)
{
_headerInfo
=
response
.
data
;
_userPoint
.
value
=
_headerInfo
?.
totalPointActive
??
0
;
return
_userPoint
.
value
;
}
else
{
_userPoint
.
value
=
0
;
return
null
;
}
}
catch
(
e
)
{
_userPoint
.
value
=
0
;
return
null
;
void
setPoint
(
int
value
)
{
if
(
point
==
value
)
return
;
_point
.
value
=
value
;
}
Future
<
int
?>
fetchUserPoint
({
bool
withLoading
=
false
})
async
{
await
_repository
.
fetchHeader
(
withLoading:
withLoading
);
return
_point
.
value
;
}
}
lib/screen/affiliate_brand_detail/affiliate_brand_detail_screen.dart
View file @
e9cf8244
...
...
@@ -140,12 +140,11 @@ class _AffiliateBrandDetailScreenState extends BaseState<AffiliateBrandDetailScr
return
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
loadNetworkImage
(
url:
_viewModel
.
brandDetailData
.
value
?.
background
,
Image
.
asset
(
'assets/images/bg_header_detail_brand.png'
,
fit:
BoxFit
.
cover
,
height:
imageHeight
,
width:
double
.
infinity
,
placeholderAsset:
'assets/images/bg_header_detail_brand.png'
,
),
Positioned
(
left:
0
,
...
...
lib/screen/game/game_tab_screen.dart
View file @
e9cf8244
import
'dart:io'
;
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/widgets/image_loader.dart'
;
...
...
@@ -56,6 +58,7 @@ class _GameTabScreenState extends BaseState<GameTabScreen> with BasicState, Popu
title:
"Games"
,
leftButtons:
_canBackButton
?
[
CustomBackButton
()]
:
[],
rightButtons:
[
if
(
Platform
.
isIOS
)
CompositedTransformTarget
(
link:
_layerLink
,
child:
IconButton
(
...
...
lib/screen/health_book/widgets/health_book_item.dart
View file @
e9cf8244
...
...
@@ -122,3 +122,5 @@ class HealthBookItem extends StatelessWidget {
lib/screen/history_point/history_point_screen.dart
View file @
e9cf8244
...
...
@@ -111,7 +111,7 @@ class _HistoryPointScreenState extends State<HistoryPointScreen> {
);
}
_showDatePicker
()
{
void
_showDatePicker
()
{
showMonthPicker
(
context:
context
,
initialDate:
_viewModel
.
selectedDate
,
lastDate:
DateTime
.
now
()).
then
((
date
)
{
if
(
date
==
null
)
return
;
_viewModel
.
selectedDate
=
date
;
...
...
@@ -202,7 +202,7 @@ class _HistoryPointScreenState extends State<HistoryPointScreen> {
final
adjustTotal
=
item
.
adjustTotal
?.
toInt
()
??
0
;
final
value
=
rewardTotal
-
redeemTotal
+
adjustTotal
;
final
valueColor
=
value
>=
0
?
const
Color
(
0xFF21C777
)
:
const
Color
(
0xFFFE515A
);
final
valueText
=
'
${value > 0 ? '+' : (value < 0 ? '-' : '')}${value.money(CurrencyUnit.noneSpace)}
'
;
final
valueText
=
'
${value > 0 ? '+' : (value < 0 ? '-' : '')}${value.
abs().
money(CurrencyUnit.noneSpace)}
'
;
final
dateText
=
item
.
transactionDatetime
?.
toDate
()?.
toFormattedString
(
format:
DateFormat
.
viFull
);
final
transactionId
=
item
.
transactionSequenceId
.
orIfBlank
(
''
);
return
InkWell
(
...
...
lib/screen/home/custom_widget/header_home_widget.dart
View file @
e9cf8244
...
...
@@ -4,6 +4,7 @@ import 'package:mypoint_flutter_app/extensions/num_extension.dart';
import
'package:mypoint_flutter_app/widgets/image_loader.dart'
;
import
'../../../preference/data_preference.dart'
;
import
'../../../preference/point/header_home_model.dart'
;
import
'../../../preference/point/point_manager.dart'
;
import
'../../../shared/router_gage.dart'
;
import
'../models/notification_unread_model.dart'
;
...
...
@@ -107,11 +108,14 @@ class HomeGreetingHeader extends StatelessWidget {
Row
(
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
[
_buildStatItem
(
Obx
(()
{
final
point
=
UserPointManager
().
pointStream
.
value
;
return
_buildStatItem
(
icon:
"assets/images/ic_point_gray.png"
,
value:
(
dataHeader
.
totalPointActive
??
0
)
.
money
(
CurrencyUnit
.
none
),
// .toString(),
value:
point
.
money
(
CurrencyUnit
.
none
),
onTap:
_onPointTap
,
),
);
}),
const
SizedBox
(
width:
8
),
_buildStatItem
(
icon:
"assets/images/ic_voucher_gray.png"
,
...
...
lib/screen/home/header_home_viewmodel.dart
View file @
e9cf8244
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'../../networking/restful_api_viewmodel.dart'
;
import
'../../preference/point/point_manager.dart'
;
import
'../../preference/point/header_home_model.dart'
;
import
'models/notification_unread_model.dart'
;
class
Header
ThemeController
extends
GetxController
{
final
background
=
RxnString
();
void
setBackground
(
String
?
url
)
=>
background
.
value
=
url
;
}
class
Header
HomeRepository
extends
RestfulApiViewModel
{
HeaderHomeRepository
.
_
();
static
final
HeaderHomeRepository
_instance
=
HeaderHomeRepository
.
_
()
;
factory
HeaderHomeRepository
()
=>
_instance
;
class
HeaderHome
View
Model
extends
RestfulApiViewModel
{
final
Rx
<
HeaderHomeModel
?>
_headerHomeData
=
Rx
<
HeaderHomeModel
?>(
null
);
f
in
al
Rxn
<
NotificationUnreadData
>
notificationUnreadData
=
Rxn
<
NotificationUnreadData
>()
;
final
Rx
<
HeaderHomeModel
?>
_headerHome
=
Rx
<
HeaderHomeModel
?>(
null
);
final
Rx
n
<
NotificationUnreadData
>
_notificationUnread
=
Rxn
<
NotificationUnreadData
>(
);
in
t
get
totalPoint
=>
header
.
totalPointActive
??
0
;
HeaderHomeModel
get
headerData
{
return
_headerHomeData
.
value
??
HeaderHomeModel
get
header
=>
_headerHome
.
value
??
HeaderHomeModel
(
greeting:
'Xin chào!'
,
totalVoucher:
0
,
totalPointActive:
0
,
background:
''
,
);
}
Rx
<
HeaderHomeModel
?>
get
headerStream
=>
_headerHome
;
Rxn
<
NotificationUnreadData
>
get
notificationStream
=>
_notificationUnread
;
Future
<
void
>
freshData
()
async
{
await
_getDynamicHeaderHome
();
await
_getNotificationUnread
();
Future
<
void
>
_load
({
bool
withLoading
=
false
})
async
{
await
Future
.
wait
([
fetchHeader
(
withLoading:
withLoading
),
_fetchNotificationUnread
(),
]);
}
Future
<
void
>
_getDynamicHeaderHome
()
async
{
Future
<
void
>
fetchHeader
({
bool
withLoading
=
false
})
async
{
if
(!
DataPreference
.
instance
.
logged
)
return
;
await
callApi
<
HeaderHomeModel
>(
request:
()
=>
client
.
getDynamicHeaderHome
(),
onSuccess:
(
data
,
_
)
{
_headerHome
Data
.
value
=
data
;
Get
.
find
<
HeaderThemeControll
er
>
().
set
Background
(
_headerHomeData
.
value
?.
background
);
_headerHome
.
value
=
data
;
UserPointManag
er
().
set
Point
(
data
.
totalPointActive
??
0
);
},
withLoading:
false
,
withLoading:
withLoading
,
);
}
Future
<
void
>
_getNotificationUnread
()
async
{
Future
<
void
>
_fetchNotificationUnread
()
async
{
if
(!
DataPreference
.
instance
.
logged
)
return
;
await
callApi
<
NotificationUnreadData
>(
request:
()
=>
client
.
getNotificationUnread
(),
onSuccess:
(
data
,
_
)
{
notificationUnread
Data
.
value
=
data
;
_
notificationUnread
.
value
=
data
;
},
withLoading:
false
,
);
}
}
class
HeaderThemeController
extends
GetxController
{
final
background
=
RxnString
();
void
setBackground
(
String
?
url
)
=>
background
.
value
=
url
;
}
class
HeaderHomeViewModel
extends
GetxController
{
final
HeaderHomeRepository
_repository
=
HeaderHomeRepository
();
HeaderHomeModel
get
headerData
=>
_repository
.
header
;
Rxn
<
NotificationUnreadData
>
get
notificationUnreadData
=>
_repository
.
notificationStream
;
@override
void
onInit
()
{
super
.
onInit
();
ever
<
HeaderHomeModel
?>(
_repository
.
headerStream
,
(
data
)
{
if
(
data
?.
background
!=
null
)
{
Get
.
find
<
HeaderThemeController
>().
setBackground
(
data
?.
background
);
}
});
}
Future
<
void
>
freshData
()
=>
_repository
.
_load
(
withLoading:
false
);
}
lib/screen/home/home_screen.dart
View file @
e9cf8244
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/preference/point/point_manager.dart'
;
import
'package:mypoint_flutter_app/screen/home/custom_widget/header_home_widget.dart'
;
import
'package:mypoint_flutter_app/screen/home/custom_widget/product_grid_widget.dart'
;
import
'package:mypoint_flutter_app/screen/pipi/pipi_detail_screen.dart'
;
import
'package:mypoint_flutter_app/screen/voucher/models/product_model.dart'
;
import
'package:mypoint_flutter_app/shared/router_gage.dart'
;
import
'../../directional/directional_action_type.dart'
;
import
'../../widgets/custom_empty_widget.dart'
;
import
'../popup_manager/popup_runner_helper.dart'
;
import
'custom_widget/achievement_carousel_widget.dart'
;
import
'custom_widget/affiliate_brand_grid_widget.dart'
;
...
...
@@ -19,6 +21,7 @@ import 'custom_widget/news_carousel_widget.dart';
import
'header_home_viewmodel.dart'
;
import
'home_tab_viewmodel.dart'
;
import
'models/header_section_type.dart'
;
import
'models/main_section_config_model.dart'
;
class
HomeScreen
extends
StatefulWidget
{
const
HomeScreen
({
super
.
key
});
...
...
@@ -35,6 +38,7 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit {
@override
void
initState
()
{
super
.
initState
();
UserPointManager
().
fetchUserPoint
();
_headerHomeVM
.
freshData
();
runPopupCheck
(
DirectionalScreenName
.
home
);
}
...
...
@@ -53,135 +57,90 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit {
}
List
<
Widget
>
_buildSectionContent
()
{
final
List
<
Widget
>
sections
=
[];
for
(
var
section
in
_viewModel
.
sectionLayouts
.
value
)
{
final
sections
=
_viewModel
.
sectionLayouts
.
map
(
_buildSection
)
.
whereType
<
Widget
>()
.
toList
();
if
(
sections
.
isEmpty
)
{
return
const
[
Padding
(
padding:
EdgeInsets
.
symmetric
(
vertical:
40
),
child:
EmptyWidget
())];
}
return
sections
;
}
Widget
?
_buildSection
(
MainSectionConfigModel
section
)
{
switch
(
section
.
headerSectionType
)
{
case
HeaderSectionType
.
banner
:
if
(
_viewModel
.
banners
.
isNotEmpty
)
{
sections
.
add
(
BannerCarousel
(
if
(
_viewModel
.
banners
.
isEmpty
)
return
null
;
return
BannerCarousel
(
banners:
_viewModel
.
banners
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
banner
),
onTap:
(
item
)
{
item
.
directionalScreen
?.
begin
();
},
),
onTap:
(
item
)
=>
item
.
directionalScreen
?.
begin
(),
);
}
break
;
case
HeaderSectionType
.
topButton
:
if
(
_viewModel
.
services
.
isNotEmpty
)
{
sections
.
add
(
MainServiceGrid
(
if
(
_viewModel
.
services
.
isEmpty
)
return
null
;
return
MainServiceGrid
(
services:
_viewModel
.
services
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
topButton
),
onTap:
(
item
)
{
item
.
directionalScreen
?.
begin
();
},
),
onTap:
(
item
)
=>
item
.
directionalScreen
?.
begin
(),
);
}
break
;
case
HeaderSectionType
.
campaign
:
if
(
_viewModel
.
achievements
.
isNotEmpty
)
{
sections
.
add
(
AchievementCarousel
(
if
(
_viewModel
.
achievements
.
isEmpty
)
return
null
;
return
AchievementCarousel
(
items:
_viewModel
.
achievements
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
campaign
),
onTap:
(
item
)
{
item
.
directionScreen
?.
begin
();
},
),
onTap:
(
item
)
=>
item
.
directionScreen
?.
begin
(),
);
}
break
;
case
HeaderSectionType
.
product
:
if
(
_viewModel
.
products
.
isNotEmpty
)
{
List
<
ProductModel
>
products
=
_viewModel
.
products
;
final
length
=
products
.
length
;
products
=
(
length
.
isOdd
)
?
products
.
sublist
(
0
,
length
-
1
)
:
products
;
sections
.
add
(
ProductGrid
(
products:
products
,
if
(
_viewModel
.
products
.
isEmpty
)
return
null
;
final
displayProducts
=
List
<
ProductModel
>.
from
(
_viewModel
.
products
);
if
(
displayProducts
.
length
.
isOdd
)
{
displayProducts
.
removeLast
();
}
if
(
displayProducts
.
isEmpty
)
return
null
;
return
ProductGrid
(
products:
displayProducts
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
product
),
onTap:
(
product
)
{
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
product
.
id
);
},
),
onTap:
(
product
)
=>
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
product
.
id
),
);
}
break
;
case
HeaderSectionType
.
news
:
if
(
_viewModel
.
news
.
isNotEmpty
)
{
sections
.
add
(
NewsCarouselWidget
(
if
(
_viewModel
.
news
.
isEmpty
)
return
null
;
return
NewsCarouselWidget
(
items:
_viewModel
.
news
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
news
),
onTap:
(
item
)
async
{
Get
.
toNamed
(
campaignDetailScreen
,
arguments:
{
"id"
:
item
.
pageId
});
},
),
onTap:
(
item
)
=>
Get
.
toNamed
(
campaignDetailScreen
,
arguments:
{
"id"
:
item
.
pageId
}),
);
}
break
;
case
HeaderSectionType
.
myProduct
:
if
(
_viewModel
.
myProducts
.
isNotEmpty
)
{
sections
.
add
(
MyProductCarouselWidget
(
if
(
_viewModel
.
myProducts
.
isEmpty
)
return
null
;
return
MyProductCarouselWidget
(
items:
_viewModel
.
myProducts
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
myProduct
),
onTap:
(
item
)
async
{
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
{
"customerProductId"
:
item
.
id
});
},
),
onTap:
(
item
)
=>
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
{
"customerProductId"
:
item
.
id
}),
);
}
break
;
case
HeaderSectionType
.
flashSale
:
final
products
=
_viewModel
.
flashSaleData
.
value
?.
products
??
[];
if
(
products
.
isNotEmpty
)
{
sections
.
add
(
FlashSaleCarouselWidget
(
if
(
products
.
isEmpty
)
return
null
;
return
FlashSaleCarouselWidget
(
products:
products
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
flashSale
),
onTap:
(
product
)
{
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
product
.
id
);
},
),
onTap:
(
product
)
=>
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
product
.
id
),
);
}
break
;
case
HeaderSectionType
.
brand
:
if
(
_viewModel
.
brands
.
isNotEmpty
)
{
sections
.
add
(
BrandGridWidget
(
if
(
_viewModel
.
brands
.
isEmpty
)
return
null
;
return
BrandGridWidget
(
brands:
_viewModel
.
brands
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
brand
),
onTap:
(
item
)
{
// Get.toNamed(affiliateDetailScreen, arguments: item.brandId);
},
),
onTap:
(
_
)
{},
);
}
break
;
case
HeaderSectionType
.
pointPartner
:
if
(
_viewModel
.
affiliates
.
isNotEmpty
)
{
sections
.
add
(
AffiliateBrandGridWidget
(
if
(
_viewModel
.
affiliates
.
isEmpty
)
return
null
;
return
AffiliateBrandGridWidget
(
affiliateBrands:
_viewModel
.
affiliates
,
sectionConfig:
_viewModel
.
getMainSectionConfigModel
(
HeaderSectionType
.
pointPartner
),
onTap:
(
item
)
{
// Get.toNamed(affiliateDetailScreen, arguments: item.brandId);
},
),
onTap:
(
_
)
{},
);
}
break
;
default
:
break
;
}
return
null
;
}
return
sections
;
}
@override
...
...
@@ -193,14 +152,14 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit {
body:
Stack
(
children:
[
NestedScrollView
(
physics:
AlwaysScrollableScrollPhysics
(),
physics:
const
AlwaysScrollableScrollPhysics
(),
headerSliverBuilder:
(
_
,
_
)
=>
[
_buildSliverHeader
(
heightHeader
)],
body:
RefreshIndicator
(
onRefresh:
_onRefresh
,
child:
Obx
(()
{
return
ListView
(
padding:
EdgeInsets
.
only
(
bottom:
paddingBottom
),
physics:
AlwaysScrollableScrollPhysics
(),
physics:
const
AlwaysScrollableScrollPhysics
(),
children:
_buildSectionContent
(),
);
}),
...
...
lib/screen/home/home_tab_viewmodel.dart
View file @
e9cf8244
import
'dart:convert'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/services.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'
;
...
...
@@ -29,9 +30,22 @@ class HomeTabViewModel extends RestfulApiViewModel {
final
Rxn
<
FlashSaleModel
>
flashSaleData
=
Rxn
<
FlashSaleModel
>();
final
Rxn
<
HoverDataModel
>
hoverData
=
Rxn
<
HoverDataModel
>();
late
final
Map
<
HeaderSectionType
,
Future
<
void
>
Function
(
MainSectionConfigModel
)>
_sectionLoaders
;
@override
void
onInit
()
{
super
.
onInit
();
_sectionLoaders
=
{
HeaderSectionType
.
topButton
:
_loadTopButtons
,
HeaderSectionType
.
banner
:
_loadBanners
,
HeaderSectionType
.
campaign
:
_loadCampaigns
,
HeaderSectionType
.
product
:
_loadProducts
,
HeaderSectionType
.
news
:
_loadNews
,
HeaderSectionType
.
flashSale
:
_loadFlashSale
,
HeaderSectionType
.
brand
:
_loadBrands
,
HeaderSectionType
.
pointPartner
:
_loadAffiliateBrands
,
HeaderSectionType
.
myProduct
:
_loadMyProducts
,
};
getSectionLayoutHome
();
loadDataPiPiHome
();
}
...
...
@@ -43,20 +57,9 @@ class HomeTabViewModel extends RestfulApiViewModel {
Future
<
void
>
getSectionLayoutHome
({
bool
showLoading
=
true
})
async
{
await
callApi
<
List
<
MainSectionConfigModel
>>(
request:
()
=>
client
.
getSectionLayoutHome
(),
onSuccess:
(
data
,
_
)
{
sectionLayouts
.
assignAll
(
data
);
},
onFailure:
(
_
,
_
,
_
)
async
{
sectionLayouts
.
assignAll
(
await
_loadSectionLayoutHomeFromCache
());
},
onComplete:
()
async
{
if
(
sectionLayouts
.
isEmpty
)
{
sectionLayouts
.
assignAll
(
await
_loadSectionLayoutHomeFromCache
());
}
for
(
final
section
in
sectionLayouts
)
{
await
_processSection
(
section
);
}
},
onSuccess:
(
data
,
_
)
=>
_resolveSectionLayouts
(
data
),
onFailure:
(
message
,
response
,
error
)
async
=>
_resolveSectionLayouts
(
await
_loadSectionLayoutHomeFromCache
()),
withLoading:
showLoading
,
);
}
...
...
@@ -74,78 +77,97 @@ class HomeTabViewModel extends RestfulApiViewModel {
Future
<
List
<
MainSectionConfigModel
>>
_loadSectionLayoutHomeFromCache
()
async
{
final
jsonStr
=
await
rootBundle
.
loadString
(
'assets/data/main_layout_section_home.json'
);
final
List
<
dynamic
>
jsonList
=
json
.
decode
(
jsonStr
);
return
jsonList
.
map
((
e
)
=>
MainSectionConfigModel
.
fromJson
(
e
)).
toList
()
??
[]
;
return
jsonList
.
map
((
e
)
=>
MainSectionConfigModel
.
fromJson
(
e
)).
toList
();
}
Future
<
void
>
_processSection
(
MainSectionConfigModel
section
)
async
{
final
path
=
section
.
apiList
??
""
;
switch
(
section
.
headerSectionType
)
{
case
HeaderSectionType
.
topButton
:
Future
<
void
>
_resolveSectionLayouts
(
List
<
MainSectionConfigModel
>
layouts
)
async
{
final
resolved
=
layouts
.
isEmpty
?
await
_loadSectionLayoutHomeFromCache
()
:
layouts
;
sectionLayouts
.
assignAll
(
resolved
);
await
_processSections
(
resolved
);
}
Future
<
void
>
_processSections
(
List
<
MainSectionConfigModel
>
sections
)
async
{
final
futures
=
<
Future
<
void
>>[];
for
(
final
section
in
sections
)
{
final
loader
=
_sectionLoaders
[
section
.
headerSectionType
];
if
(
loader
!=
null
)
{
futures
.
add
(
loader
(
section
));
}
else
{
debugPrint
(
'HomeTabViewModel: Unsupported section type
${section.headerSectionType}
'
);
}
}
await
Future
.
wait
(
futures
);
}
Future
<
void
>
_loadTopButtons
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
MainServiceModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
MainServiceModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
services
.
assignAll
(
res
.
data
??
[]);
break
;
case
HeaderSectionType
.
banner
:
}
Future
<
void
>
_loadBanners
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
BannerModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
BannerModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
banners
.
assignAll
(
res
.
data
??
[]);
break
;
case
HeaderSectionType
.
campaign
:
}
Future
<
void
>
_loadCampaigns
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
AchievementModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
AchievementModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
achievements
.
assignAll
(
res
.
data
??
[]);
break
;
case
HeaderSectionType
.
product
:
}
Future
<
void
>
_loadProducts
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
ProductModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
ProductModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
products
.
assignAll
(
res
.
data
??
[]);
break
;
case
HeaderSectionType
.
news
:
}
Future
<
void
>
_loadNews
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
PageItemModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
PageItemModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
news
.
assignAll
(
res
.
data
??
[]);
break
;
case
HeaderSectionType
.
flashSale
:
}
Future
<
void
>
_loadFlashSale
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchObject
<
FlashSaleModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
FlashSaleModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
flashSaleData
.
value
=
res
.
data
;
break
;
case
HeaderSectionType
.
brand
:
}
Future
<
void
>
_loadBrands
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
BrandModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
BrandModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
brands
.
assignAll
(
res
.
data
??
[]);
break
;
case
HeaderSectionType
.
pointPartner
:
}
Future
<
void
>
_loadAffiliateBrands
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
AffiliateBrandModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
AffiliateBrandModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
affiliates
.
assignAll
((
res
.
data
??
[]).
take
(
6
).
toList
());
break
;
case
HeaderSectionType
.
myProduct
:
}
Future
<
void
>
_loadMyProducts
(
MainSectionConfigModel
section
)
async
{
final
res
=
await
client
.
fetchList
<
MyProductModel
>(
path
,
section
.
apiList
??
''
,
(
json
)
=>
MyProductModel
.
fromJson
(
json
as
Map
<
String
,
dynamic
>),
);
myProducts
.
assignAll
(
res
.
data
??
[]);
break
;
default
:
print
(
"Unknown section type:
${section.headerSectionType}
"
);
break
;
}
}
}
lib/screen/pageDetail/campaign_detail_screen.dart
View file @
e9cf8244
...
...
@@ -30,13 +30,12 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
@override
void
initState
()
{
super
.
initState
();
ever
(
_viewModel
.
errorMessage
,
(
valu
e
)
{
if
(
value
!=
null
&&
value
.
toString
().
isNotEmpty
)
{
_viewModel
.
onShowAlertError
=
(
messag
e
)
{
if
(
message
.
isEmpty
)
return
;
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
showAlertError
(
content:
value
);
});
}
showAlertError
(
content:
message
);
});
};
DetailPageRuleType
?
type
;
String
?
pageId
;
final
args
=
Get
.
arguments
;
...
...
@@ -52,7 +51,7 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
return
Scaffold
(
backgroundColor:
BaseColor
.
second200
,
body:
Obx
(()
{
CampaignDetailModel
?
pageDetail
=
_viewModel
.
campaignDetail
.
value
.
data
?.
pageDetail
;
CampaignDetailModel
?
pageDetail
=
_viewModel
.
campaignDetail
.
value
?.
pageDetail
;
if
(
pageDetail
==
null
)
{
return
Stack
(
children:
[
...
...
lib/screen/pageDetail/campaign_detail_viewmodel.dart
View file @
e9cf8244
import
'package:get/get.dart'
;
import
'package:flutter/material.dart'
;
import
'package:mypoint_flutter_app/configs/constants.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'
;
import
'../../base/base_response_model.dart'
;
import
'../../networking/restful_api_viewmodel.dart'
;
import
'model/campaign_detail_model.dart'
;
import
'model/detail_page_rule_type.dart'
;
import
'../faqs/faqs_model.dart'
;
class
CampaignDetailViewModel
extends
RestfulApiViewModel
{
var
campaignDetail
=
BaseResponseModel
<
CampaignDetailResponseModel
>().
obs
;
var
isLoading
=
false
.
obs
;
var
errorMessage
=
""
.
obs
;
var
campaignDetail
=
Rxn
<
CampaignDetailResponseModel
>();
void
Function
(
String
message
)?
onShowAlertError
;
void
fetchData
(
DetailPageRuleType
?
type
,
String
?
pageId
)
{
Future
<
void
>
fetchData
(
DetailPageRuleType
?
type
,
String
?
pageId
)
async
{
if
((
pageId
??
""
).
isNotEmpty
)
{
fetchWebsitePageGetDetail
(
pageId
!);
await
fetchWebsitePageGetDetail
(
pageId
!);
return
;
}
if
(
type
!=
null
)
{
fetchWebsitePage
(
type
!
);
await
fetchWebsitePage
(
type
);
return
;
}
fetchFAQItems
();
await
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
??
""
;
await
callApi
<
FAQItemModelResponse
>(
request:
()
=>
client
.
websiteFolderGetPageList
({
"folder_uri"
:
"ABOUT"
}),
onSuccess:
(
data
,
_
)
async
{
final
pageId
=
(
data
.
items
??
[])
.
firstWhereOrNull
((
item
)
=>
(
item
.
pageId
??
""
).
isNotEmpty
)
?.
pageId
??
""
;
if
(
pageId
.
isEmpty
)
{
errorMessage
.
value
=
Constants
.
commonError
;
}
else
{
fetchWebsitePageGetDetail
(
pageId
);
onShowAlertError
?.
call
(
Constants
.
commonError
);
return
;
}
});
await
fetchWebsitePageGetDetail
(
pageId
);
},
onFailure:
(
msg
,
_
,
_
)
async
{
onShowAlertError
?.
call
(
msg
);
},
);
}
void
fetchWebsitePage
(
DetailPageRuleType
type
)
{
showLoading
();
isLoading
(
true
);
client
.
websitePage
(
type
).
then
((
value
)
{
campaignDetail
.
value
=
value
;
if
(!
value
.
isSuccess
)
{
errorMessage
.
value
=
value
.
errorMessage
??
Constants
.
commonError
;
}
hideLoading
();
isLoading
(
false
);
});
Future
<
void
>
fetchWebsitePage
(
DetailPageRuleType
type
)
async
{
await
callApi
<
CampaignDetailResponseModel
>(
request:
()
=>
client
.
websitePage
(
type
),
onSuccess:
(
data
,
_
)
{
campaignDetail
.
value
=
data
;
},
onFailure:
(
msg
,
_
,
_
)
async
{
onShowAlertError
?.
call
(
msg
);
},
);
}
void
fetchWebsitePageGetDetail
(
String
pageId
)
{
showLoading
();
isLoading
(
true
);
client
.
websitePageGetDetail
(
pageId
).
then
((
value
)
{
campaignDetail
.
value
=
value
;
if
(!
value
.
isSuccess
)
{
errorMessage
.
value
=
value
.
errorMessage
??
Constants
.
commonError
;
}
hideLoading
();
isLoading
(
false
);
});
Future
<
void
>
fetchWebsitePageGetDetail
(
String
pageId
)
async
{
await
callApi
<
CampaignDetailResponseModel
>(
request:
()
=>
client
.
websitePageGetDetail
(
pageId
),
onSuccess:
(
data
,
response
)
{
campaignDetail
.
value
=
data
;
},
onFailure:
(
msg
,
response
,
error
)
async
{
onShowAlertError
?.
call
(
msg
);
},
);
}
Future
<
void
>
submitShareContent
()
async
{
final
path
=
campaignDetail
.
value
.
data
?.
pageDetail
?.
clickButtonShare
??
""
;
final
path
=
campaignDetail
.
value
?.
pageDetail
?.
clickButtonShare
??
""
;
if
(
path
.
isEmpty
)
return
;
await
client
.
submitShareContent
(
path
);
}
...
...
lib/screen/personal/personal_screen.dart
View file @
e9cf8244
...
...
@@ -4,6 +4,7 @@ import 'package:get/get.dart';
import
'package:mypoint_flutter_app/directional/directional_screen.dart'
;
import
'package:mypoint_flutter_app/extensions/num_extension.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'package:mypoint_flutter_app/preference/point/point_manager.dart'
;
import
'../../base/base_screen.dart'
;
import
'../../base/basic_state.dart'
;
import
'../../directional/directional_action_type.dart'
;
...
...
@@ -26,24 +27,17 @@ class PersonalScreen extends BaseScreen {
class
_PersonalScreenState
extends
BaseState
<
PersonalScreen
>
with
BasicState
,
PopupOnInit
{
final
_headerHomeVM
=
Get
.
find
<
HeaderHomeViewModel
>();
String
?
_version
,
_buildNumber
;
final
_pointManager
=
UserPointManager
();
late
final
Future
<
List
<
String
?>>
_appInfoFuture
;
@override
void
initState
()
{
super
.
initState
();
_loadAppInfo
();
_appInfoFuture
=
Future
.
wait
([
AppInfoHelper
.
version
,
AppInfoHelper
.
buildNumber
]);
_pointManager
.
fetchUserPoint
();
runPopupCheck
(
DirectionalScreenName
.
personal
);
}
Future
<
void
>
_loadAppInfo
()
async
{
final
v
=
await
AppInfoHelper
.
version
;
final
b
=
await
AppInfoHelper
.
buildNumber
;
setState
(()
{
_version
=
v
??
""
;
_buildNumber
=
b
??
""
;
});
}
@override
Widget
createBody
()
{
return
Scaffold
(
...
...
@@ -56,7 +50,17 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
children:
[
_buildInvitationSection
(),
_buildMenuItems
(),
_buildVersionInfo
(
_version
,
_buildNumber
),
FutureBuilder
<
List
<
String
?>>
(
future:
_appInfoFuture
,
builder:
(
context
,
snapshot
)
{
if
(!
snapshot
.
hasData
)
{
return
const
SizedBox
.
shrink
();
}
final
version
=
snapshot
.
data
?[
0
]
??
''
;
final
buildNumber
=
snapshot
.
data
?[
1
]
??
''
;
return
_buildVersionInfo
(
version
,
buildNumber
);
},
),
Container
(
color:
Colors
.
grey
[
200
],
height:
MediaQuery
.
of
(
context
).
padding
.
bottom
+
16
),
],
),
...
...
@@ -145,10 +149,12 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
},
child:
Row
(
children:
[
Text
(
(
data
.
totalPointActive
??
0
).
money
(
CurrencyUnit
.
point
),
Obx
(()
{
return
Text
(
(
_pointManager
.
pointStream
.
value
).
money
(
CurrencyUnit
.
point
),
style:
const
TextStyle
(
color:
Colors
.
white
,
fontSize:
16
,
fontWeight:
FontWeight
.
bold
),
),
);
}),
const
SizedBox
(
width:
4
),
const
Icon
(
Icons
.
chevron_right
,
color:
Colors
.
white
,
size:
22
),
],
...
...
@@ -346,7 +352,6 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
return
;
}
print
(
"Safe back to login screen with phone:
$phone
, displayName:
$displayName
"
);
if
(
phone
.
isNotEmpty
)
{
await
DataPreference
.
instance
.
clearLoginToken
();
Get
.
offAllNamed
(
loginScreen
,
arguments:
{
"phone"
:
phone
,
'fullName'
:
displayName
});
...
...
lib/screen/splash/models/check_update_response_model.dart
View file @
e9cf8244
...
...
@@ -18,6 +18,8 @@ class CheckUpdateResponseModel {
switch
(
updateMode
?.
toUpperCase
()
??
""
)
{
case
'NOW'
:
return
UpdateStatus
.
force
;
case
'NONE'
:
return
UpdateStatus
.
none
;
default
:
return
UpdateStatus
.
suggest
;
}
...
...
@@ -34,4 +36,3 @@ class CheckUpdateResponseModel {
Map
<
String
,
dynamic
>
toJson
()
=>
_$CheckUpdateResponseModelToJson
(
this
);
}
lib/screen/splash/splash_screen.dart
View file @
e9cf8244
...
...
@@ -2,6 +2,7 @@ import 'dart:io';
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'package:mypoint_flutter_app/screen/splash/splash_screen_viewmodel.dart'
;
import
'package:mypoint_flutter_app/shared/router_gage.dart'
;
import
'package:mypoint_flutter_app/widgets/alert/custom_alert_dialog.dart'
;
...
...
@@ -33,7 +34,7 @@ class _SplashScreenState extends BaseState<SplashScreen> with BasicState {
}
var
status
=
updateData
?.
status
??
UpdateStatus
.
none
;
if
(
status
==
UpdateStatus
.
none
)
{
Get
.
offAllNamed
(
onboardingScreen
);
_viewModel
.
directionWhenTokenInvalid
(
);
}
else
{
_showSuggestUpdateAlert
(
updateData
);
}
...
...
lib/screen/splash/splash_screen_viewmodel.dart
View file @
e9cf8244
...
...
@@ -50,7 +50,7 @@ class SplashScreenViewModel extends RestfulApiViewModel {
final
token
=
await
_getTokenFromSDK
();
if
(
token
.
orEmpty
.
isEmpty
)
{
_
directionWhenTokenInvalid
();
directionWhenTokenInvalid
();
return
;
}
...
...
@@ -59,7 +59,7 @@ class SplashScreenViewModel extends RestfulApiViewModel {
}
on
Object
catch
(
e
)
{
debugPrint
(
'❌ SplashScreen - makeDataFollowInitApp error:
$e
'
);
DataPreference
.
instance
.
clearLoginToken
();
_
directionWhenTokenInvalid
();
directionWhenTokenInvalid
();
}
}
...
...
@@ -96,25 +96,30 @@ class SplashScreenViewModel extends RestfulApiViewModel {
final
profile
=
await
_fetchUserProfile
();
if
(
profile
==
null
)
{
DataPreference
.
instance
.
clearLoginToken
();
_
directionWhenTokenInvalid
();
directionWhenTokenInvalid
();
return
;
}
await
_prepareInitialData
(
profile
);
_goToMain
();
}
void
_
directionWhenTokenInvalid
()
{
Future
<
void
>
directionWhenTokenInvalid
()
async
{
if
(
Get
.
currentRoute
==
onboardingScreen
)
return
;
if
(
kIsWeb
)
{
print
(
'❌ No token found on web, closing app'
);
final
closeSuccess
=
await
webCloseApp
({
'message'
:
'No token found, cannot proceed'
,
'timestamp'
:
DateTime
.
now
().
millisecondsSinceEpoch
,
});
if
(
closeSuccess
)
return
;
}
final
phone
=
DataPreference
.
instance
.
phoneNumberUsedForLoginScreen
;
final
displayName
=
DataPreference
.
instance
.
displayName
;
if
(
phone
.
isEmpty
)
{
Get
.
offAllNamed
(
onboardingScreen
);
// if (kIsWeb) {
// print('❌ No token found on web, closing app');
// webCloseApp({
// 'message': 'No token found, cannot proceed',
// 'timestamp': DateTime.now().millisecondsSinceEpoch,
// });
// } else {
// Get.toNamed(onboardingScreen);
// }
}
else
{
Get
.
offAllNamed
(
loginScreen
,
arguments:
{
"phone"
:
phone
,
'fullName'
:
displayName
});
}
}
Future
<
ProfileResponseModel
?>
_fetchUserProfile
()
async
{
...
...
lib/screen/voucher/detail/voucher_detail_screen.dart
View file @
e9cf8244
...
...
@@ -39,7 +39,7 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
@override
void
initState
()
{
super
.
initState
();
UserPointManager
().
fetchUserPoint
();
int
?
productId
;
int
?
customerProductId
;
...
...
@@ -482,9 +482,12 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
});
}
_handleRedeemProduct
()
{
Future
<
void
>
_handleRedeemProduct
()
async
{
final
point
=
await
UserPointManager
().
fetchUserPoint
(
withLoading:
true
)
??
0
;
final
amountToBePaid
=
_viewModel
.
product
.
value
?.
amountToBePaid
??
0
;
if
(
UserPointManager
().
point
<
amountToBePaid
)
{
print
(
'amountToBePaid:
$amountToBePaid
'
);
print
(
'UserPointManager().point:
$point
'
);
if
(
point
<
amountToBePaid
)
{
showAlertError
(
content:
"Bạn không đủ điểm để đổi ưu đãi này"
);
return
;
}
...
...
lib/screen/voucher/detail/voucher_detail_viewmodel.dart
View file @
e9cf8244
...
...
@@ -99,7 +99,7 @@ class VoucherDetailViewModel extends RestfulApiViewModel {
});
}
redeemProduct
()
{
void
redeemProduct
()
{
showLoading
();
final
requestId
=
Uuid
().
v4
();
client
...
...
lib/screen/voucher/sub_widget/voucher_item_list.dart
View file @
e9cf8244
...
...
@@ -63,7 +63,7 @@ class VoucherListItem extends StatelessWidget {
child:
loadNetworkImage
(
url:
bgImage
,
fit:
BoxFit
.
cover
,
placeholderAsset:
'assets/images/
sample
.png'
,
placeholderAsset:
'assets/images/
bg_default_169
.png'
,
),
),
if
(!
product
.
inStock
)
...
...
Prev
1
2
3
4
5
6
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