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
682ab1de
Commit
682ab1de
authored
Dec 31, 2025
by
DatHV
Browse files
fix bug.
parent
1edd930e
Changes
36
Hide whitespace changes
Inline
Side-by-side
lib/features/onboarding/onboarding_screen.dart
View file @
682ab1de
...
...
@@ -166,7 +166,7 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
const
TextSpan
(
text:
" và "
),
WidgetSpan
(
child:
GestureDetector
(
onTap:
()
=>
Get
.
to
(
FAQScreen
()),
// Get.to(C
ampaignDetailScreen
(
type: DetailPageRuleType.privacyPolicy
)
),
onTap:
()
=>
Get
.
to
Named
(
c
ampaignDetailScreen
,
arguments:
{
"
type
"
:
DetailPageRuleType
.
privacyPolicy
}
),
child:
const
Text
(
"Chính sách bảo mật"
,
style:
TextStyle
(
...
...
lib/features/otp/delete_account_otp_repository.dart
View file @
682ab1de
...
...
@@ -6,6 +6,7 @@ import '../../core/network/restful_api_viewmodel.dart';
import
'../../app/config/constants.dart'
;
import
'../../shared/preferences/data_preference.dart'
;
import
'../../shared/widgets/custom_toast_message.dart'
;
import
'model/otp_verify_response_model.dart'
;
import
'otp_viewmodel.dart'
;
class
DeleteAccountOtpRepository
extends
RestfulApiViewModel
implements
IOtpRepository
{
...
...
@@ -20,10 +21,10 @@ class DeleteAccountOtpRepository extends RestfulApiViewModel implements IOtpRepo
Future
<
void
>
sendOtp
()
async
{}
@override
Future
<
BaseResponseModel
<
EmptyCodable
>>
verifyOtp
(
String
otpCode
)
async
{
Future
<
BaseResponseModel
<
OTPVerifyResponseModel
>>
verifyOtp
(
String
otpCode
)
{
showLoading
();
try
{
final
value
=
await
client
.
verifyDeleteAccount
(
otpCode
);
return
client
.
verifyDeleteAccount
(
otpCode
).
then
((
value
)
async
{
hideLoading
(
);
if
(
value
.
isSuccess
)
{
await
DataPreference
.
instance
.
clearBioToken
(
phoneNumber
);
await
DataPreference
.
instance
.
clearData
();
...
...
@@ -31,9 +32,7 @@ class DeleteAccountOtpRepository extends RestfulApiViewModel implements IOtpRepo
showToastMessage
(
"Xóa tài khoản thành công"
);
}
return
value
;
}
finally
{
hideLoading
();
}
});
}
@override
...
...
@@ -46,7 +45,7 @@ class DeleteAccountOtpRepository extends RestfulApiViewModel implements IOtpRepo
return
otpTtl
;
}
final
mgs
=
value
.
errorMessage
??
Constants
.
commonError
;
Get
.
snackbar
(
"Thông báo"
,
mgs
);
showToastMessage
(
mgs
);
return
null
;
}
finally
{
hideLoading
();
...
...
lib/features/otp/forgot_pass_otp_repository.dart
View file @
682ab1de
...
...
@@ -17,7 +17,11 @@ class ForgotPassOTPRepository extends RestfulApiViewModel implements IOtpReposit
@override
Future
<
int
?>
resendOtp
()
{
throw
UnimplementedError
();
showLoading
();
return
client
.
otpCreateNew
(
phoneNumber
).
then
((
value
)
{
hideLoading
();
return
value
.
data
?.
resendAfterSecond
;
});
}
@override
...
...
lib/features/personal/personal_edit_item_model.dart
View file @
682ab1de
...
...
@@ -46,16 +46,16 @@ class PersonalEditDataModel {
});
Json
get
body
=>
<
String
,
dynamic
>
{
"worker_site_id"
:
DataPreference
.
instance
.
profile
?.
workerSite
?.
id
,
"fullname"
:
name
,
"nickname"
:
nickname
,
"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_full"
:
address
??
""
,
"address_district_code"
:
district
?.
code
??
""
,
"address_province_code"
:
province
?.
code
??
""
,
"identification_number"
:
identificationNumber
,
"email"
:
email
,
"identification_number"
:
identificationNumber
??
""
,
"email"
:
email
??
""
,
"avatar"
:
""
,
"avatar_2"
:
""
,
};
...
...
lib/features/personal/personal_edit_screen.dart
View file @
682ab1de
...
...
@@ -28,20 +28,10 @@ class _PersonalEditScreenState extends BaseState<PersonalEditScreen> with BasicS
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
);
showAlertError
(
content:
"Cập nhật thông tin cá nhân thành công!"
,
headerImage:
"assets/images/ic_pipi_05.png"
,
onConfirmed:
()
=>
Get
.
back
());
};
}
...
...
@@ -191,6 +181,7 @@ class _PersonalEditScreenState extends BaseState<PersonalEditScreen> with BasicS
item
.
sectionType
==
SectionPersonalEditType
.
gender
;
final
isDate
=
item
.
sectionType
==
SectionPersonalEditType
.
birthday
;
final
isTappableItem
=
isTapField
||
isDate
;
final
value
=
item
.
value
??
""
;
return
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
8
,
bottom:
8
,
left:
16
,
right:
16
),
// all(16.0),
child:
Column
(
...
...
@@ -223,7 +214,7 @@ class _PersonalEditScreenState extends BaseState<PersonalEditScreen> with BasicS
Expanded
(
child:
TextField
(
keyboardType:
item
.
keyboardType
??
TextInputType
.
text
,
controller:
TextEditingController
(
text:
item
.
value
??
""
),
controller:
TextEditingController
(
text:
value
),
enabled:
isTappableItem
?
false
:
(
item
.
isEditable
??
true
),
decoration:
InputDecoration
.
collapsed
(
hintText:
item
.
hintText
??
""
,
...
...
@@ -242,6 +233,14 @@ class _PersonalEditScreenState extends BaseState<PersonalEditScreen> with BasicS
),
),
const
SizedBox
(
height:
6
),
if
(
item
.
sectionType
==
SectionPersonalEditType
.
email
&&
value
.
isNotEmpty
&&
viewModel
.
isValidEmail
(
value
)
==
false
)
Row
(
children:
[
const
Icon
(
Icons
.
error
,
color:
Colors
.
red
,
size:
14
),
const
SizedBox
(
width:
4
),
Text
(
'Email không đúng định dạng'
,
style:
const
TextStyle
(
fontSize:
12
,
color:
Colors
.
red
)),
],
),
if
(
item
.
warningText
!=
null
)
Row
(
children:
[
...
...
lib/features/personal/personal_edit_viewmodel.dart
View file @
682ab1de
...
...
@@ -157,7 +157,7 @@ class PersonalEditViewModel extends RestfulApiViewModel {
}
if
(
model
.
birthday
==
null
)
{
return
false
;
}
}
if
(
model
.
gender
==
null
||
model
.
gender
==
PersonalGender
.
unknown
)
{
return
false
;
}
...
...
lib/features/personal/personal_screen.dart
View file @
682ab1de
...
...
@@ -74,19 +74,18 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
}
Widget
_buildHeaderPersonal
(
HeaderHomeModel
data
)
{
final
width
=
MediaQuery
.
of
(
context
).
size
.
width
;
final
topPadding
=
MediaQuery
.
of
(
context
).
padding
.
top
;
final
name
=
DataPreference
.
instance
.
displayName
;
final
level
=
DataPreference
.
instance
.
rankName
??
"Hạng Đồng"
;
final
email
=
DataPreference
.
instance
.
profile
?.
workerSite
?.
email
??
""
;
final
topWebPadding
=
Constants
.
extendTopPaddingNavigation
;
final
avatar
=
WebData
.
getAvatar
();
return
Container
(
height:
width
*
163
/
375
+
topWebPadding
,
decoration:
BoxDecoration
(
image:
DecorationImage
(
image:
NetworkImage
(
data
.
background
??
""
),
fit:
BoxFit
.
cover
)),
child:
Padding
(
padding:
EdgeInsets
.
only
(
top:
12
+
topWebPadding
,
bottom:
12
,
left:
8
,
right:
8
),
// symmetric(horizontal: 12, vertical: 8),
child:
SafeArea
(
bottom:
false
,
minimum:
EdgeInsets
.
only
(
top:
12
+
topWebPadding
,
bottom:
12
,
left:
12
,
right:
12
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
GestureDetector
(
...
...
@@ -94,47 +93,51 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
await
Get
.
toNamed
(
personalEditScreen
);
setState
(()
{});
},
child:
Container
(
margin:
EdgeInsets
.
only
(
top:
topPadding
),
child:
Row
(
children:
[
Container
(
width:
64
,
height:
64
,
decoration:
BoxDecoration
(
shape:
BoxShape
.
circle
,
border:
Border
.
all
(
color:
Colors
.
white
,
width:
2
),
),
child:
ClipOval
(
child:
loadNetworkImage
(
url:
avatar
,
fit:
BoxFit
.
cover
,
placeholderAsset:
"assets/images/ic_logo.png"
)
),
child:
Row
(
children:
[
Container
(
width:
64
,
height:
64
,
decoration:
BoxDecoration
(
shape:
BoxShape
.
circle
,
border:
Border
.
all
(
color:
Colors
.
white
,
width:
2
),
),
child:
ClipOval
(
child:
loadNetworkImage
(
url:
avatar
,
fit:
BoxFit
.
cover
,
placeholderAsset:
"assets/images/ic_logo.png"
)
),
const
SizedBox
(
width:
8
),
Column
(
),
const
SizedBox
(
width:
8
),
Expanded
(
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
name
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
const
TextStyle
(
fontSize:
22
,
fontWeight:
FontWeight
.
bold
,
color:
Colors
.
white
),
),
if
(
email
.
isNotEmpty
)
Text
(
email
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
const
TextStyle
(
fontSize:
16
,
color:
Colors
.
white70
,
fontWeight:
FontWeight
.
w600
),
),
],
),
const
Spacer
(
),
const
Icon
(
Icons
.
chevron_right
,
color:
Colors
.
white
,
size:
22
),
]
,
)
,
),
const
SizedBox
(
width:
8
),
const
Icon
(
Icons
.
chevron_right
,
color:
Colors
.
white
,
size:
22
)
,
]
,
),
),
const
S
pacer
(
),
const
S
izedBox
(
height:
16
),
Row
(
children:
[
GestureDetector
(
...
...
lib/features/splash/splash_screen_viewmodel.dart
View file @
682ab1de
...
...
@@ -85,7 +85,7 @@ class SplashScreenViewModel extends RestfulApiViewModel {
// Get token from SDK
final
token
=
await
webGetToken
().
timeout
(
_sdkTimeout
);
if
(
token
!=
null
&&
token
.
isNotEmpty
)
{
debugPrint
(
'✅ SplashScreen - Token retrieved from x-app-sdk:
$
{
token.
substring(0, 8)}
.
..'
);
debugPrint
(
'✅ SplashScreen - Token retrieved from x-app-sdk:
$token
...'
);
return
token
;
}
else
{
final
error
=
webGetLastError
();
...
...
lib/features/topup/topup_screen.dart
View file @
682ab1de
...
...
@@ -89,7 +89,7 @@ class _PhoneTopUpScreenState extends BaseState<PhoneTopUpScreen> with BasicState
),
keyboardType:
TextInputType
.
phone
,
onChanged:
(
value
)
{
_viewModel
.
phoneNumber
.
value
=
value
;
_viewModel
.
phoneNumber
.
value
=
value
.
trim
()
;
_deb
.
run
(()
=>
_viewModel
.
checkMobileNetwork
());
},
),
...
...
@@ -110,9 +110,11 @@ class _PhoneTopUpScreenState extends BaseState<PhoneTopUpScreen> with BasicState
selectedBrand:
_viewModel
.
selectedBrand
.
value
,
onSelected:
(
brand
)
{
Navigator
.
pop
(
context
);
print
(
"BrandSelectShe 2222 2 et
${brand.name}
"
);
if
(
brand
.
id
!=
_viewModel
.
selectedBrand
.
value
?.
id
)
return
;
_viewModel
.
selectedProduct
.
value
=
null
;
_viewModel
.
selectedBrand
.
value
=
brand
;
print
(
"BrandSelectSheet
${brand.name}
"
);
_viewModel
.
getTelcoDetail
();
},
),
...
...
@@ -163,7 +165,7 @@ class _PhoneTopUpScreenState extends BaseState<PhoneTopUpScreen> with BasicState
return
GestureDetector
(
onTap:
()
{
setState
(()
{
_viewModel
.
phoneNumber
.
value
=
phone
;
_viewModel
.
phoneNumber
.
value
=
phone
.
trim
()
;
_phoneController
.
text
=
phone
;
_viewModel
.
checkMobileNetwork
();
});
...
...
@@ -365,7 +367,7 @@ class _PhoneTopUpScreenState extends BaseState<PhoneTopUpScreen> with BasicState
String
phone
=
contact
.
phones
.
first
.
number
;
phone
=
phone
.
replaceAll
(
RegExp
(
r'[\s\-\(\)]'
),
''
);
_phoneController
.
text
=
phone
;
_viewModel
.
phoneNumber
.
value
=
phone
;
_viewModel
.
phoneNumber
.
value
=
phone
.
trim
()
;
_viewModel
.
checkMobileNetwork
();
}
catch
(
e
)
{
debugPrint
(
'❌ pickContact error:
$e
'
);
...
...
lib/features/topup/topup_viewmodel.dart
View file @
682ab1de
...
...
@@ -66,13 +66,15 @@ class TopUpViewModel extends RestfulApiViewModel {
}
Future
<
void
>
checkMobileNetwork
()
async
{
final
phone
=
phoneNumber
.
value
.
trim
();
if
(
phone
.
isEmpty
)
return
;
await
callApi
<
BrandNameCheckResponse
>(
request:
()
=>
_callProductApi
((
api
)
=>
api
.
checkMobileNetwork
(
phone
Number
.
value
)),
request:
()
=>
_callProductApi
((
api
)
=>
api
.
checkMobileNetwork
(
phone
)),
onSuccess:
(
data
,
_
)
{
final
brandCode
=
data
.
brand
??
''
;
final
brandCode
=
(
data
.
brand
??
''
).
toUpperCase
()
;
var
brand
=
topUpBrands
.
isNotEmpty
?
topUpBrands
.
firstWhere
(
(
brand
)
=>
brand
.
code
==
brandCode
,
(
brand
)
=>
(
brand
.
code
??
""
).
toUpperCase
()
==
brandCode
,
orElse:
()
=>
topUpBrands
.
first
,
)
:
topUpBrands
.
firstOrNull
;
selectedBrand
.
value
=
brand
;
...
...
lib/features/transaction/history/transaction_history_detail_screen.dart
View file @
682ab1de
...
...
@@ -25,6 +25,9 @@ class TransactionHistoryDetailScreen extends BaseScreen {
class
_TransactionHistoryDetailScreenState
extends
BaseState
<
TransactionHistoryDetailScreen
>
with
BasicState
{
late
final
TransactionHistoryDetailViewModel
_viewModel
;
late
var
canBack
=
true
;
void
_goHome
()
{
Get
.
offAllNamed
(
mainScreen
,
arguments:
{
"tabIndex"
:
0
});
}
@override
void
initState
()
{
...
...
@@ -252,7 +255,8 @@ class _TransactionHistoryDetailScreenState extends BaseState<TransactionHistoryD
onPressed:
()
{
final
finish
=
transaction
.
directionScreenRedButton
?.
begin
();
if
(
finish
!=
true
)
{
Get
.
until
((
route
)
=>
Get
.
currentRoute
==
mainScreen
);
print
(
"finish directionScreenRedButton"
);
_goHome
();
}
},
style:
ElevatedButton
.
styleFrom
(
...
...
@@ -270,7 +274,7 @@ class _TransactionHistoryDetailScreenState extends BaseState<TransactionHistoryD
if
(
transaction
.
titleClearButton
!=
null
)
TextButton
(
onPressed:
()
{
Get
.
until
((
route
)
=>
Get
.
currentRoute
==
mainScreen
);
_goHome
(
);
},
style:
TextButton
.
styleFrom
(
minimumSize:
const
Size
(
double
.
infinity
,
50
),
...
...
lib/features/voucher/detail/voucher_detail_screen.dart
View file @
682ab1de
import
'dart:math'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'
;
import
'package:mypoint_flutter_app/core/utils/extensions/num_extension.dart'
;
...
...
@@ -35,6 +36,7 @@ class VoucherDetailScreen extends BaseScreen {
class
_VoucherDetailScreenState
extends
BaseState
<
VoucherDetailScreen
>
with
BasicState
{
late
final
VoucherDetailViewModel
_viewModel
;
late
final
String
_viewModelTag
;
double
_infoHeight
=
0
;
@override
...
...
@@ -59,13 +61,23 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
});
return
;
}
_viewModel
=
Get
.
put
(
VoucherDetailViewModel
(
productId:
productId
,
customerProductId:
customerProductId
));
_viewModelTag
=
'voucher_detail_
${DateTime.now().microsecondsSinceEpoch}
'
;
_viewModel
=
Get
.
put
(
VoucherDetailViewModel
(
productId:
productId
,
customerProductId:
customerProductId
),
tag:
_viewModelTag
,
);
_viewModel
.
onShowAlertError
=
(
message
)
{
if
(
message
.
isEmpty
)
return
;
showAlertError
(
content:
message
);
};
}
@override
void
dispose
()
{
Get
.
delete
<
VoucherDetailViewModel
>(
tag:
_viewModelTag
);
super
.
dispose
();
}
@override
Widget
createBody
()
{
return
Scaffold
(
...
...
@@ -108,7 +120,12 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
CustomBackButton
(),
_buildFavoriteButton
()],
children:
[
CustomBackButton
(),
_buildFavoriteButton
(),
if
(
kIsWeb
)
Expanded
(
child:
SizedBox
.
shrink
())
],
),
),
),
...
...
lib/features/voucher/sub_widget/voucher_action_menu.dart
View file @
682ab1de
...
...
@@ -11,7 +11,7 @@ class VoucherActionMenu extends StatelessWidget {
padding:
const
EdgeInsets
.
symmetric
(
vertical:
12
),
child:
Row
(
children:
const
[
_ActionItem
(
icon:
"assets/images/ic_topup.png"
,
label:
'Nạp tiền
\n
d
iện thoại'
,
type:
DirectionalScreenName
.
topup
,),
_ActionItem
(
icon:
"assets/images/ic_topup.png"
,
label:
'Nạp tiền
\n
đ
iện thoại'
,
type:
DirectionalScreenName
.
topup
,),
_ActionItem
(
icon:
"assets/images/ic_card_code.png"
,
label:
'Đổi mã
\n
thẻ nạp'
,
type:
DirectionalScreenName
.
productMobileCard
,),
_ActionItem
(
icon:
"assets/images/ic_sim_service.png"
,
label:
'Gói cước
\n
nhà mạng'
,
type:
DirectionalScreenName
.
simService
,),
_ActionItem
(
icon:
"assets/images/ic_topup_data.png"
,
label:
'Ưu đãi
\n
Data'
,
type:
DirectionalScreenName
.
mobileTopupData
,),
...
...
lib/features/voucher/voucher_list/voucher_list_viewmodel.dart
View file @
682ab1de
...
...
@@ -52,8 +52,9 @@ class VoucherListViewModel extends RestfulApiViewModel {
}
void
onSearchChanged
(
String
value
)
{
if
(
_searchQuery
==
value
)
return
;
_searchQuery
=
value
;
final
value_
=
value
.
trim
();
if
(
_searchQuery
==
value_
)
return
;
_searchQuery
=
value_
;
_debounce
?.
cancel
();
_debounce
=
Timer
(
const
Duration
(
seconds:
1
),
()
{
loadData
(
reset:
true
);
...
...
lib/shared/widgets/alert/custom_alert_dialog.dart
View file @
682ab1de
...
...
@@ -33,30 +33,33 @@ class CustomAlertDialog extends StatelessWidget {
children:
[
_buildHeaderImage
(),
Padding
(
padding:
const
EdgeInsets
.
all
(
16
),
padding:
const
EdgeInsets
.
all
(
8
),
child:
Column
(
children:
[
if
((
alertData
.
title
??
""
).
isNotEmpty
)
if
((
alertData
.
title
??
""
).
isNotEmpty
)
...
[
Text
(
alertData
.
title
!,
style:
const
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
bold
),
textAlign:
TextAlign
.
center
,
),
const
SizedBox
(
height:
8
),
if
(
alertData
.
description
!=
null
)
const
SizedBox
(
height:
4
)
],
if
(
alertData
.
description
!=
null
)...
[
HtmlWidget
(
'''
<div style="text-align: center;">
${alertData.description!}
</div>
'''
),
const
SizedBox
(
height:
4
),
if
((
alertData
.
content
??
""
).
isNotEmpty
)
const
SizedBox
(
height:
4
),
],
if
((
alertData
.
content
??
""
).
isNotEmpty
)...
[
Text
(
alertData
.
content
!,
style:
TextStyle
(
fontSize:
14
,
fontWeight:
FontWeight
.
w600
,
color:
BaseColor
.
primary500
),
textAlign:
TextAlign
.
center
,
),
const
SizedBox
(
height:
8
),
const
SizedBox
(
height:
4
),
],
_buildButtons
(),
],
),
...
...
pubspec.yaml
View file @
682ab1de
...
...
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version
:
1.21.1
1
+20251
024
01
version
:
1.21.1
3
+20251
231
01
environment
:
sdk
:
^3.7.0
...
...
Prev
1
2
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