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
fc2caf86
Commit
fc2caf86
authored
Oct 09, 2025
by
DatHV
Browse files
update ui, logic.
parent
0b973e61
Changes
35
Hide whitespace changes
Inline
Side-by-side
lib/configs/api_paths.dart
View file @
fc2caf86
...
...
@@ -88,6 +88,8 @@ class APIPaths {//sandbox
static
const
String
customerContractDelete
=
"/customerContractDelete/1.0.0"
;
static
const
String
getProductVnTraSold
=
"/product/api/v2.0/vntra_sold"
;
static
const
String
detailMyPackageVnTra
=
"/product/api/v2.0/vntra_sold/%@"
;
static
const
String
getHealthBookCards
=
"/product/api/v2.0/cards_sold"
;
static
const
String
detailHealthBookCardDetail
=
"/product/api/v2.0/cards_sold/%@"
;
static
const
String
getCampaignLiveTransactions
=
"/campaign/api/v3.0/%@/live-transactions"
;
static
const
String
getCampaignMissions
=
"/campaign/api/v3.0/%@/missions"
;
static
const
String
getCampaignReward
=
"/campaign/api/v3.0/%@/reward"
;
...
...
lib/directional/directional_screen.dart
View file @
fc2caf86
import
'package:flutter/cupertino.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/extensions/string_extension.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'package:mypoint_flutter_app/widgets/alert/popup_data_model.dart'
;
import
'package:url_launcher/url_launcher.dart'
;
import
'package:uuid/uuid.dart'
;
import
'../base/app_loading.dart'
;
import
'../networking/app_navigator.dart'
;
import
'../networking/restful_api_viewmodel.dart'
;
import
'../screen/webview/web_view_screen.dart'
;
import
'../shared/router_gage.dart'
;
import
'directional_action_type.dart'
;
...
...
@@ -16,12 +21,14 @@ class Defines {
class
DirectionalScreen
{
final
String
?
clickActionType
;
final
String
?
clickActionParam
;
final
PopupDataModel
?
popup
;
const
DirectionalScreen
.
_
({
this
.
clickActionType
,
this
.
clickActionParam
});
const
DirectionalScreen
.
_
({
this
.
clickActionType
,
this
.
clickActionParam
,
this
.
popup
});
factory
DirectionalScreen
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
DirectionalScreen
.
_
(
clickActionType:
json
[
'click_action_type'
]
as
String
?,
clickActionParam:
json
[
'click_action_param'
]
as
String
?,
popup:
json
[
'popup'
]
!=
null
?
PopupDataModel
.
fromJson
(
json
[
'popup'
]
as
Map
<
String
,
dynamic
>)
:
null
,
);
Map
<
String
,
dynamic
>
toJson
()
=>
{
...
...
@@ -156,6 +163,9 @@ class DirectionalScreen {
case
DirectionalScreenName
.
myVnTraPackage
:
Get
.
toNamed
(
trafficServiceScreen
);
return
true
;
case
DirectionalScreenName
.
familyHealthBook
:
Get
.
toNamed
(
healthBookScreen
);
return
true
;
case
DirectionalScreenName
.
campaignSevenDayScreen
:
Get
.
toNamed
(
campaignSevenDayScreen
);
return
true
;
...
...
@@ -178,6 +188,25 @@ class DirectionalScreen {
case
DirectionalScreenName
.
qrCode
:
Get
.
toNamed
(
qrCodeScreen
);
return
true
;
case
DirectionalScreenName
.
makeDirectionScreen
:
if
((
clickActionParam
??
''
).
isEmpty
)
return
false
;
()
async
{
final
vm
=
RestfulApiViewModel
();
await
vm
.
callApi
<
DirectionalScreen
>(
request:
()
=>
vm
.
client
.
getDirectionScreen
(
clickActionParam
!),
onSuccess:
(
screen
,
res
)
async
{
final
popup
=
screen
.
popup
;
if
(
popup
!=
null
)
{
AppNavigator
.
showPopup
(
data:
popup
);
}
else
{
screen
.
begin
();
}
},
withLoading:
true
,
showAppNavigatorDialog:
false
,
);
}();
return
true
;
default
:
print
(
"Không nhận diện được action type:
$clickActionType
"
);
return
false
;
...
...
lib/extensions/string_extension.dart
View file @
fc2caf86
...
...
@@ -14,6 +14,8 @@ extension NullableString on String? {
final
s
=
this
?.
trim
();
return
(
s
==
null
||
s
.
isEmpty
)
?
fallback
:
s
;
}
bool
get
hasText
=>
(
this
?.
trim
().
isNotEmpty
??
false
);
}
extension
StringUrlExtension
on
String
{
...
...
lib/networking/app_navigator.dart
View file @
fc2caf86
...
...
@@ -9,6 +9,7 @@ import '../resources/base_color.dart';
import
'../shared/router_gage.dart'
;
import
'../widgets/alert/custom_alert_dialog.dart'
;
import
'../widgets/alert/data_alert_model.dart'
;
import
'../widgets/alert/popup_data_model.dart'
;
class
AppNavigator
{
static
final
GlobalKey
<
NavigatorState
>
key
=
GlobalKey
<
NavigatorState
>();
...
...
@@ -92,7 +93,7 @@ class AppNavigator {
);
}
static
showAlertError
({
static
void
showAlertError
({
required
String
content
,
bool
?
barrierDismissible
,
String
headerImage
=
"assets/images/ic_pipi_03.png"
,
...
...
@@ -127,4 +128,16 @@ class AppNavigator {
barrierDismissible:
barrierDismissible
??
false
,
);
}
static
void
showPopup
({
required
PopupDataModel
data
,
bool
?
barrierDismissibl
,
bool
showCloseButton
=
false
,
ButtonsDirection
direction
=
ButtonsDirection
.
column
,
})
{
Get
.
dialog
(
CustomAlertDialog
(
alertData:
data
.
dataAlertModel
,
showCloseButton:
showCloseButton
,
direction:
direction
),
barrierDismissible:
barrierDismissibl
??
true
,
);
}
}
lib/networking/restful_api_client_all_request.dart
View file @
fc2caf86
...
...
@@ -14,8 +14,10 @@ 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
'../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'
;
import
'../screen/register_campaign/model/verify_register_model.dart'
;
import
'../screen/splash/models/update_response_model.dart'
;
import
'../preference/point/header_home_model.dart'
;
import
'../screen/affiliate/model/affiliate_brand_model.dart'
;
...
...
@@ -791,6 +793,19 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient {
});
}
Future
<
BaseResponseModel
<
HealthBookResponseModel
>>
getHealthBookCards
(
Json
body
)
async
{
return
requestNormal
(
APIPaths
.
getHealthBookCards
,
Method
.
GET
,
body
,
(
data
)
{
return
HealthBookResponseModel
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
HealthBookCardItemModel
>>
getDetailHealthBookCard
(
String
id
)
async
{
final
path
=
APIPaths
.
detailHealthBookCardDetail
.
replaceAll
(
"%@"
,
id
);
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
{
return
HealthBookCardItemModel
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
TrafficServiceResponseModel
>>
getProductVnTraSold
(
Json
body
)
async
{
return
requestNormal
(
APIPaths
.
getProductVnTraSold
,
Method
.
GET
,
body
,
(
data
)
{
return
TrafficServiceResponseModel
.
fromJson
(
data
as
Json
);
...
...
@@ -1018,4 +1033,25 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient {
return
EmptyCodable
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
DirectionalScreen
>>
getDirectionScreen
(
String
path
)
async
{
var
path_
=
path
.
startsWith
(
'/'
)
?
path
:
'/
$path
'
;
return
requestNormal
(
path_
,
Method
.
GET
,
{},
(
data
)
{
return
DirectionalScreen
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
EmptyCodable
>>
submitShareContent
(
String
path
)
async
{
var
path_
=
path
.
startsWith
(
'/'
)
?
path
:
'/
$path
'
;
return
requestNormal
(
path_
,
Method
.
GET
,
{},
(
data
)
{
return
EmptyCodable
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
VerifyRegisterCampaignModel
>>
verifyRegisterForm
(
String
path
)
async
{
var
path_
=
path
.
startsWith
(
'/'
)
?
path
:
'/
$path
'
;
return
requestNormal
(
path_
,
Method
.
POST
,
{},
(
data
)
{
return
VerifyRegisterCampaignModel
.
fromJson
(
data
as
Json
);
});
}
}
\ No newline at end of file
lib/screen/campaign7day/campaign_7day_viewmodel.dart
View file @
fc2caf86
...
...
@@ -40,9 +40,9 @@ class Campaign7DayViewModel extends RestfulApiViewModel {
});
}
void
getCampaign7DayInfo
()
{
void
getCampaign7DayInfo
(
{
bool
silent
=
false
}
)
{
client
.
getCampaignMissions
(
campaignId
).
then
((
value
)
{
if
(!
value
.
isSuccess
)
{
if
(!
value
.
isSuccess
&&
!
silent
)
{
onShowAlertError
?.
call
(
value
.
errorMessage
??
Constants
.
commonError
);
}
campaign7DayInfo
.
value
=
value
.
data
;
...
...
@@ -55,7 +55,7 @@ class Campaign7DayViewModel extends RestfulApiViewModel {
client
.
submitPerformMission
(
mission
,
campaignId
).
then
((
value
)
{
hideLoading
();
if
(
value
.
isSuccess
)
{
//
getCampaign7DayInfo();
getCampaign7DayInfo
(
silent:
true
);
if
(
mission
.
popup
!=
null
)
{
submitPerformMissionResponse
?.
call
(
mission
);
}
else
{
...
...
lib/screen/health_book/health_book_model.dart
0 → 100644
View file @
fc2caf86
import
'package:json_annotation/json_annotation.dart'
;
import
'../traffic_service/traffic_service_model.dart'
;
part
'health_book_model.g.dart'
;
@JsonSerializable
(
explicitToJson:
true
)
class
HealthBookCardItemModel
{
@JsonKey
(
name:
'item_id'
)
final
int
?
itemId
;
@JsonKey
(
name:
'card_name'
)
final
String
?
cardName
;
@JsonKey
(
name:
'full_name'
)
final
String
?
fullName
;
@JsonKey
(
name:
'card_code'
)
final
String
?
cardCode
;
@JsonKey
(
name:
'expire_date'
)
final
String
?
expireDate
;
@JsonKey
(
name:
'phone_number'
)
final
String
?
phoneNumber
;
@JsonKey
(
name:
'updated_at'
)
final
String
?
updatedAt
;
@JsonKey
(
name:
'count_checkup_unused'
)
final
int
?
countCheckupUnused
;
@JsonKey
(
name:
'bottom_button'
)
final
ButtonConfigModel
?
bottomButton
;
final
List
<
ProductMediaItem
>?
media
;
@JsonKey
(
name:
'buy_more_note'
)
final
ButtonConfigModel
?
buyMoreNote
;
final
ActiveTextConfig
?
active
;
const
HealthBookCardItemModel
({
this
.
itemId
,
this
.
cardName
,
this
.
fullName
,
this
.
cardCode
,
this
.
expireDate
,
this
.
phoneNumber
,
this
.
updatedAt
,
this
.
countCheckupUnused
,
this
.
bottomButton
,
this
.
media
,
this
.
buyMoreNote
,
this
.
active
,
});
factory
HealthBookCardItemModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$HealthBookCardItemModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$HealthBookCardItemModelToJson
(
this
);
}
class
HealthBookResponseModel
{
final
int
?
total
;
final
List
<
HealthBookCardItemModel
>?
products
;
HealthBookResponseModel
({
this
.
total
,
this
.
products
,
});
factory
HealthBookResponseModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
HealthBookResponseModel
(
total:
json
[
'total'
],
products:
(
json
[
'products'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
HealthBookCardItemModel
.
fromJson
(
e
))
.
toList
(),
);
}
Map
<
String
,
dynamic
>
toJson
()
=>
{
'total'
:
total
,
'products'
:
products
?.
map
((
e
)
=>
e
.
toJson
()).
toList
(),
};
}
lib/screen/health_book/health_book_model.g.dart
0 → 100644
View file @
fc2caf86
// GENERATED CODE - DO NOT MODIFY BY HAND
part of
'health_book_model.dart'
;
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
HealthBookCardItemModel
_$HealthBookCardItemModelFromJson
(
Map
<
String
,
dynamic
>
json
,
)
=>
HealthBookCardItemModel
(
itemId:
(
json
[
'item_id'
]
as
num
?)?.
toInt
(),
cardName:
json
[
'card_name'
]
as
String
?,
fullName:
json
[
'full_name'
]
as
String
?,
cardCode:
json
[
'card_code'
]
as
String
?,
expireDate:
json
[
'expire_date'
]
as
String
?,
phoneNumber:
json
[
'phone_number'
]
as
String
?,
updatedAt:
json
[
'updated_at'
]
as
String
?,
countCheckupUnused:
(
json
[
'count_checkup_unused'
]
as
num
?)?.
toInt
(),
bottomButton:
json
[
'bottom_button'
]
==
null
?
null
:
ButtonConfigModel
.
fromJson
(
json
[
'bottom_button'
]
as
Map
<
String
,
dynamic
>,
),
media:
(
json
[
'media'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
ProductMediaItem
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
(),
buyMoreNote:
json
[
'buy_more_note'
]
==
null
?
null
:
ButtonConfigModel
.
fromJson
(
json
[
'buy_more_note'
]
as
Map
<
String
,
dynamic
>,
),
active:
json
[
'active'
]
==
null
?
null
:
ActiveTextConfig
.
fromJson
(
json
[
'active'
]
as
Map
<
String
,
dynamic
>),
);
Map
<
String
,
dynamic
>
_$HealthBookCardItemModelToJson
(
HealthBookCardItemModel
instance
,
)
=>
<
String
,
dynamic
>{
'item_id'
:
instance
.
itemId
,
'card_name'
:
instance
.
cardName
,
'full_name'
:
instance
.
fullName
,
'card_code'
:
instance
.
cardCode
,
'expire_date'
:
instance
.
expireDate
,
'phone_number'
:
instance
.
phoneNumber
,
'updated_at'
:
instance
.
updatedAt
,
'count_checkup_unused'
:
instance
.
countCheckupUnused
,
'bottom_button'
:
instance
.
bottomButton
?.
toJson
(),
'media'
:
instance
.
media
?.
map
((
e
)
=>
e
.
toJson
()).
toList
(),
'buy_more_note'
:
instance
.
buyMoreNote
?.
toJson
(),
'active'
:
instance
.
active
?.
toJson
(),
};
lib/screen/health_book/health_book_screen.dart
0 → 100644
View file @
fc2caf86
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/extensions/datetime_extensions.dart'
;
import
'package:mypoint_flutter_app/extensions/string_extension.dart'
;
import
'package:mypoint_flutter_app/widgets/custom_empty_widget.dart'
;
import
'package:mypoint_flutter_app/widgets/image_loader.dart'
;
import
'../../extensions/date_format.dart'
;
import
'../../resources/base_color.dart'
;
import
'../../shared/router_gage.dart'
;
import
'../../widgets/custom_navigation_bar.dart'
;
import
'health_book_viewmodel.dart'
;
class
HealthBookScreen
extends
StatefulWidget
{
const
HealthBookScreen
({
super
.
key
});
@override
State
<
HealthBookScreen
>
createState
()
=>
_HealthBookScreenState
();
}
class
_HealthBookScreenState
extends
State
<
HealthBookScreen
>
{
final
HealthBookViewModel
_viewModel
=
Get
.
put
(
HealthBookViewModel
());
@override
void
initState
()
{
super
.
initState
();
_viewModel
.
getHealthBookCards
();
}
@override
Widget
build
(
BuildContext
context
)
{
final
tags
=
_viewModel
.
headerFilterOrder
;
return
Scaffold
(
appBar:
CustomNavigationBar
(
title:
"Sổ sức khoẻ điện tử"
),
body:
Column
(
children:
[
const
SizedBox
(
height:
8
),
Obx
(()
=>
SizedBox
(
child:
SingleChildScrollView
(
scrollDirection:
Axis
.
horizontal
,
physics:
const
BouncingScrollPhysics
(),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
4
),
child:
Row
(
children:
List
.
generate
(
tags
.
length
,
(
index
)
{
final
isSelected
=
index
==
_viewModel
.
selectedIndex
.
value
;
return
GestureDetector
(
onTap:
()
{
if
(
_viewModel
.
selectedIndex
.
value
==
index
)
return
;
setState
(()
{
_viewModel
.
selectedIndex
.
value
=
index
;
_viewModel
.
getHealthBookCards
();
});
},
child:
Container
(
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
8
,
vertical:
4
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
12
),
decoration:
BoxDecoration
(
border:
Border
.
all
(
color:
isSelected
?
BaseColor
.
primary500
:
Colors
.
grey
.
shade300
,
),
borderRadius:
BorderRadius
.
circular
(
8
),
),
child:
Text
(
tags
[
index
].
title
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
softWrap:
false
,
style:
TextStyle
(
color:
isSelected
?
BaseColor
.
primary500
:
Colors
.
black87
,
),
),
),
);
}),
),
),
)
),
const
Divider
(
height:
14
,
color:
Colors
.
black12
),
const
SizedBox
(
height:
8
),
Expanded
(
child:
Obx
(()
{
final
products
=
_viewModel
.
healthBookData
.
value
?.
products
??
[];
return
products
.
isEmpty
?
Center
(
child:
EmptyWidget
())
:
ListView
.
builder
(
itemCount:
products
.
length
,
itemBuilder:
(
context
,
index
)
{
final
item
=
products
[
index
];
return
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
4
),
child:
Container
(
decoration:
BoxDecoration
(
border:
Border
.
all
(
color:
Colors
.
grey
.
shade300
),
borderRadius:
BorderRadius
.
circular
(
12
),
),
child:
ListTile
(
onTap:
()
{
print
(
'TODO Tapped on item:
${item.phoneNumber}
'
);
// Get.toNamed(trafficServiceDetailScreen, arguments: {'serviceId': item.itemId});
},
leading:
SizedBox
(
width:
60
,
// <= giới hạn rõ
height:
60
,
child:
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
8
),
child:
loadNetworkImage
(
url:
item
.
media
?.
firstOrNull
?.
url
??
''
,
fit:
BoxFit
.
cover
,
placeholderAsset:
'assets/images/bg_default_11.png'
,
),
),
),
title:
Text
(
item
.
fullName
??
''
,
style:
TextStyle
(
fontWeight:
FontWeight
.
bold
)),
subtitle:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
const
SizedBox
(
height:
4
),
Text
(
item
.
cardName
??
''
,
style:
const
TextStyle
(
fontSize:
14
,
color:
Colors
.
black87
,
fontWeight:
FontWeight
.
w500
,
),
),
const
SizedBox
(
height:
4
),
Text
(
(
item
.
expireDate
??
''
).
toDate
()?.
toFormattedString
()
??
''
,
style:
const
TextStyle
(
fontSize:
14
,
color:
Colors
.
black87
,
fontWeight:
FontWeight
.
w500
,
),
),
const
SizedBox
(
height:
4
),
Text
(
'Cập nhật lúc
${(item.updatedAt ?? '').toDate()?.toFormattedString(format: DateFormat.ddMMyyyyhhmm) ?? ''}
'
,
style:
const
TextStyle
(
fontSize:
11
,
color:
Colors
.
black54
),
),
],
),
trailing:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
8
,
vertical:
4
),
decoration:
BoxDecoration
(
color:
Colors
.
green
.
shade50
,
borderRadius:
BorderRadius
.
circular
(
4
),
),
child:
Text
(
item
.
active
?.
text
??
''
,
style:
const
TextStyle
(
fontSize:
12
,
color:
Colors
.
green
,
fontWeight:
FontWeight
.
w500
),
),
),
),
),
);
},
);
}),
),
],
),
);
}
}
lib/screen/health_book/health_book_viewmodel.dart
0 → 100644
View file @
fc2caf86
import
'package:get/get_rx/src/rx_types/rx_types.dart'
;
import
'package:mypoint_flutter_app/configs/constants.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'
;
import
'package:mypoint_flutter_app/screen/traffic_service/traffic_service_model.dart'
;
import
'../../networking/restful_api_viewmodel.dart'
;
import
'health_book_model.dart'
;
class
HealthBookViewModel
extends
RestfulApiViewModel
{
var
healthBookData
=
Rxn
<
HealthBookResponseModel
>();
var
healthBookDataDetail
=
Rxn
<
HealthBookCardItemModel
>();
void
Function
(
String
message
)?
onShowAlertError
;
RxInt
selectedIndex
=
0
.
obs
;
List
<
HeaderFilterOrderModel
>
get
headerFilterOrder
{
return
[
HeaderFilterOrderModel
(
title:
'Tất cả'
,
suffixChecking:
'tatca'
,
selected:
true
,
),
HeaderFilterOrderModel
(
title:
'Hiệu lực'
,
suffixChecking:
'hieuluc'
,
),
HeaderFilterOrderModel
(
title:
'Không hiệu lực'
,
expired:
"true"
,
suffixChecking:
'khonghieuluc'
,
),
HeaderFilterOrderModel
(
title:
'Lượt khám'
,
expired:
"true"
,
sort:
SortFilter
.
asc
,
suffixChecking:
'luotkham'
,
),
];
}
Future
<
void
>
getHealthBookCards
()
async
{
var
body
=
headerFilterOrder
[
selectedIndex
.
value
].
params
;
body
[
'page'
]
=
1
;
body
[
'size'
]
=
10000
;
showLoading
();
try
{
final
response
=
await
client
.
getHealthBookCards
(
body
);
hideLoading
();
if
(
response
.
isSuccess
)
{
healthBookData
.
value
=
response
.
data
;
}
else
{
onShowAlertError
?.
call
(
response
.
errorMessage
??
Constants
.
commonError
);
}
}
catch
(
error
)
{
hideLoading
();
onShowAlertError
?.
call
(
"Error fetching product detail:
$error
"
);
}
}
Future
<
void
>
getDetailHealthBookCard
(
String
id
)
async
{
showLoading
();
try
{
final
response
=
await
client
.
getDetailHealthBookCard
(
id
);
hideLoading
();
if
(
response
.
isSuccess
)
{
healthBookDataDetail
.
value
=
response
.
data
;
}
else
{
onShowAlertError
?.
call
(
response
.
errorMessage
??
Constants
.
commonError
);
}
}
catch
(
error
)
{
hideLoading
();
onShowAlertError
?.
call
(
"Error fetching product detail:
$error
"
);
}
}
}
\ No newline at end of file
lib/screen/home/custom_widget/header_home_widget.dart
View file @
fc2caf86
...
...
@@ -106,19 +106,20 @@ class HomeGreetingHeader extends StatelessWidget {
),
const
SizedBox
(
height:
2
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
[
_buildStatItem
(
icon:
"assets/images/ic_point_gray.png"
,
value:
(
dataHeader
.
totalPointActive
??
0
).
money
(
CurrencyUnit
.
none
),
// .toString(),
onTap:
_onPointTap
,
),
SizedBox
(
width:
12
),
const
SizedBox
(
width:
8
),
_buildStatItem
(
icon:
"assets/images/ic_voucher_gray.png"
,
value:
dataHeader
.
totalVoucher
.
toString
(),
onTap:
_onMyVoucherTap
,
),
SizedBox
(
width:
12
),
const
SizedBox
(
width:
8
),
_buildStatItem
(
icon:
"assets/images/ic_rank_gray.png"
,
value:
level
,
onTap:
_onRankTap
),
],
),
...
...
lib/screen/invite_friend_campaign/invite_friend_campaign_screen.dart
View file @
fc2caf86
...
...
@@ -32,7 +32,7 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
fetchContacts
();
viewModel
.
onShowAlertError
=
(
message
,
onBack
)
{
if
(
message
.
isNotEmpty
)
{
showAlertError
(
content:
message
,
onConfirmed:
onBack
?
()
=>
Get
.
back
()
:
null
);
showAlertError
(
content:
message
,
onConfirmed:
onBack
?
()
=>
Get
.
back
()
:
null
,
showCloseButton:
onBack
==
null
);
}
};
viewModel
.
phoneInviteFriendResponse
=
(
sms
,
phone
)
{
...
...
@@ -41,7 +41,7 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
viewModel
.
loadData
();
}
_openSMS
(
String
sms
,
String
phone
)
async
{
Future
<
void
>
_openSMS
(
String
sms
,
String
phone
)
async
{
final
uri
=
Uri
(
scheme:
'sms'
,
path:
phone
,
queryParameters:
<
String
,
String
>{
'body'
:
sms
});
if
(
await
canLaunchUrl
(
uri
))
{
...
...
lib/screen/notification/notification_screen.dart
View file @
fc2caf86
...
...
@@ -52,7 +52,7 @@ class _NotificationScreenState extends BaseState<NotificationScreen> with BasicS
link:
_layerLink
,
child:
IconButton
(
key:
_infoKey
,
icon:
const
Icon
(
Icons
.
settings
,
color:
Colors
.
black54
),
icon:
const
Icon
(
Icons
.
settings
),
onPressed:
_toggleSetting
,
),
),
...
...
lib/screen/order_menu/order_menu_screen.dart
View file @
fc2caf86
...
...
@@ -16,7 +16,7 @@ class OrderMenuScreen extends StatelessWidget {
final
List
<
_OrderMenuItem
>
items
=
[
_OrderMenuItem
(
title:
'Thẻ nạp của tôi'
,
icon:
Icons
.
credit_card
,
type:
'VIEW_MY_MOBILE_CARD'
),
_OrderMenuItem
(
title:
'Sổ sức khỏe điện tử'
,
icon:
Icons
.
medical_services_outlined
,
type:
''
),
_OrderMenuItem
(
title:
'Sổ sức khỏe điện tử'
,
icon:
Icons
.
medical_services_outlined
,
type:
'
FAMILY_HEALTH_BOOK
'
),
_OrderMenuItem
(
title:
'Dịch vụ giao thông'
,
icon:
Icons
.
traffic_outlined
,
type:
'APP_SCREEN_MY_VNTRA_PACKAGE'
),
];
...
...
lib/screen/pageDetail/campaign_detail_screen.dart
View file @
fc2caf86
...
...
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import
'package:flutter_widget_from_html/flutter_widget_from_html.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/widgets/custom_empty_widget.dart'
;
import
'package:share_plus/share_plus.dart'
;
import
'../../base/base_screen.dart'
;
import
'../../base/basic_state.dart'
;
import
'../../extensions/string_extension.dart'
;
...
...
@@ -61,6 +62,7 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
final
List
<
CampaignDetailItemModel
>
items
=
pageDetail
.
items
??
[];
final
buttonOn
=
pageDetail
.
buttonOn
??
"0"
;
final
heightContainerBottomButton
=
MediaQuery
.
of
(
context
).
padding
.
bottom
+
16
+
48
;
final
showShareButton
=
(
pageDetail
.
shareContent
??
''
).
isNotEmpty
;
return
Stack
(
children:
[
SingleChildScrollView
(
...
...
@@ -105,6 +107,29 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
],
),
),
if
(
showShareButton
)
Positioned
(
top:
MediaQuery
.
of
(
context
).
padding
.
top
+
8
,
right:
12
,
child:
SizedBox
(
width:
32
,
height:
32
,
child:
Material
(
color:
Colors
.
white
,
shape:
const
CircleBorder
(),
child:
IconButton
(
onPressed:
()
{
final
content
=
pageDetail
.
shareContent
??
""
;
SharePlus
.
instance
.
share
(
ShareParams
(
text:
content
,
title:
"Chia sẻ từ MyPoint"
),
);
_viewModel
.
submitShareContent
();
},
icon:
const
Icon
(
Icons
.
share
,
size:
16
),
),
),
),
),
Positioned
(
top:
MediaQuery
.
of
(
context
).
padding
.
top
+
8
,
left:
8
,
child:
CustomBackButton
()),
if
(
buttonOn
==
"1"
)
_bottomButton
(
pageDetail
),
],
...
...
@@ -131,7 +156,6 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
SizedBox
(
height:
12
),
ElevatedButton
(
onPressed:
()
{
print
(
"pageDetail?.directionalScreen"
);
print
(
pageDetail
?.
directionalScreen
);
print
(
pageDetail
?.
buttonClickActionType
);
print
(
pageDetail
?.
buttonClickActionParam
);
...
...
@@ -178,20 +202,18 @@ 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:
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
!),
],
)
)
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
)
{
...
...
@@ -201,11 +223,7 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
CampaignItemPageWidget
(
itemPageCampaign:
item
,
onTap:
()
async
{
Get
.
offNamed
(
campaignDetailScreen
,
arguments:
{
"id"
:
item
.
pageId
},
preventDuplicates:
false
,
);
Get
.
offNamed
(
campaignDetailScreen
,
arguments:
{
"id"
:
item
.
pageId
},
preventDuplicates:
false
);
},
),
);
...
...
lib/screen/pageDetail/campaign_detail_viewmodel.dart
View file @
fc2caf86
...
...
@@ -64,4 +64,10 @@ class CampaignDetailViewModel extends RestfulApiViewModel {
isLoading
(
false
);
});
}
Future
<
void
>
submitShareContent
()
async
{
final
path
=
campaignDetail
.
value
.
data
?.
pageDetail
?.
clickButtonShare
??
""
;
if
(
path
.
isEmpty
)
return
;
await
client
.
submitShareContent
(
path
);
}
}
lib/screen/pageDetail/model/campaign_detail_model.dart
View file @
fc2caf86
...
...
@@ -22,6 +22,10 @@ class CampaignDetailModel {
final
String
?
buttonClickActionType
;
@JsonKey
(
name:
"button_click_action_param"
)
final
String
?
buttonClickActionParam
;
@JsonKey
(
name:
"share_content"
)
final
String
?
shareContent
;
@JsonKey
(
name:
"click_button_share"
)
final
String
?
clickButtonShare
;
final
List
<
CampaignDetailItemModel
>?
items
;
CampaignDetailModel
({
...
...
@@ -34,6 +38,8 @@ class CampaignDetailModel {
this
.
buttonColor
,
this
.
buttonName
,
this
.
buttonTextColor
,
this
.
shareContent
,
this
.
clickButtonShare
,
this
.
items
,
});
...
...
lib/screen/pageDetail/model/campaign_detail_model.g.dart
View file @
fc2caf86
...
...
@@ -17,6 +17,8 @@ CampaignDetailModel _$CampaignDetailModelFromJson(Map<String, dynamic> json) =>
buttonColor:
json
[
'button_color'
]
as
String
?,
buttonName:
json
[
'button_name'
]
as
String
?,
buttonTextColor:
json
[
'button_text_color'
]
as
String
?,
shareContent:
json
[
'share_content'
]
as
String
?,
clickButtonShare:
json
[
'click_button_share'
]
as
String
?,
items:
(
json
[
'items'
]
as
List
<
dynamic
>?)
?.
map
(
...
...
@@ -38,6 +40,8 @@ Map<String, dynamic> _$CampaignDetailModelToJson(
'button_text_color'
:
instance
.
buttonTextColor
,
'button_click_action_type'
:
instance
.
buttonClickActionType
,
'button_click_action_param'
:
instance
.
buttonClickActionParam
,
'share_content'
:
instance
.
shareContent
,
'click_button_share'
:
instance
.
clickButtonShare
,
'items'
:
instance
.
items
,
};
...
...
lib/screen/personal/personal_screen.dart
View file @
fc2caf86
...
...
@@ -223,7 +223,7 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
'type'
:
'APP_SCREEN_ORDER_MENU'
,
},
{
'icon'
:
Icons
.
info_outline
,
'title'
:
'Giới thiệu MyPoint'
,
'sectionDivider'
:
true
,
'type'
:
'VIEW_WEBSITE_PAGE'
},
{
'icon'
:
Icons
.
headset_
mic_outlin
ed
,
'title'
:
'Hỗ trợ'
,
'type'
:
'APP_SCREEN_CUSTOMER_FEEDBACK'
},
{
'icon'
:
Icons
.
headset_
round
ed
,
'title'
:
'Hỗ trợ'
,
'type'
:
'APP_SCREEN_CUSTOMER_FEEDBACK'
},
{
'icon'
:
Icons
.
settings_outlined
,
'title'
:
'Cài đặt'
,
'type'
:
'APP_SCREEN_SETTING'
},
{
'icon'
:
Icons
.
logout
,
'title'
:
'Đăng xuất'
,
'color'
:
Colors
.
red
[
400
],
'type'
:
'LOGOUT'
},
];
...
...
lib/screen/register_campaign/input_form_cell.dart
View file @
fc2caf86
...
...
@@ -71,7 +71,7 @@ class _InputFormCellState extends State<InputFormCell> {
border:
InputBorder
.
none
,
counterText:
''
,
),
style:
const
TextStyle
(
fontSize:
14
),
style:
const
TextStyle
(
fontSize:
14
,
color:
Colors
.
black
),
onChanged:
(
value
)
{
widget
.
model
.
content
=
value
;
// ✅ update model
widget
.
onChanged
?.
call
();
// ✅ callback để validate bên ngoài
...
...
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