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
fda33894
Commit
fda33894
authored
Jun 06, 2025
by
DatHV
Browse files
cap nhat giao dien
parent
75178f29
Changes
86
Hide whitespace changes
Inline
Side-by-side
lib/screen/home/pipi_detail_viewmodel.dart
0 → 100644
View file @
fda33894
import
'package:get/get_rx/src/rx_types/rx_types.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_request.dart'
;
import
'../../base/restful_api_viewmodel.dart'
;
import
'models/pipi_detail_model.dart'
;
class
PipiDetailViewModel
extends
RestfulApiViewModel
{
var
items
=
RxList
<
PipiSupportItemModel
>();
PipiDetailViewModel
();
@override
void
onInit
()
{
super
.
onInit
();
fetchPipiDetails
();
}
Future
<
void
>
fetchPipiDetails
()
async
{
try
{
final
response
=
await
client
.
getPipiDetail
();
items
.
value
=
response
.
data
?.
items
??
[];
}
catch
(
error
)
{
print
(
"Error fetching Pipi details:
$error
"
);
}
}
}
\ No newline at end of file
lib/screen/login/login_viewmodel.dart
View file @
fda33894
...
...
@@ -4,6 +4,7 @@ import 'package:mypoint_flutter_app/configs/constants.dart';
import
'package:mypoint_flutter_app/networking/restful_api_request.dart'
;
import
'package:mypoint_flutter_app/screen/otp/forgot_pass_otp_repository.dart'
;
import
'package:mypoint_flutter_app/screen/otp/otp_screen.dart'
;
import
'package:mypoint_flutter_app/shared/router_gage.dart'
;
import
'../../base/base_response_model.dart'
;
import
'../../base/restful_api_viewmodel.dart'
;
import
'../../model/auth/login_token_response_model.dart'
;
...
...
@@ -71,7 +72,7 @@ class LoginViewModel extends RestfulApiViewModel {
final
userProfile
=
value
.
data
;
if
(
value
.
isSuccess
&&
userProfile
!=
null
)
{
await
DataPreference
.
instance
.
saveUserProfile
(
userProfile
);
Get
.
to
(
MainTab
Screen
()
);
Get
.
to
Named
(
main
Screen
);
}
else
{
DataPreference
.
instance
.
clearLoginToken
();
final
mgs
=
value
.
errorMessage
??
Constants
.
commonError
;
...
...
lib/screen/news/models/news_item_model.dart
0 → 100644
View file @
fda33894
import
'package:json_annotation/json_annotation.dart'
;
part
'news_item_model.g.dart'
;
@JsonSerializable
()
class
NewsItemModel
{
final
String
?
thumbnail
;
@JsonKey
(
name:
'page_id'
)
final
String
?
pageId
;
@JsonKey
(
name:
'sub_title'
)
final
String
?
subTitle
;
final
String
?
title
;
@JsonKey
(
name:
'publish_at_date'
)
final
String
?
publishAtDate
;
final
String
?
chapeau
;
NewsItemModel
({
this
.
thumbnail
,
this
.
pageId
,
this
.
subTitle
,
this
.
title
,
this
.
publishAtDate
,
this
.
chapeau
,
});
factory
NewsItemModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$NewsItemModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$NewsItemModelToJson
(
this
);
}
\ No newline at end of file
lib/screen/news/models/news_item_model.g.dart
0 → 100644
View file @
fda33894
// GENERATED CODE - DO NOT MODIFY BY HAND
part of
'news_item_model.dart'
;
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
NewsItemModel
_$NewsItemModelFromJson
(
Map
<
String
,
dynamic
>
json
)
=>
NewsItemModel
(
thumbnail:
json
[
'thumbnail'
]
as
String
?,
pageId:
json
[
'page_id'
]
as
String
?,
subTitle:
json
[
'sub_title'
]
as
String
?,
title:
json
[
'title'
]
as
String
?,
publishAtDate:
json
[
'publish_at_date'
]
as
String
?,
chapeau:
json
[
'chapeau'
]
as
String
?,
);
Map
<
String
,
dynamic
>
_$NewsItemModelToJson
(
NewsItemModel
instance
)
=>
<
String
,
dynamic
>{
'thumbnail'
:
instance
.
thumbnail
,
'page_id'
:
instance
.
pageId
,
'sub_title'
:
instance
.
subTitle
,
'title'
:
instance
.
title
,
'publish_at_date'
:
instance
.
publishAtDate
,
'chapeau'
:
instance
.
chapeau
,
};
lib/screen/news/news_item_widget.dart
0 → 100644
View file @
fda33894
import
'package:flutter/material.dart'
;
import
'package:mypoint_flutter_app/widgets/image_loader.dart'
;
import
'../faqs/faqs_model.dart'
;
class
PageItemWidget
extends
StatelessWidget
{
final
PageItemModel
item
;
const
PageItemWidget
({
super
.
key
,
required
this
.
item
});
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
margin:
const
EdgeInsets
.
symmetric
(
vertical:
6
,
horizontal:
16
),
padding:
const
EdgeInsets
.
all
(
12
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
),
boxShadow:
[
BoxShadow
(
color:
Colors
.
black26
,
blurRadius:
3
,
offset:
const
Offset
(
2
,
1
))],
),
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
// Nội dung văn bản
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
item
.
title
??
''
,
style:
const
TextStyle
(
fontWeight:
FontWeight
.
bold
,
fontSize:
14
)),
const
SizedBox
(
height:
4
),
Text
(
item
.
chapeau
??
''
,
style:
const
TextStyle
(
fontSize:
13
,
color:
Colors
.
black87
)),
],
),
),
const
SizedBox
(
width:
12
),
// Ảnh thumbnail
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
8
),
child:
loadNetworkImage
(
url:
item
.
thumbnail
??
''
,
width:
64
,
height:
64
,
fit:
BoxFit
.
cover
,
placeholderAsset:
"assets/images/bg_default_11.png"
,
),
),
],
),
);
}
}
lib/screen/news/news_list_screen.dart
0 → 100644
View file @
fda33894
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'../../../shared/router_gage.dart'
;
import
'../../../widgets/custom_empty_widget.dart'
;
import
'../../../widgets/custom_navigation_bar.dart'
;
import
'news_item_widget.dart'
;
import
'news_list_viewmodel.dart'
;
class
NewsListScreen
extends
StatefulWidget
{
const
NewsListScreen
({
super
.
key
});
@override
_NewsListScreenState
createState
()
=>
_NewsListScreenState
();
}
class
_NewsListScreenState
extends
State
<
NewsListScreen
>
{
late
final
NewsListViewModel
_viewModel
=
Get
.
put
(
NewsListViewModel
());
@override
void
initState
()
{
super
.
initState
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
backgroundColor:
Colors
.
grey
[
50
],
appBar:
CustomNavigationBar
(
title:
"MyPoint có gì hot?"
),
body:
Column
(
children:
[
Expanded
(
child:
Obx
(()
{
if
(
_viewModel
.
newsList
.
value
.
isEmpty
)
{
return
const
Center
(
child:
EmptyWidget
());
}
return
RefreshIndicator
(
onRefresh:
()
=>
_viewModel
.
getNewsList
(
isRefresh:
true
),
child:
ListView
.
builder
(
physics:
const
AlwaysScrollableScrollPhysics
(),
itemCount:
_viewModel
.
newsList
.
length
,
itemBuilder:
(
context
,
index
)
{
if
(
index
>=
_viewModel
.
newsList
.
length
)
{
_viewModel
.
getNewsList
(
isRefresh:
false
);
return
const
Center
(
child:
Padding
(
padding:
EdgeInsets
.
all
(
16
),
child:
CircularProgressIndicator
()),
);
}
final
news
=
_viewModel
.
newsList
.
value
[
index
];
return
GestureDetector
(
onTap:
()
{
Get
.
toNamed
(
campaignDetailScreen
,
arguments:
{
"id"
:
news
.
pageId
??
""
});
},
child:
PageItemWidget
(
item:
news
),
);
},
),
);
}),
),
],
),
);
}
}
lib/screen/news/news_list_viewmodel.dart
0 → 100644
View file @
fda33894
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_request.dart'
;
import
'../../base/restful_api_viewmodel.dart'
;
import
'../../preference/data_preference.dart'
;
import
'../faqs/faqs_model.dart'
;
class
NewsListViewModel
extends
RestfulApiViewModel
{
String
folderUri
;
var
newsList
=
<
PageItemModel
>[].
obs
;
var
isLoading
=
false
.
obs
;
var
_canLoadMore
=
true
;
int
start
=
0
;
int
limit
=
20
;
NewsListViewModel
({
this
.
folderUri
=
"TIN-TUC"
});
@override
onInit
()
{
super
.
onInit
();
getNewsList
();
}
Future
<
void
>
getNewsList
({
bool
isRefresh
=
false
})
async
{
if
(
isLoading
.
value
)
return
;
if
(!
isRefresh
&&
!
_canLoadMore
)
return
;
showLoading
();
isLoading
(
true
);
final
body
=
{
"folder_uri"
:
folderUri
,
"start"
:
isRefresh
?
0
:
newsList
.
length
,
"limit"
:
limit
,
};
client
.
websiteFolderGetPageList
(
body
).
then
((
value
)
{
hideLoading
();
isLoading
(
false
);
_canLoadMore
=
value
.
data
?.
items
?.
length
==
limit
;
if
(
isRefresh
)
{
newsList
.
value
.
clear
();
}
newsList
.
addAll
(
value
.
data
?.
items
??
[]);
});
}
}
\ No newline at end of file
lib/screen/onboarding/onboarding_screen.dart
View file @
fda33894
...
...
@@ -189,7 +189,7 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
const
TextSpan
(
text:
"Bằng việc tiếp tục, bạn đã đọc và đồng ý với "
),
WidgetSpan
(
child:
GestureDetector
(
onTap:
()
=>
Get
.
to
(
C
ampaignDetailScreen
(
type:
DetailPageRuleType
.
termsOfUse
)
),
onTap:
()
=>
Get
.
to
Named
(
c
ampaignDetailScreen
,
arguments:
{
"
type
"
:
DetailPageRuleType
.
termsOfUse
}
),
child:
const
Text
(
"Điều khoản sử dụng"
,
style:
TextStyle
(
...
...
lib/screen/pageDetail/campaign_detail_screen.dart
View file @
fda33894
...
...
@@ -3,23 +3,20 @@ import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import
'package:get/get.dart'
;
import
'../../base/base_screen.dart'
;
import
'../../base/basic_state.dart'
;
import
'../../directional/directional_action_type.dart'
;
import
'../../directional/directional_screen.dart'
;
import
'../../extensions/string_extension.dart'
;
// tuỳ dự án
import
'../../extensions/string_extension.dart'
;
import
'../../resouce/base_color.dart'
;
import
'../../shared/router_gage.dart'
;
import
'../../widgets/back_button.dart'
;
import
'../../widgets/network_image_with_aspect_ratio.dart'
;
// widget custom
import
'../../widgets/network_image_with_aspect_ratio.dart'
;
import
'campaign_detail_viewmodel.dart'
;
import
'campaign_item_page_widget.dart'
;
import
'model/campaign_detail_item_model.dart'
;
import
'model/campaign_detail_model.dart'
;
import
'model/detail_page_rule_type.dart'
;
import
'model/media_type_item_campaign.dart'
;
class
CampaignDetailScreen
extends
BaseScreen
{
final
DetailPageRuleType
?
type
;
final
String
?
pageId
;
const
CampaignDetailScreen
({
super
.
key
,
this
.
type
,
this
.
pageId
});
const
CampaignDetailScreen
({
super
.
key
});
@override
State
<
CampaignDetailScreen
>
createState
()
=>
_CampaignDetailScreenState
();
...
...
@@ -38,7 +35,14 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
});
}
});
_viewModel
.
fetchData
(
widget
.
type
,
widget
.
pageId
);
DetailPageRuleType
?
type
;
String
?
pageId
;
final
args
=
Get
.
arguments
;
if
(
args
is
Map
)
{
type
=
args
[
'type'
];
pageId
=
args
[
'id'
];
}
_viewModel
.
fetchData
(
type
,
pageId
);
}
@override
...
...
@@ -70,10 +74,10 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
color:
Colors
.
grey
.
shade200
,
child:
const
Center
(
child:
CircularProgressIndicator
()),
),
errorWidget:
Image
.
asset
(
"assets/images/bg_
header_campaign_
default.png"
,
fit:
BoxFit
.
cover
),
errorWidget:
Image
.
asset
(
"assets/images/bg_default
_169
.png"
,
fit:
BoxFit
.
cover
),
)
else
Image
.
asset
(
"assets/images/bg_
header_campaign_
default.png"
,
fit:
BoxFit
.
cover
),
Image
.
asset
(
"assets/images/bg_default
_169
.png"
,
fit:
BoxFit
.
cover
),
Transform
.
translate
(
offset:
const
Offset
(
0
,
-
32
),
child:
Container
(
...
...
@@ -90,7 +94,7 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
Text
(
publishDate
,
style:
const
TextStyle
(
color:
Colors
.
grey
,
fontSize:
14
)),
const
SizedBox
(
height:
8
),
Text
(
title
,
style:
const
TextStyle
(
fontSize:
20
,
fontWeight:
FontWeight
.
bold
)),
const
SizedBox
(
height:
16
),
const
SizedBox
(
height:
8
),
_buildItems
(
items
),
const
SizedBox
(
height:
8
),
],
...
...
@@ -100,11 +104,7 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
],
),
),
Positioned
(
top:
MediaQuery
.
of
(
context
).
padding
.
top
+
8
,
left:
8
,
child:
CustomBackButton
(),
),
Positioned
(
top:
MediaQuery
.
of
(
context
).
padding
.
top
+
8
,
left:
8
,
child:
CustomBackButton
()),
if
(
buttonOn
==
"1"
)
_bottomButton
(
pageDetail
),
],
);
...
...
@@ -127,20 +127,25 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
child:
Center
(
child:
Column
(
children:
[
SizedBox
(
height:
12
,
),
SizedBox
(
height:
12
),
ElevatedButton
(
onPressed:
()
{
DirectionalScreen
(
clickActionType:
pageDetail
?.
buttonClickActionType
,
clickActionParam:
pageDetail
?.
buttonClickActionParam
).
begin
();
print
(
"pageDetail?.directionalScreen"
);
print
(
pageDetail
?.
directionalScreen
);
print
(
pageDetail
?.
buttonClickActionType
);
print
(
pageDetail
?.
buttonClickActionParam
);
pageDetail
?.
directionalScreen
?.
begin
();
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
parseHexColor
(
buttonColor
),
minimumSize:
const
Size
.
fromHeight
(
48
),
),
child:
Text
(
buttonName
,
style:
TextStyle
(
color:
parseHexColor
(
buttonTextColor
),
fontWeight:
FontWeight
.
bold
)),
child:
Text
(
buttonName
,
style:
TextStyle
(
color:
parseHexColor
(
buttonTextColor
),
fontWeight:
FontWeight
.
bold
),
),
),
SizedBox
(
height:
MediaQuery
.
of
(
context
).
padding
.
bottom
)
SizedBox
(
height:
MediaQuery
.
of
(
context
).
padding
.
bottom
)
,
],
),
),
...
...
@@ -151,8 +156,8 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
Widget
_buildItems
(
List
<
CampaignDetailItemModel
>
items
)
{
List
<
Widget
>
widgets
=
[];
for
(
var
item
in
items
)
{
final
mediaType
=
item
.
mediaType
?.
value
??
""
;
//
toLowerCase()
?? ""
;
if
(
mediaType
==
MediaTypeItemCampaign
.
image
.
name
)
{
final
mediaType
=
(
item
.
mediaType
??
""
).
toLowerCase
();
if
(
mediaType
==
MediaTypeItemCampaign
.
image
.
key
||
mediaType
==
MediaTypeItemCampaign
.
imageLink
.
key
)
{
if
(
item
.
contentText
!=
null
&&
item
.
contentText
!.
isNotEmpty
)
{
widgets
.
add
(
Padding
(
...
...
@@ -169,10 +174,27 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
),
);
}
}
else
if
(
mediaType
==
MediaTypeItemCampaign
.
text
.
name
)
{
}
else
if
(
mediaType
==
MediaTypeItemCampaign
.
text
.
key
)
{
if
(
item
.
contentText
!=
null
&&
item
.
contentText
!.
isNotEmpty
)
{
widgets
.
add
(
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
16
),
child:
HtmlWidget
(
item
.
contentText
!)));
}
}
else
if
(
mediaType
==
MediaTypeItemCampaign
.
pageLink
.
key
)
{
if
(
item
.
pages
?.
isNotEmpty
==
true
)
{
for
(
var
item
in
item
.
pages
!)
{
widgets
.
add
(
CampaignItemPageWidget
(
itemPageCampaign:
item
,
onTap:
()
async
{
Get
.
offNamed
(
campaignDetailScreen
,
arguments:
{
"id"
:
item
.
pageId
},
preventDuplicates:
false
,
);
},
),
);
}
}
}
}
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
stretch
,
children:
widgets
);
...
...
lib/screen/pageDetail/campaign_item_page_widget.dart
0 → 100644
View file @
fda33894
import
'package:flutter/material.dart'
;
import
'model/campaign_detail_item_model.dart'
;
class
CampaignItemPageWidget
extends
StatelessWidget
{
final
CampaignItemPageModel
itemPageCampaign
;
final
VoidCallback
?
onTap
;
const
CampaignItemPageWidget
({
super
.
key
,
required
this
.
itemPageCampaign
,
this
.
onTap
});
@override
Widget
build
(
BuildContext
context
)
{
final
hasImage
=
itemPageCampaign
.
thumbnail
!=
null
&&
itemPageCampaign
.
thumbnail
!.
isNotEmpty
;
return
GestureDetector
(
onTap:
onTap
,
child:
Container
(
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
2
,
vertical:
8
),
padding:
const
EdgeInsets
.
all
(
12
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
),
boxShadow:
[
BoxShadow
(
blurRadius:
6
,
offset:
const
Offset
(
2
,
2
),
color:
Colors
.
black
.
withOpacity
(
0.1
),
)
],
),
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Expanded
(
child:
_buildTextContent
(),
),
if
(
hasImage
)
const
SizedBox
(
width:
12
),
if
(
hasImage
)
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
8
),
child:
Image
.
network
(
itemPageCampaign
.
thumbnail
!,
width:
60
,
height:
60
,
fit:
BoxFit
.
cover
,
errorBuilder:
(
_
,
__
,
___
)
=>
const
Icon
(
Icons
.
image_not_supported
),
),
),
],
),
),
);
}
Widget
_buildTextContent
()
{
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
if
(
itemPageCampaign
.
title
!=
null
)
Text
(
itemPageCampaign
.
title
!,
style:
const
TextStyle
(
fontWeight:
FontWeight
.
bold
,
fontSize:
14
,
),
),
const
SizedBox
(
height:
4
),
if
(
itemPageCampaign
.
subTitle
!=
null
&&
itemPageCampaign
.
subTitle
!.
isNotEmpty
)
Text
(
itemPageCampaign
.
subTitle
!,
style:
const
TextStyle
(
fontSize:
13
),
),
if
(
itemPageCampaign
.
chapeau
!=
null
&&
itemPageCampaign
.
chapeau
!.
isNotEmpty
)
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
4
),
child:
Text
(
itemPageCampaign
.
chapeau
!,
style:
const
TextStyle
(
fontSize:
13
,
color:
Colors
.
black87
),
),
),
],
);
}
}
lib/screen/pageDetail/model/campaign_detail_item_model.dart
View file @
fda33894
...
...
@@ -10,21 +10,60 @@ class CampaignDetailItemModel {
@JsonKey
(
name:
"content_text"
)
String
?
contentText
;
@JsonKey
(
name:
"media_type"
,
fromJson:
MediaTypeItemCampaign
.
fromString
,
toJson:
_mediaTypeToJson
)
MediaTypeItemCampaign
?
mediaType
;
@JsonKey
(
name:
"media_type"
)
String
?
mediaType
;
List
<
CampaignItemPageModel
>?
pages
;
CampaignDetailItemModel
({
this
.
contentCaption
,
this
.
contentText
,
this
.
mediaType
,
this
.
pages
,
});
factory
CampaignDetailItemModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$CampaignDetailItemModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$CampaignDetailItemModelToJson
(
this
);
/// 🎯 Helper để convert Enum sang String khi serialize JSON
static
String
?
_mediaTypeToJson
(
MediaTypeItemCampaign
?
type
)
=>
type
?.
toJson
();
}
class
CampaignItemPageModel
{
final
String
?
publishAtDate
;
final
String
?
title
;
final
String
?
pageId
;
final
String
?
chapeau
;
final
String
?
subTitle
;
final
String
?
thumbnail
;
const
CampaignItemPageModel
({
this
.
publishAtDate
,
this
.
title
,
this
.
pageId
,
this
.
chapeau
,
this
.
subTitle
,
this
.
thumbnail
,
});
factory
CampaignItemPageModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
CampaignItemPageModel
(
publishAtDate:
json
[
'publish_at_date'
]
as
String
?,
title:
json
[
'title'
]
as
String
?,
pageId:
json
[
'page_id'
]
as
String
?,
chapeau:
json
[
'chapeau'
]
as
String
?,
subTitle:
json
[
'sub_title'
]
as
String
?,
thumbnail:
json
[
'thumbnail'
]
as
String
?,
);
}
Map
<
String
,
dynamic
>
toJson
()
{
return
{
'publish_at_date'
:
publishAtDate
,
'title'
:
title
,
'page_id'
:
pageId
,
'chapeau'
:
chapeau
,
'sub_title'
:
subTitle
,
'thumbnail'
:
thumbnail
,
};
}
}
lib/screen/pageDetail/model/campaign_detail_item_model.g.dart
View file @
fda33894
...
...
@@ -11,7 +11,13 @@ CampaignDetailItemModel _$CampaignDetailItemModelFromJson(
)
=>
CampaignDetailItemModel
(
contentCaption:
json
[
'content_caption'
]
as
String
?,
contentText:
json
[
'content_text'
]
as
String
?,
mediaType:
MediaTypeItemCampaign
.
fromString
(
json
[
'media_type'
]
as
String
?),
mediaType:
json
[
'media_type'
]
as
String
?,
pages:
(
json
[
'pages'
]
as
List
<
dynamic
>?)
?.
map
(
(
e
)
=>
CampaignItemPageModel
.
fromJson
(
e
as
Map
<
String
,
dynamic
>),
)
.
toList
(),
);
Map
<
String
,
dynamic
>
_$CampaignDetailItemModelToJson
(
...
...
@@ -19,5 +25,6 @@ Map<String, dynamic> _$CampaignDetailItemModelToJson(
)
=>
<
String
,
dynamic
>{
'content_caption'
:
instance
.
contentCaption
,
'content_text'
:
instance
.
contentText
,
'media_type'
:
CampaignDetailItemModel
.
_mediaTypeToJson
(
instance
.
mediaType
),
'media_type'
:
instance
.
mediaType
,
'pages'
:
instance
.
pages
,
};
lib/screen/pageDetail/model/campaign_detail_model.dart
View file @
fda33894
import
'package:json_annotation/json_annotation.dart'
;
import
'package:mypoint_flutter_app/directional/directional_screen.dart'
;
import
'campaign_detail_item_model.dart'
;
part
'campaign_detail_model.g.dart'
;
...
...
@@ -36,6 +37,13 @@ class CampaignDetailModel {
this
.
items
,
});
DirectionalScreen
?
get
directionalScreen
{
return
DirectionalScreen
.
build
(
clickActionType:
buttonClickActionType
,
clickActionParam:
buttonClickActionParam
,
);
}
factory
CampaignDetailModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$CampaignDetailModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$CampaignDetailModelToJson
(
this
);
}
...
...
lib/screen/pageDetail/model/media_type_item_campaign.dart
View file @
fda33894
import
'package:get/get.dart'
;
import
'package:json_annotation/json_annotation.dart'
;
/// 🎯 Enum ánh xạ với String
enum
MediaTypeItemCampaign
{
image
(
"image"
),
text
(
"text"
);
image
,
imageLink
,
pageLink
,
text
,
}
final
String
value
;
const
MediaTypeItemCampaign
(
this
.
value
);
/// 🎯 Chuyển từ String sang Enum
static
MediaTypeItemCampaign
?
fromString
(
String
?
value
)
{
return
MediaTypeItemCampaign
.
values
.
firstWhereOrNull
((
e
)
=>
e
.
value
==
value
);
extension
MediaTypeItemCampaignExtension
on
MediaTypeItemCampaign
{
String
get
key
{
switch
(
this
)
{
case
MediaTypeItemCampaign
.
image
:
return
"image"
;
case
MediaTypeItemCampaign
.
imageLink
:
return
"image_link"
;
case
MediaTypeItemCampaign
.
pageLink
:
return
"page_link"
;
case
MediaTypeItemCampaign
.
text
:
return
"text"
;
}
}
/// 🎯 Chuyển từ Enum sang String
String
toJson
()
=>
value
;
}
\ No newline at end of file
lib/screen/support/support_screen.dart
View file @
fda33894
...
...
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/screen/support/support_item_model.dart'
;
import
'package:mypoint_flutter_app/screen/support/support_screen_viewmodel.dart'
;
import
'package:mypoint_flutter_app/shared/router_gage.dart'
;
import
'package:url_launcher/url_launcher.dart'
;
import
'../../resouce/base_color.dart'
;
...
...
@@ -43,9 +44,9 @@ class _SupportScreenState extends State<SupportScreen> {
case
SupportItemType
.
question
:
Get
.
to
(
FAQScreen
());
case
SupportItemType
.
termsOfUse
:
Get
.
to
(
C
ampaignDetailScreen
(
type:
DetailPageRuleType
.
termsOfUse
)
);
Get
.
to
Named
(
c
ampaignDetailScreen
,
arguments:
{
"
type
"
:
DetailPageRuleType
.
termsOfUse
}
);
case
SupportItemType
.
privacyPolicy
:
Get
.
to
(
C
ampaignDetailScreen
(
type:
DetailPageRuleType
.
privacyPolicy
)
);
Get
.
to
Named
(
c
ampaignDetailScreen
,
arguments:
{
"
type
"
:
DetailPageRuleType
.
privacyPolicy
}
);
}
}
...
...
lib/screen/voucher/detail/voucher_detail_screen.dart
View file @
fda33894
...
...
@@ -43,7 +43,11 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
int
?
customerProductId
;
final
args
=
Get
.
arguments
;
if
(
args
is
Map
)
{
if
(
args
is
int
)
{
productId
=
args
;
}
else
if
(
args
is
String
)
{
productId
=
int
.
tryParse
(
args
);
}
else
if
(
args
is
Map
)
{
productId
=
args
[
'productId'
];
customerProductId
=
args
[
'customerProductId'
];
}
...
...
lib/screen/voucher/models/my_product_status_type.dart
View file @
fda33894
...
...
@@ -37,6 +37,4 @@ enum MyProductStatusType {
return
"voucher.expired_voucher_empty"
;
}
}
// Nếu bạn cần thêm color hoặc các property khác thì viết tiếp ở đây
}
\ No newline at end of file
lib/screen/voucher/sub_widget/voucher_section_title.dart
View file @
fda33894
import
'package:flutter/material.dart'
;
class
Vouch
erSectionTitle
extends
StatelessWidget
{
class
Head
erSectionTitle
extends
StatelessWidget
{
final
String
title
;
final
VoidCallback
?
onViewAll
;
// 👈 Optional
const
Vouch
erSectionTitle
({
const
Head
erSectionTitle
({
super
.
key
,
required
this
.
title
,
this
.
onViewAll
,
// 👈 Nếu null thì không hiển thị button
...
...
lib/screen/voucher/voucher_tab_screen.dart
View file @
fda33894
...
...
@@ -50,16 +50,16 @@ class VoucherTabScreen extends StatelessWidget {
child:
ListView
(
physics:
const
AlwaysScrollableScrollPhysics
(),
children:
[
const
Vouch
erSectionTitle
(
title:
'Ưu đãi từ nhà mạng'
),
const
Head
erSectionTitle
(
title:
'Ưu đãi từ nhà mạng'
),
const
VoucherActionMenu
(),
Vouch
erSectionTitle
(
Head
erSectionTitle
(
title:
'Săn ưu đãi'
,
onViewAll:
()
{
Get
.
toNamed
(
vouchersScreen
,
arguments:
{
"isHotProduct"
:
true
});
},
),
VoucherItemGrid
(
items:
viewModel
.
hotProducts
),
const
Vouch
erSectionTitle
(
title:
'Tất cả ưu đãi'
),
const
Head
erSectionTitle
(
title:
'Tất cả ưu đãi'
),
VoucherItemList
(
items:
viewModel
.
allProducts
),
if
(
viewModel
.
isLoadMore
.
value
)
const
Padding
(
...
...
lib/screen/vplay_game_center/vplay_game_center_screen.dart
0 → 100644
View file @
fda33894
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/widgets/image_loader.dart'
;
import
'../../shared/router_gage.dart'
;
import
'../../widgets/custom_navigation_bar.dart'
;
import
'../faqs/faqs_model.dart'
;
import
'../news/news_list_viewmodel.dart'
;
class
VplayGameCenterScreen
extends
StatelessWidget
{
const
VplayGameCenterScreen
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
final
viewModel
=
Get
.
put
(
NewsListViewModel
(
folderUri:
"GAME-VPLAY"
));
final
width
=
MediaQuery
.
of
(
context
).
size
.
width
;
final
space
=
12.0
;
final
itemWidth
=
(
width
-
space
*
3
)
/
2
;
return
Scaffold
(
appBar:
CustomNavigationBar
(
title:
"Trung tâm trò chơi"
),
body:
Obx
(()
{
final
items
=
viewModel
.
newsList
.
value
;
return
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
GridView
.
builder
(
itemCount:
items
.
length
,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount
(
crossAxisCount:
2
,
crossAxisSpacing:
space
,
mainAxisSpacing:
space
,
childAspectRatio:
itemWidth
/(
itemWidth
+
60
),
),
itemBuilder:
(
context
,
index
)
{
final
item
=
items
[
index
];
return
GestureDetector
(
onTap:
()
{
Get
.
toNamed
(
campaignDetailScreen
,
arguments:
{
"id"
:
item
.
pageId
??
""
});
},
child:
_buildVplayGameItem
(
item
,
itemWidth
),
);
},
),
);
}),
);
}
Widget
_buildVplayGameItem
(
PageItemModel
item
,
double
itemWidth
)
{
return
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
20
),
child:
loadNetworkImage
(
url:
item
.
thumbnail
??
''
,
width:
itemWidth
,
height:
itemWidth
,
fit:
BoxFit
.
cover
,
placeholderAsset:
"assets/images/bg_default_11.png"
,
),
),
const
SizedBox
(
height:
4
),
Text
(
item
.
title
??
''
,
textAlign:
TextAlign
.
center
,
maxLines:
2
,
style:
const
TextStyle
(
fontSize:
15
,
fontWeight:
FontWeight
.
bold
),
),
],
);
}
}
Prev
1
2
3
4
5
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment