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
b3c72190
Commit
b3c72190
authored
Nov 19, 2025
by
DatHV
Browse files
update invite friend.
update config build
parent
6b980613
Changes
29
Hide whitespace changes
Inline
Side-by-side
android/app/build.gradle.kts
View file @
b3c72190
...
...
@@ -97,6 +97,10 @@ android {
buildFeatures
{
buildConfig
=
true
}
lint
{
disable
+=
setOf
(
"NullSafeMutableLiveData"
)
abortOnError
=
false
}
// --- Flavors giữ nguyên ENV của bạn ---
flavorDimensions
+=
"environment"
...
...
android/app/proguard-rules.pro
View file @
b3c72190
...
...
@@ -8,3 +8,5 @@
#
Retain
Kotlin
metadata
-
keepclassmembers
class
kotlin
.
Metadata
{
*
;
}
#
Disable
Lifecycle
lint
crash
-
dontwarn
androidx
.
lifecycle
.
MutableLiveData
assets/images/ic_logo.png
View replaced file @
6b980613
View file @
b3c72190
1.64 KB
|
W:
|
H:
7.02 KB
|
W:
|
H:
2-up
Swipe
Onion skin
lib/app/app_initializer.dart
View file @
b3c72190
import
'dart:async'
;
import
'package:firebase_messaging/firebase_messaging.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
...
...
@@ -14,6 +15,8 @@ import '../core/services/deep_link_service.dart';
/// Main app initialization and setup
class
AppInitializer
{
static
const
Duration
_asyncInitTimeout
=
Duration
(
seconds:
8
);
/// Initialize all core app features
static
Future
<
void
>
initialize
()
async
{
debugPrint
(
'🚀 Initializing app...'
);
...
...
@@ -21,9 +24,21 @@ class AppInitializer {
await
DataPreference
.
instance
.
init
();
DioHttpService
();
Get
.
put
(
HeaderThemeController
(),
permanent:
true
);
await
_initializeFirebase
();
await
_initializeWebFeatures
();
await
DeepLinkService
().
initialize
();
await
_runStep
(
label:
'Firebase'
,
action:
_initializeFirebase
,
timeout:
_asyncInitTimeout
,
);
await
_runStep
(
label:
'Web features'
,
action:
_initializeWebFeatures
,
timeout:
_asyncInitTimeout
,
);
await
_runStep
(
label:
'DeepLinkService'
,
action:
()
=>
DeepLinkService
().
initialize
(),
timeout:
_asyncInitTimeout
,
);
debugPrint
(
'✅ App initialization completed'
);
}
...
...
@@ -70,6 +85,25 @@ class AppInitializer {
}
}
static
Future
<
void
>
_runStep
({
required
String
label
,
required
Future
<
void
>
Function
()
action
,
Duration
?
timeout
,
})
async
{
try
{
if
(
timeout
!=
null
)
{
await
action
().
timeout
(
timeout
);
}
else
{
await
action
();
}
}
on
TimeoutException
{
debugPrint
(
'⏱️
$label
initialization timed out after
${timeout?.inSeconds}
s, continuing without it'
);
}
catch
(
e
,
s
)
{
debugPrint
(
'❌
$label
initialization failed:
$e
'
);
debugPrintStack
(
stackTrace:
s
);
}
}
/// Setup post-initialization callbacks
static
void
setupPostInitCallbacks
()
{
try
{
...
...
lib/app/config/api_paths.dart
View file @
b3c72190
...
...
@@ -80,6 +80,9 @@ class APIPaths {//sandbox
static
const
String
affiliateBrandGetDetail
=
"/affiliateBrandGetDetail/1.0.0"
;
static
const
String
campaignInviteFriend
=
"/campaign/api/v3.0/invite-friend"
;
static
const
String
inviteFriendCampaigns
=
"/campaign/api/v3.0/invite-friend/campaigns"
;
static
const
String
referralCampaignsInviteFriend
=
"/campaign/api/v3.0/invite-friend/apply?invite_username=%@"
;
static
const
String
referralCampaignAccept
=
"/campaign/api/v3.0/invite-friend/campaigns/%@/accept"
;
static
const
String
inviteFriendCampaignDetail
=
"/campaign/api/v3.0/invite-friend/campaigns/%@"
;
static
const
String
phoneInviteFriend
=
"/campaign/api/v3.0/invite-friend/invite"
;
static
const
String
rewardOpportunityGetList
=
"/rewardOpportunityGetList/1.0.0"
;
static
const
String
rewardOpportunityOpenRequest
=
"/rewardOpportunityOpenRequest/1.0.0"
;
...
...
lib/app/config/constants.dart
View file @
b3c72190
class
Constants
{
static
get
commonError
=>
"Hệ thống không thể xử lý yêu cầu hiện tại. Vui lòng thử lại sau hoặc liên hệ hotline 1900599863 để được trợ giúp."
;
static
String
get
commonError
=>
"Hệ thống không thể xử lý yêu cầu hiện tại. Vui lòng thử lại sau hoặc liên hệ hotline 1900599863 để được trợ giúp."
;
static
var
otpTtl
=
180
;
static
var
directionInApp
=
"IN-APP"
;
static
var
phoneNumberCount
=
10
;
static
const
timeoutSeconds
=
30
;
static
const
appStoreId
=
'1495923300'
;
static
const
double
webTopPadding
=
24.0
;
}
class
ErrorCodes
{
...
...
lib/app/routing/deferred_routes.dart
View file @
b3c72190
...
...
@@ -19,6 +19,10 @@ import '../../features/health_book/health_book_screen.dart'
deferred
as
health_book
show
HealthBookScreen
;
import
'../../features/invite_friend_campaign/invite_friend_campaign_screen.dart'
deferred
as
invite_friend
show
InviteFriendCampaignScreen
;
import
'../../features/invite_friend_campaign/referral_code_invite_friend/referral_code_invite_friend_screen.dart'
deferred
as
referral_code_invite_friend
show
ReferralCodeInviteFriendScreen
;
import
'../../features/invite_friend_campaign/campaign_invite_referral_info/campaign_invite_referral_info_screen.dart'
deferred
as
campaign_invite_referral_info_screen
show
CampaignInviteReferralInfoScreen
;
import
'../../features/quiz_campaign/quiz_campaign_screen.dart'
deferred
as
survey_question
show
SurveyQuestionScreen
;
import
'../../features/device_manager/device_manager_screen.dart'
...
...
@@ -334,6 +338,32 @@ class InviteFriendDeferredScreen extends StatelessWidget {
}
}
class
ReferralCodeInviteFriendDeferredScreen
extends
StatelessWidget
{
const
ReferralCodeInviteFriendDeferredScreen
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
DeferredScreen
(
loadLibrary:
referral_code_invite_friend
.
loadLibrary
,
builder:
(
_
)
=>
referral_code_invite_friend
.
ReferralCodeInviteFriendScreen
(),
errorMessage:
'Không thể tải nhập mã giới thiệu bạn bè.'
,
);
}
}
class
CampaignInviteReferralInfoDeferredScreen
extends
StatelessWidget
{
const
CampaignInviteReferralInfoDeferredScreen
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
DeferredScreen
(
loadLibrary:
campaign_invite_referral_info_screen
.
loadLibrary
,
builder:
(
_
)
=>
campaign_invite_referral_info_screen
.
CampaignInviteReferralInfoScreen
(),
errorMessage:
'Không thể tải nhập mã giới thiệu bạn bè.'
,
);
}
}
class
OrderMenuDeferredScreen
extends
StatelessWidget
{
const
OrderMenuDeferredScreen
({
super
.
key
});
...
...
lib/core/network/restful_api_client_all_request.dart
View file @
b3c72190
...
...
@@ -362,6 +362,32 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient {
});
}
Future
<
BaseResponseModel
<
CampaignInviteFriendDetail
>>
getReferralCampaigns
(
String
code
)
async
{
final
path
=
APIPaths
.
referralCampaignsInviteFriend
.
replaceAll
(
"%@"
,
code
);
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
{
return
CampaignInviteFriendDetail
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
InviteFriendAcceptResponse
>>
referralCampaignAccept
(
String
id
,
Json
body
)
async
{
String
?
token
=
DataPreference
.
instance
.
token
??
""
;
body
[
"access_token"
]
=
token
;
final
path
=
APIPaths
.
referralCampaignAccept
.
replaceAll
(
"%@"
,
id
);
return
requestNormal
(
path
,
Method
.
POST
,
body
,
(
data
)
{
return
InviteFriendAcceptResponse
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
CampaignInviteFriendItemModel
>>
getInviteFriendCampaignDetail
(
String
id
,
Json
body
)
async
{
String
?
token
=
DataPreference
.
instance
.
token
??
""
;
body
[
"access_token"
]
=
token
;
final
path
=
APIPaths
.
inviteFriendCampaignDetail
.
replaceAll
(
"%@"
,
id
);
return
requestNormal
(
path
,
Method
.
GET
,
body
,
(
data
)
{
return
CampaignInviteFriendItemModel
.
fromJson
(
data
as
Json
);
});
}
// inviteFriendCampaignDetail
Future
<
BaseResponseModel
<
CheckInDataModel
>>
rewardOpportunityGetList
()
async
{
String
?
token
=
DataPreference
.
instance
.
token
??
""
;
final
body
=
{
"access_token"
:
token
,
'number_day'
:
'7'
};
...
...
@@ -379,7 +405,6 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient {
}
Future
<
BaseResponseModel
<
TransactionHistoryResponse
>>
getTransactionHistoryResponse
(
Json
body
)
async
{
await
Future
.
delayed
(
Duration
(
milliseconds:
3500
));
return
requestNormal
(
APIPaths
.
getTransactionOrderHistory
,
Method
.
GET
,
body
,
(
data
)
{
return
TransactionHistoryResponse
.
fromJson
(
data
as
Json
);
});
...
...
lib/core/services/deep_link_service.dart
View file @
b3c72190
...
...
@@ -22,7 +22,7 @@ class DeepLinkService {
_initialized
=
true
;
debugPrint
(
'🔗 Initializing DeepLinkService...'
);
await
_initBranchSdk
();
//
await _initBranchSdk();
await
_handleInitialLink
();
_listenLinkStream
();
}
...
...
lib/features/home/home_screen.dart
View file @
b3c72190
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'
package:mypoint_flutter_app
/features/home/custom_widget/header_home_widget.dart'
;
import
'
package:mypoint_flutter_app
/features/home/custom_widget/product_grid_widget.dart'
;
import
'
package:mypoint_flutter_app
/features/pipi/pipi_detail_screen.dart'
;
import
'
package:mypoint_flutter_app
/features/voucher/models/product_model.dart'
;
import
'
package:mypoint_flutter_app
/shared/router_gage.dart'
;
import
'
../..
/features/home/custom_widget/header_home_widget.dart'
;
import
'
../..
/features/home/custom_widget/product_grid_widget.dart'
;
import
'
../..
/features/pipi/pipi_detail_screen.dart'
;
import
'
../..
/features/voucher/models/product_model.dart'
;
import
'
../..
/shared/router_gage.dart'
;
import
'../../shared/widgets/base_view/base_screen.dart'
;
import
'../../shared/widgets/base_view/basic_state.dart'
;
import
'../../app/routing/directional_action_type.dart'
;
...
...
lib/features/invite_friend_campaign/campaign_invite_referral_info/campaign_invite_referral_info_screen.dart
0 → 100644
View file @
b3c72190
import
'dart:math'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/app/routing/directional_action_type.dart'
;
import
'package:mypoint_flutter_app/core/theme/base_color.dart'
;
import
'package:mypoint_flutter_app/shared/navigation/directional_screen.dart'
;
import
'../../../shared/widgets/alert/custom_alert_dialog.dart'
;
import
'../../../shared/widgets/alert/data_alert_model.dart'
;
import
'../../../shared/widgets/base_view/base_screen.dart'
;
import
'../../../shared/widgets/base_view/basic_state.dart'
;
import
'../../../shared/widgets/custom_empty_widget.dart'
;
import
'../../../shared/widgets/custom_navigation_bar.dart'
;
import
'../../../shared/widgets/image_loader.dart'
;
import
'../../../shared/widgets/measure_size.dart'
;
import
'../models/invite_friend_campaign_model.dart'
;
import
'../referral_code_invite_friend/referral_code_invite_friend_viewmodel.dart'
;
class
CampaignInviteReferralInfoScreen
extends
BaseScreen
{
const
CampaignInviteReferralInfoScreen
({
super
.
key
});
@override
State
<
CampaignInviteReferralInfoScreen
>
createState
()
=>
_CampaignInviteReferralInfoScreenState
();
}
class
_CampaignInviteReferralInfoScreenState
extends
BaseState
<
CampaignInviteReferralInfoScreen
>
with
BasicState
{
late
final
ReferralCodeInviteFriendViewModel
_viewModel
;
final
ValueNotifier
<
double
>
_infoHeightNotifier
=
ValueNotifier
<
double
>(
0
);
String
?
id
;
String
?
code
;
@override
void
initState
()
{
super
.
initState
();
final
args
=
Get
.
arguments
;
if
(
args
is
Map
)
{
id
=
args
[
'id'
];
code
=
args
[
'code'
];
}
_viewModel
=
Get
.
put
(
ReferralCodeInviteFriendViewModel
());
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
_viewModel
.
getInviteFriendCampaignDetail
(
id
??
''
,
{
'invite_username'
:
code
??
''
});
});
_viewModel
.
onShowAlertError
=
(
message
)
{
if
(
message
.
isEmpty
)
return
;
showAlertError
(
content:
message
);
};
_viewModel
.
acceptCampaignResponse
=
(
data
)
{
final
popup
=
DataAlertModel
(
title:
data
.
title
??
'Nhận lời mời thành công'
,
description:
data
.
content
??
'Hãy thực hiện nhiệm vụ và xem trạng thái nhận quà trong mục lịch sử'
,
localHeaderImage:
'assets/images/ic_pipi_05.png'
,
buttons:
[
AlertButton
(
text:
'Đóng'
,
onPressed:
()
{
Get
.
back
();
},
bgColor:
BaseColor
.
second300
,
textColor:
Colors
.
white
,
),
AlertButton
(
text:
'Thực hiện'
,
onPressed:
()
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
Get
.
back
();
data
.
directionalScreen
?.
begin
();
});
},
bgColor:
BaseColor
.
primary500
,
textColor:
Colors
.
white
,
),
],
);
showAlert
(
data:
popup
,
direction:
ButtonsDirection
.
row
);
};
}
@override
void
dispose
()
{
_infoHeightNotifier
.
dispose
();
super
.
dispose
();
}
@override
Widget
createBody
()
{
return
Scaffold
(
backgroundColor:
Colors
.
grey
.
shade100
,
appBar:
PreferredSize
(
preferredSize:
const
Size
.
fromHeight
(
kToolbarHeight
),
child:
Obx
(()
{
final
title
=
_viewModel
.
campaignDetail
.
value
?.
name
??
'Chi tiết chiến dịch'
;
return
CustomNavigationBar
(
title:
title
);
}),
),
body:
Obx
(()
{
final
campaign
=
_viewModel
.
campaignDetail
.
value
;
if
(
campaign
==
null
)
{
return
Center
(
child:
EmptyWidget
(
isLoading:
_viewModel
.
isLoading
.
value
));
}
return
Stack
(
children:
[
SingleChildScrollView
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_buildHeaderWithInfo
(
campaign
),
ValueListenableBuilder
<
double
>(
valueListenable:
_infoHeightNotifier
,
builder:
(
_
,
value
,
_
)
=>
SizedBox
(
height:
max
(
value
-
24
,
0
)),
),
_buildTextBlock
(
"Phần thưởng"
,
campaign
.
rewardContent
),
_buildTextBlock
(
"Hướng dẫn"
,
campaign
.
steps
?.
description
),
_buildStepList
(
campaign
.
steps
?.
steps
),
Container
(
color:
Colors
.
transparent
,
child:
SizedBox
(
height:
64
)),
],
),
),
],
);
}),
bottomNavigationBar:
_buildBottomAction
(),
);
}
Widget
_buildStepList
(
List
<
CampaignStepItemInviteFriendDetailModel
>?
steps
)
{
if
(
steps
==
null
||
steps
.
isEmpty
)
return
const
SizedBox
();
return
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
4
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
steps
.
asMap
().
entries
.
map
((
entry
)
{
final
item
=
entry
.
value
;
final
title
=
(
item
.
title
?.
trim
().
isNotEmpty
??
false
)
?
item
.
title
!.
trim
()
:
item
.
description
?.
trim
();
final
description
=
(
item
.
title
?.
trim
().
isNotEmpty
??
false
)
?
item
.
description
?.
trim
()
:
null
;
return
Container
(
width:
double
.
infinity
,
margin:
const
EdgeInsets
.
only
(
top:
8
),
padding:
const
EdgeInsets
.
all
(
16
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
16
),
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
title
??
''
,
style:
const
TextStyle
(
fontSize:
12
,
fontWeight:
FontWeight
.
bold
,
color:
Colors
.
black87
),
),
const
SizedBox
(
height:
4
),
if
(
description
!=
null
&&
description
.
isNotEmpty
)
...[
const
SizedBox
(
height:
4
),
Text
(
description
,
style:
const
TextStyle
(
fontSize:
13
,
color:
Colors
.
black87
),
),
],
],
),
);
}).
toList
(),
),
);
}
Widget
_buildBottomAction
()
{
return
_buildBottomActionContainer
(
child:
SizedBox
(
width:
double
.
infinity
,
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Row
(
children:
[
Expanded
(
child:
ElevatedButton
(
onPressed:
()
{
Get
.
back
();
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
Colors
.
white
,
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
)),
),
child:
const
Padding
(
padding:
EdgeInsets
.
symmetric
(
vertical:
12
),
child:
Text
(
'Huỷ'
,
style:
TextStyle
(
fontSize:
16
,
color:
Colors
.
black
,
fontWeight:
FontWeight
.
w600
),
),
),
),
),
const
SizedBox
(
width:
12
),
Expanded
(
child:
ElevatedButton
(
onPressed:
()
{
final
campaign
=
_viewModel
.
campaignDetail
.
value
;
if
(
campaign
==
null
)
return
;
_viewModel
.
acceptCampaign
(
campaign
,
code
??
''
);
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
BaseColor
.
primary500
,
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
)),
),
child:
const
Padding
(
padding:
EdgeInsets
.
symmetric
(
vertical:
12
),
child:
Text
(
'Xác nhận'
,
style:
TextStyle
(
fontSize:
16
,
color:
Colors
.
white
,
fontWeight:
FontWeight
.
w600
),
),
),
),
),
],
),
TextButton
(
onPressed:
()
{
final
id
=
_viewModel
.
campaignDetail
.
value
?.
rulesId
.
toString
()
??
''
;
if
(
id
.
isEmpty
)
return
;
final
direction
=
DirectionalScreen
.
buildByName
(
name:
DirectionalScreenName
.
website
,
clickActionParam:
id
);
direction
?.
begin
();
},
style:
TextButton
.
styleFrom
(
backgroundColor:
Colors
.
white
,
foregroundColor:
Colors
.
blueAccent
,
),
child:
const
Text
(
'Xem thể lệ'
,
style:
TextStyle
(
fontSize:
16
,
fontWeight:
FontWeight
.
w600
)),
),
],
),
),
);
}
Widget
_buildBottomActionContainer
({
required
Widget
child
})
{
return
Container
(
padding:
const
EdgeInsets
.
only
(
left:
16
,
right:
16
,
top:
12
,
bottom:
4
),
decoration:
const
BoxDecoration
(
color:
Colors
.
white
,
boxShadow:
[
BoxShadow
(
color:
Colors
.
black54
,
blurRadius:
8
,
offset:
Offset
(
0
,
4
))],
),
child:
SafeArea
(
top:
false
,
child:
child
),
);
}
Widget
_buildTextBlock
(
String
title
,
String
?
content
)
{
if
(
content
==
null
||
content
.
isEmpty
)
return
const
SizedBox
();
return
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
8
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
const
SizedBox
(
height:
8
),
Text
(
title
,
style:
const
TextStyle
(
fontSize:
16
,
fontWeight:
FontWeight
.
bold
)),
Container
(
width:
double
.
infinity
,
padding:
const
EdgeInsets
.
all
(
16
),
margin:
const
EdgeInsets
.
only
(
top:
8
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
16
)),
child:
HtmlWidget
(
content
,
textStyle:
const
TextStyle
(
fontSize:
14
,
color:
Colors
.
black87
)),
),
],
),
);
}
Widget
_buildHeaderWithInfo
(
CampaignInviteFriendItemModel
campaign
)
{
final
double
screenWidth
=
MediaQuery
.
of
(
context
).
size
.
width
;
final
double
imageHeight
=
screenWidth
/
(
16
/
9
);
return
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
loadNetworkImage
(
url:
campaign
.
bannerUrl
,
fit:
BoxFit
.
cover
,
height:
imageHeight
,
width:
double
.
infinity
,
placeholderAsset:
'assets/images/bg_default_169.png'
,
),
Positioned
(
left:
16
,
right:
16
,
child:
MeasureSize
(
onChange:
(
size
)
{
if
(
_infoHeightNotifier
.
value
!=
size
.
height
)
{
_infoHeightNotifier
.
value
=
size
.
height
;
}
},
child:
Transform
.
translate
(
offset:
Offset
(
0
,
imageHeight
-
24
),
child:
_buildInfo
(
campaign
),
),
),
),
],
);
}
Widget
_buildInfo
(
CampaignInviteFriendItemModel
campaign
)
{
return
Container
(
padding:
const
EdgeInsets
.
all
(
16
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
16
),
boxShadow:
[
BoxShadow
(
color:
Colors
.
black
.
withOpacity
(
0.1
),
blurRadius:
8
,
offset:
const
Offset
(
0
,
4
))],
),
child:
HtmlWidget
(
campaign
.
inviteDescription
??
''
,
textStyle:
const
TextStyle
(
fontSize:
14
,
color:
Colors
.
black87
),
),
);
}
}
lib/features/invite_friend_campaign/invite_friend_campaign_list_screen.dart
deleted
100644 → 0
View file @
6b980613
import
'package:flutter/material.dart'
;
import
'../../shared/widgets/custom_navigation_bar.dart'
;
class
InviteFriendCampaignListScreen
extends
StatefulWidget
{
const
InviteFriendCampaignListScreen
({
super
.
key
});
@override
State
<
InviteFriendCampaignListScreen
>
createState
()
=>
_InviteFriendCampaignListScreenState
();
}
class
_InviteFriendCampaignListScreenState
extends
State
<
InviteFriendCampaignListScreen
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
CustomNavigationBar
(
title:
"title"
),
body:
Container
(),
);
}
}
lib/features/invite_friend_campaign/invite_friend_campaign_screen.dart
View file @
b3c72190
...
...
@@ -2,10 +2,11 @@ import 'package:flutter/material.dart';
import
'package:flutter/services.dart'
;
import
'package:flutter_contacts/flutter_contacts.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/features/invite_friend_campaign/popup_invite_friend_code.dart'
;
import
'package:mypoint_flutter_app/shared/widgets/custom_toast_message.dart'
;
import
'package:mypoint_flutter_app/shared/widgets/custom_empty_widget.dart'
;
import
'package:share_plus/share_plus.dart'
;
import
'package:url_launcher/url_launcher.dart'
;
import
'../../../features/invite_friend_campaign/popup_invite_friend_code.dart'
;
import
'../../../shared/widgets/custom_toast_message.dart'
;
import
'../../shared/widgets/base_view/base_screen.dart'
;
import
'../../shared/widgets/base_view/basic_state.dart'
;
import
'../../core/theme/base_color.dart'
;
...
...
@@ -65,9 +66,16 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_buildHeaderInfo
(),
SizedBox
(
height:
56
),
_buildInviteCardBox
(),
if
(
viewModel
.
inviteFriendDetail
.
value
!=
null
)
...[
_buildHeaderInfo
(),
SizedBox
(
height:
56
),
_buildInviteCardBox
(),
],
if
(
viewModel
.
inviteFriendDetail
.
value
==
null
)
...[
SizedBox
(
height:
16
),
EmptyWidget
(
isLoading:
viewModel
.
isLoading
.
value
),
SizedBox
(
height:
16
),
],
_buildContactCardBox
(),
_buildCampaignBox
(),
Container
(
color:
Colors
.
grey
.
shade100
,
child:
SizedBox
(
height:
64
)),
...
...
@@ -109,7 +117,11 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
left:
16
,
right:
16
,
child:
GestureDetector
(
onTap:
()
{},
onTap:
()
{
Get
.
toNamed
(
referralCodeInviteFriendScreen
,
arguments:
{
'title'
:
viewModel
.
inviteFriendDetail
.
value
?.
name
??
'Mã giới thiệu'
});
},
child:
Container
(
height:
42
,
decoration:
BoxDecoration
(
...
...
@@ -120,7 +132,7 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
child:
Row
(
children:
[
const
SizedBox
(
width:
8
),
Icon
(
Icons
.
person
,
color:
Colors
.
black54
),
Icon
(
Icons
.
person
_add_alt_sharp
,
color:
Colors
.
black54
),
const
SizedBox
(
width:
8
),
const
Text
(
'Nhập mã giới thiệu bạn bè ở đây'
,
...
...
lib/features/invite_friend_campaign/invite_friend_campaign_viewmodel.dart
View file @
b3c72190
...
...
@@ -50,10 +50,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel {
onSuccess:
(
data
,
_
)
{
campaignDetail
.
value
=
data
;
},
onFailure:
(
msg
,
_
,
_
)
async
{
// Silent failure for this optional call
},
showAppNavigatorDialog:
false
,
withLoading:
false
,
);
}
}
lib/features/invite_friend_campaign/models/invite_friend_campaign_model.dart
View file @
b3c72190
import
'package:mypoint_flutter_app/shared/navigation/directional_screen.dart'
;
class
RewardInviteItemModel
{
String
?
type
;
String
?
icon
;
...
...
@@ -67,6 +69,42 @@ class InviteFriendDetailModel {
}
}
class
CampaignStepsInviteFriendDetailModel
{
String
?
description
;
List
<
CampaignStepItemInviteFriendDetailModel
>?
steps
;
CampaignStepsInviteFriendDetailModel
({
this
.
description
,
this
.
steps
,
});
factory
CampaignStepsInviteFriendDetailModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
CampaignStepsInviteFriendDetailModel
(
description:
json
[
'description'
],
steps:
(
json
[
'steps'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
CampaignStepItemInviteFriendDetailModel
.
fromJson
(
e
))
.
toList
(),
);
}
}
class
CampaignStepItemInviteFriendDetailModel
{
String
?
description
;
String
?
title
;
CampaignStepItemInviteFriendDetailModel
({
this
.
description
,
this
.
title
,
});
factory
CampaignStepItemInviteFriendDetailModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
CampaignStepItemInviteFriendDetailModel
(
description:
json
[
'description'
],
title:
json
[
'title'
],
);
}
}
class
CampaignInviteFriendItemModel
{
String
?
id
;
String
?
name
;
...
...
@@ -77,6 +115,8 @@ class CampaignInviteFriendItemModel {
RewardInviteItemModel
?
reward
;
String
?
rules
;
String
?
inviteLink
;
String
?
rewardContent
;
CampaignStepsInviteFriendDetailModel
?
steps
;
int
?
rulesId
;
CampaignInviteFriendItemModel
({
...
...
@@ -89,6 +129,8 @@ class CampaignInviteFriendItemModel {
this
.
reward
,
this
.
rules
,
this
.
inviteLink
,
this
.
rewardContent
,
this
.
steps
,
this
.
rulesId
,
});
...
...
@@ -105,6 +147,10 @@ class CampaignInviteFriendItemModel {
:
null
,
rules:
json
[
'rules'
],
inviteLink:
json
[
'invite_link'
],
rewardContent:
json
[
'reward_content'
],
steps:
json
[
'steps'
]
!=
null
?
CampaignStepsInviteFriendDetailModel
.
fromJson
(
json
[
'steps'
])
:
null
,
rulesId:
json
[
'rules_id'
],
);
}
...
...
@@ -129,7 +175,6 @@ class CampaignInviteFriendDetail {
}
}
class
InviteFriendResponse
{
final
String
?
sms
;
...
...
@@ -146,4 +191,43 @@ class InviteFriendResponse {
'sms'
:
sms
,
};
}
}
class
InviteFriendAcceptResponse
{
final
String
?
content
;
final
String
?
title
;
final
String
?
clickActionType
;
final
String
?
clickActionParam
;
DirectionalScreen
?
get
directionalScreen
{
return
DirectionalScreen
.
build
(
clickActionType:
clickActionType
,
clickActionParam:
clickActionParam
,
);
}
InviteFriendAcceptResponse
({
this
.
content
,
this
.
title
,
this
.
clickActionType
,
this
.
clickActionParam
,
});
factory
InviteFriendAcceptResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
InviteFriendAcceptResponse
(
content:
json
[
'content'
]
as
String
?,
title:
json
[
'title'
]
as
String
?,
clickActionType:
json
[
'click_action_type'
]
as
String
?,
clickActionParam:
json
[
'click_action_param'
]
as
String
?,
);
}
Map
<
String
,
dynamic
>
toJson
()
{
return
{
'content'
:
content
,
'title'
:
title
,
'click_action_type'
:
clickActionType
,
'click_action_param'
:
clickActionParam
,
};
}
}
\ No newline at end of file
lib/features/invite_friend_campaign/popup_invite_friend_code.dart
View file @
b3c72190
import
'dart:ui'
as
ui
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:get/get.dart'
;
import
'package:image_gallery_saver/image_gallery_saver.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
import
'package:qr_flutter/qr_flutter.dart'
;
import
'../../app/routing/app_navigator.dart'
;
import
'../../core/theme/base_color.dart'
;
import
'../../shared/widgets/alert/data_alert_model.dart'
;
import
'../../shared/widgets/custom_toast_message.dart'
;
void
showPopupInviteFriendCode
(
BuildContext
context
,
String
qrString
)
{
final
width
=
MediaQuery
.
of
(
context
).
size
.
width
;
final
qrKey
=
GlobalKey
();
Future
<
void
>
saveQrImage
()
async
{
if
(
kIsWeb
)
{
showToastMessage
(
'Chức năng lưu ảnh không hỗ trợ trên web.'
);
return
;
}
final
boundary
=
qrKey
.
currentContext
?.
findRenderObject
()
as
RenderRepaintBoundary
?;
if
(
boundary
==
null
)
{
showToastMessage
(
'Không thể lưu ảnh.'
);
return
;
}
final
hasPermission
=
await
_ensureMediaPermission
();
if
(!
hasPermission
)
{
_showAlertNotPermission
();
return
;
}
try
{
final
ui
.
Image
image
=
await
boundary
.
toImage
(
pixelRatio:
3.0
);
final
ByteData
?
data
=
await
image
.
toByteData
(
format:
ui
.
ImageByteFormat
.
png
);
final
bytes
=
data
?.
buffer
.
asUint8List
();
if
(
bytes
==
null
)
{
showToastMessage
(
'Không thể lưu ảnh.'
);
return
;
}
final
result
=
await
ImageGallerySaver
.
saveImage
(
bytes
,
name:
'mypoint_invite_
${DateTime.now().toString()}
'
,
quality:
100
,
);
final
success
=
(
result
[
'isSuccess'
]
??
result
[
'success'
]
??
result
[
'status'
])
==
true
;
showToastMessage
(
success
?
'Đã lưu ảnh vào thư viện.'
:
'Không thể lưu ảnh.'
);
}
catch
(
e
)
{
showToastMessage
(
'Không thể lưu ảnh.'
);
}
}
showModalBottomSheet
(
context:
context
,
isScrollControlled:
true
,
...
...
@@ -52,25 +99,53 @@ void showPopupInviteFriendCode(BuildContext context, String qrString) {
children:
[
Image
.
asset
(
'assets/images/ic_pipi_couple.png'
,
height:
72
),
const
SizedBox
(
height:
16
),
Container
(
color:
Colors
.
white
,
child:
QrImageView
(
data:
qrString
,
version:
QrVersions
.
auto
,
size:
width
/
1.7
,
embeddedImage:
const
AssetImage
(
'assets/images/ic_logo.png'
),
embeddedImageStyle:
const
QrEmbeddedImageStyle
(
size:
Size
(
40
,
40
)),
if
(!
kIsWeb
)
...[
RepaintBoundary
(
key:
qrKey
,
child:
Container
(
color:
Colors
.
white
,
child:
QrImageView
(
data:
qrString
,
version:
QrVersions
.
auto
,
size:
width
/
1.7
,
embeddedImage:
const
AssetImage
(
'assets/images/ic_logo.png'
),
embeddedImageStyle:
const
QrEmbeddedImageStyle
(
size:
Size
(
40
,
40
)),
),
),
),
),
const
SizedBox
(
height:
8
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
const
[
Icon
(
Icons
.
download_outlined
,
color:
Colors
.
black54
),
SizedBox
(
width:
4
),
Text
(
'Lưu ảnh'
),
],
),
const
SizedBox
(
height:
8
),
InkWell
(
borderRadius:
BorderRadius
.
circular
(
24
),
onTap:
saveQrImage
,
child:
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
8
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
const
[
Icon
(
Icons
.
download_outlined
,
color:
Colors
.
black54
),
SizedBox
(
width:
4
),
Text
(
'Lưu ảnh'
),
],
),
),
),
]
else
...[
Container
(
padding:
const
EdgeInsets
.
all
(
16
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
),
),
child:
QrImageView
(
data:
qrString
,
version:
QrVersions
.
auto
,
size:
width
/
1.7
,
embeddedImage:
const
AssetImage
(
'assets/images/ic_logo.png'
),
embeddedImageStyle:
const
QrEmbeddedImageStyle
(
size:
Size
(
40
,
40
)),
),
),
const
SizedBox
(
height:
8
),
],
const
SizedBox
(
height:
8
),
const
Text
(
'Mã giới thiệu:'
),
Text
(
...
...
@@ -88,3 +163,41 @@ void showPopupInviteFriendCode(BuildContext context, String qrString) {
},
);
}
void
_showAlertNotPermission
(
)
{
final
dataAlert
=
DataAlertModel
(
title:
"Không có quyền truy cập"
,
description:
"Ứng dụng không có quyền lưu hình ảnh. Vui lòng vào cài đặt để cho phép quyền truy cập"
,
localHeaderImage:
"assets/images/ic_pipi_03.png"
,
buttons:
[
AlertButton
(
text:
"Cài Đặt"
,
onPressed:
()
async
{
Get
.
back
();
openAppSettings
();
},
bgColor:
BaseColor
.
primary500
,
textColor:
Colors
.
white
,
),
AlertButton
(
text:
"Đóng"
,
onPressed:
()
=>
Get
.
back
(),
bgColor:
Colors
.
white
,
textColor:
BaseColor
.
second500
),
],
);
AppNavigator
.
showAlert
(
data:
dataAlert
);
}
Future
<
bool
>
_ensureMediaPermission
()
async
{
if
(
kIsWeb
)
return
false
;
PermissionStatus
status
;
if
(
defaultTargetPlatform
==
TargetPlatform
.
iOS
)
{
status
=
await
Permission
.
photosAddOnly
.
request
();
if
(
status
.
isGranted
)
return
true
;
status
=
await
Permission
.
photos
.
request
();
if
(
status
.
isGranted
)
return
true
;
}
else
{
status
=
await
Permission
.
photos
.
request
();
if
(
status
.
isGranted
)
return
true
;
status
=
await
Permission
.
storage
.
request
();
if
(
status
.
isGranted
||
status
.
isLimited
)
return
true
;
}
return
false
;
}
lib/features/invite_friend_campaign/referral_code_invite_friend/referral_code_invite_friend_screen.dart
0 → 100644
View file @
b3c72190
import
'dart:async'
;
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'../../../../core/utils/extensions/string_extension.dart'
;
import
'../../../features/invite_friend_campaign/referral_code_invite_friend/referral_code_invite_friend_viewmodel.dart'
;
import
'../../../shared/router_gage.dart'
;
import
'../../../shared/widgets/alert/custom_alert_dialog.dart'
;
import
'../../../shared/widgets/alert/data_alert_model.dart'
;
import
'../../../shared/widgets/custom_empty_widget.dart'
;
import
'../../../core/theme/base_color.dart'
;
import
'../../../shared/widgets/base_view/base_screen.dart'
;
import
'../../../shared/widgets/base_view/basic_state.dart'
;
import
'../../../shared/widgets/custom_navigation_bar.dart'
;
import
'../../../shared/widgets/image_loader.dart'
;
import
'../models/invite_friend_campaign_model.dart'
;
class
ReferralCodeInviteFriendScreen
extends
BaseScreen
{
const
ReferralCodeInviteFriendScreen
({
super
.
key
});
@override
State
<
ReferralCodeInviteFriendScreen
>
createState
()
=>
_ReferralCodeInviteFriendScreenState
();
}
class
_ReferralCodeInviteFriendScreenState
extends
BaseState
<
ReferralCodeInviteFriendScreen
>
with
BasicState
{
final
ReferralCodeInviteFriendViewModel
viewModel
=
Get
.
put
(
ReferralCodeInviteFriendViewModel
());
final
TextEditingController
_codeController
=
TextEditingController
();
Timer
?
_debounce
;
String
_title
=
'Mời bạn bè'
;
void
_onCodeChanged
(
String
value
)
{
setState
(()
{});
_debounce
?.
cancel
();
final
code
=
value
.
trim
();
if
(
code
.
isEmpty
)
{
viewModel
.
referralCampaignData
.
value
=
null
;
return
;
}
_debounce
=
Timer
(
const
Duration
(
seconds:
1
),
()
{
viewModel
.
getCampaignReferralCodeInvite
(
code
);
});
}
void
_clearCode
()
{
_debounce
?.
cancel
();
_codeController
.
clear
();
viewModel
.
referralCampaignData
.
value
=
null
;
setState
(()
{});
}
@override
void
initState
()
{
super
.
initState
();
final
args
=
Get
.
arguments
;
if
(
args
is
Map
)
{
_title
=
args
[
'title'
];
}
viewModel
.
getCampaignReferralCodeInvite
(
''
);
viewModel
.
onShowAlertError
=
(
message
)
{
showAlertError
(
content:
message
);
};
viewModel
.
acceptCampaignResponse
=
(
data
)
{
final
popup
=
DataAlertModel
(
title:
data
.
title
??
'Nhận lời mời thành công'
,
description:
data
.
content
??
'Hãy thực hiện nhiệm vụ và xem trạng thái nhận quà trong mục lịch sử'
,
localHeaderImage:
'assets/images/ic_pipi_05.png'
,
buttons:
[
AlertButton
(
text:
'Đóng'
,
onPressed:
()
{
Get
.
back
();
},
bgColor:
BaseColor
.
second300
,
textColor:
Colors
.
white
,
),
AlertButton
(
text:
'Thực hiện'
,
onPressed:
()
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
Get
.
back
();
data
.
directionalScreen
?.
begin
();
});
},
bgColor:
BaseColor
.
primary500
,
textColor:
Colors
.
white
,
),
],
);
showAlert
(
data:
popup
,
direction:
ButtonsDirection
.
row
);
};
}
@override
void
dispose
()
{
_debounce
?.
cancel
();
_codeController
.
dispose
();
super
.
dispose
();
}
@override
Widget
createBody
()
{
return
Scaffold
(
appBar:
CustomNavigationBar
(
title:
_title
),
body:
Column
(
children:
[
_buildCodeInput
(),
Expanded
(
child:
Obx
(()
{
final
detail
=
viewModel
.
referralCampaignData
.
value
;
final
campaigns
=
detail
?.
campaigns
??
[];
if
(
campaigns
.
isEmpty
)
{
return
Center
(
child:
EmptyWidget
(
isLoading:
viewModel
.
isLoading
.
value
));
}
return
SingleChildScrollView
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_buildCampaignBox
(
detail
,
campaigns
),
Container
(
color:
Colors
.
grey
.
shade100
,
child:
const
SizedBox
(
height:
64
)),
],
),
);
}),
),
],
),
);
}
Widget
_buildCodeInput
()
{
return
Padding
(
padding:
const
EdgeInsets
.
all
(
16
),
child:
Container
(
decoration:
BoxDecoration
(
// color: Colors.white,
color:
Color
(
0xFFFDE8EA
),
border:
Border
.
all
(
color:
BaseColor
.
primary500
),
borderRadius:
BorderRadius
.
circular
(
12
),
),
child:
TextField
(
controller:
_codeController
,
onChanged:
_onCodeChanged
,
decoration:
InputDecoration
(
prefixIcon:
Icon
(
Icons
.
person_add_alt_sharp
,
color:
Colors
.
black
),
hintText:
'Nhập mã giới thiệu bạn bè ở đây'
,
suffixIcon:
_codeController
.
text
.
isEmpty
?
null
:
IconButton
(
icon:
Icon
(
Icons
.
close
,
color:
Colors
.
black
),
onPressed:
_clearCode
),
border:
InputBorder
.
none
,
contentPadding:
const
EdgeInsets
.
symmetric
(
vertical:
12
,
horizontal:
8
),
),
),
),
);
}
Widget
_buildCampaignBox
(
CampaignInviteFriendDetail
?
detail
,
List
<
CampaignInviteFriendItemModel
>
campaigns
)
{
return
Container
(
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
16
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
)),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
if
((
detail
?.
title
??
''
).
isNotEmpty
)
Padding
(
padding:
const
EdgeInsets
.
fromLTRB
(
0
,
8
,
8
,
0
),
child:
Text
(
detail
?.
title
??
''
,
maxLines:
1
,
style:
const
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
w800
),
),
),
ListView
.
separated
(
shrinkWrap:
true
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
4
),
physics:
const
NeverScrollableScrollPhysics
(),
itemCount:
campaigns
.
length
,
separatorBuilder:
(
_
,
_
)
=>
Divider
(
height:
1
,
color:
Colors
.
grey
.
shade200
),
itemBuilder:
(
context
,
index
)
{
final
campaign
=
campaigns
[
index
];
return
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
final
code
=
_codeController
.
text
.
trim
();
Get
.
toNamed
(
campaignInviteReferralInfoScreen
,
arguments:
{
'id'
:
campaign
.
id
??
''
,
'code'
:
code
});
print
(
'Tap campaign:
${campaign.id}
'
);
},
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
12
),
child:
Row
(
children:
[
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
8
),
child:
loadNetworkImage
(
url:
campaign
.
avatarUrl
,
width:
82
,
height:
82
,
placeholderAsset:
"assets/images/bg_default_11.png"
,
),
),
const
SizedBox
(
width:
8
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
if
(
campaign
.
name
.
orEmpty
.
isNotEmpty
)
Text
(
campaign
.
name
??
''
,
maxLines:
2
,
style:
const
TextStyle
(
fontWeight:
FontWeight
.
bold
,
fontSize:
14
),
),
if
(
campaign
.
name
.
orEmpty
.
isNotEmpty
)
...[
const
SizedBox
(
height:
4
),
Text
(
campaign
.
description
??
''
,
maxLines:
2
,
style:
const
TextStyle
(
fontSize:
12
,
color:
Colors
.
black54
),
),
],
],
),
),
const
SizedBox
(
width:
8
),
OutlinedButton
(
style:
OutlinedButton
.
styleFrom
(
side:
const
BorderSide
(
color:
BaseColor
.
primary500
,
width:
1.5
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
12
)),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
8
),
backgroundColor:
BaseColor
.
primary500
,
),
onPressed:
()
{
viewModel
.
acceptCampaign
(
campaign
,
_codeController
.
text
.
trim
());
},
child:
const
Text
(
'Nhận lời mời'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontWeight:
FontWeight
.
w600
,
fontSize:
13
),
),
),
],
),
),
);
},
),
],
),
);
}
}
lib/features/invite_friend_campaign/referral_code_invite_friend/referral_code_invite_friend_viewmodel.dart
0 → 100644
View file @
b3c72190
import
'package:get/get_rx/src/rx_types/rx_types.dart'
;
import
'package:mypoint_flutter_app/core/network/restful_api_client_all_request.dart'
;
import
'../../../app/config/callbacks.dart'
;
import
'../../../core/network/restful_api_viewmodel.dart'
;
import
'../models/invite_friend_campaign_model.dart'
;
class
ReferralCodeInviteFriendViewModel
extends
RestfulApiViewModel
{
final
Rxn
<
CampaignInviteFriendItemModel
>
campaignDetail
=
Rxn
<
CampaignInviteFriendItemModel
>();
final
Rxn
<
CampaignInviteFriendDetail
>
referralCampaignData
=
Rxn
<
CampaignInviteFriendDetail
>();
void
Function
(
String
message
)?
onShowAlertError
;
void
Function
(
InviteFriendAcceptResponse
data
)?
acceptCampaignResponse
;
Future
<
void
>
getInviteFriendCampaignDetail
(
String
id
,
Json
body
)
async
{
await
callApi
<
CampaignInviteFriendItemModel
>(
request:
()
=>
client
.
getInviteFriendCampaignDetail
(
id
,
body
),
onSuccess:
(
data
,
_
)
{
campaignDetail
.
value
=
data
;
},
onFailure:
(
msg
,
_
,
_
)
async
{
onShowAlertError
?.
call
(
msg
);
},
);
}
Future
<
void
>
acceptCampaign
(
CampaignInviteFriendItemModel
campaign
,
String
code
)
async
{
await
callApi
<
InviteFriendAcceptResponse
>(
request:
()
=>
client
.
referralCampaignAccept
(
campaign
.
id
??
''
,
{
'username'
:
code
}),
onSuccess:
(
data
,
_
)
{
acceptCampaignResponse
?.
call
(
data
);
},
onFailure:
(
msg
,
_
,
_
)
async
{
onShowAlertError
?.
call
(
msg
);
},
);
}
Future
<
void
>
getCampaignReferralCodeInvite
(
String
code
)
async
{
await
callApi
<
CampaignInviteFriendDetail
>(
request:
()
=>
client
.
getDetailCampaignInviteFriend
(),
onSuccess:
(
data
,
_
)
{
referralCampaignData
.
value
=
data
;
},
onFailure:
(
msg
,
_
,
_
)
async
{
onShowAlertError
?.
call
(
msg
);
},
);
}
}
lib/features/notification/notification_detail_screen.dart
View file @
b3c72190
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/core/utils/extensions/string_extension.dart'
;
import
'package:mypoint_flutter_app/shared/widgets/custom_app_bar.dart'
;
import
'../../shared/widgets/base_view/base_screen.dart'
;
import
'../../shared/widgets/base_view/basic_state.dart'
;
import
'../../shared/widgets/custom_navigation_bar.dart'
;
import
'../../shared/widgets/image_loader.dart'
;
import
'models/notification_detail_model.dart'
;
import
'notification_detail_viewmodel.dart'
;
...
...
@@ -57,7 +57,7 @@ class _NotificationDetailScreenState extends BaseState<NotificationDetailScreen>
@override
Widget
createBody
()
{
return
Scaffold
(
appBar:
Custom
AppBar
.
back
(
title:
"
Chi tiết thông báo
"
),
appBar:
Custom
NavigationBar
(
title:
'
Chi tiết thông báo
'
),
body:
Obx
(
()
=>
Container
(
color:
Colors
.
grey
.
shade100
,
...
...
lib/features/personal/personal_screen.dart
View file @
b3c72190
...
...
@@ -5,6 +5,7 @@ import 'package:mypoint_flutter_app/shared/navigation/directional_screen.dart';
import
'package:mypoint_flutter_app/core/utils/extensions/num_extension.dart'
;
import
'package:mypoint_flutter_app/shared/preferences/data_preference.dart'
;
import
'package:mypoint_flutter_app/shared/preferences/point/point_manager.dart'
;
import
'../../app/config/constants.dart'
;
import
'../../shared/widgets/base_view/base_screen.dart'
;
import
'../../shared/widgets/base_view/basic_state.dart'
;
import
'../../app/routing/directional_action_type.dart'
;
...
...
@@ -76,11 +77,12 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
final
name
=
DataPreference
.
instance
.
displayName
;
final
level
=
DataPreference
.
instance
.
rankName
??
"Hạng Đồng"
;
final
email
=
DataPreference
.
instance
.
profile
?.
workerSite
?.
email
??
""
;
final
topWebPadding
=
kIsWeb
?
Constants
.
webTopPadding
:
0.0
;
return
Container
(
height:
width
*
163
/
375
,
height:
width
*
163
/
375
+
topWebPadding
,
decoration:
BoxDecoration
(
image:
DecorationImage
(
image:
NetworkImage
(
data
.
background
??
""
),
fit:
BoxFit
.
cover
)),
child:
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
8
),
padding:
EdgeInsets
.
only
(
top:
12
+
topWebPadding
,
bottom:
12
,
left:
8
,
right:
8
),
//
symmetric(horizontal: 12, vertical: 8),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
...
...
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