Commit b3c72190 authored by DatHV's avatar DatHV
Browse files

update invite friend.

update config build
parent 6b980613
...@@ -97,6 +97,10 @@ android { ...@@ -97,6 +97,10 @@ android {
buildFeatures { buildFeatures {
buildConfig = true buildConfig = true
} }
lint {
disable += setOf("NullSafeMutableLiveData")
abortOnError = false
}
// --- Flavors giữ nguyên ENV của bạn --- // --- Flavors giữ nguyên ENV của bạn ---
flavorDimensions += "environment" flavorDimensions += "environment"
......
...@@ -8,3 +8,5 @@ ...@@ -8,3 +8,5 @@
# Retain Kotlin metadata # Retain Kotlin metadata
-keepclassmembers class kotlin.Metadata { *; } -keepclassmembers class kotlin.Metadata { *; }
# Disable Lifecycle lint crash
-dontwarn androidx.lifecycle.MutableLiveData
assets/images/ic_logo.png

1.64 KB | W: | H:

assets/images/ic_logo.png

7.02 KB | W: | H:

assets/images/ic_logo.png
assets/images/ic_logo.png
assets/images/ic_logo.png
assets/images/ic_logo.png
  • 2-up
  • Swipe
  • Onion skin
import 'dart:async';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -14,6 +15,8 @@ import '../core/services/deep_link_service.dart'; ...@@ -14,6 +15,8 @@ import '../core/services/deep_link_service.dart';
/// Main app initialization and setup /// Main app initialization and setup
class AppInitializer { class AppInitializer {
static const Duration _asyncInitTimeout = Duration(seconds: 8);
/// Initialize all core app features /// Initialize all core app features
static Future<void> initialize() async { static Future<void> initialize() async {
debugPrint('🚀 Initializing app...'); debugPrint('🚀 Initializing app...');
...@@ -21,9 +24,21 @@ class AppInitializer { ...@@ -21,9 +24,21 @@ class AppInitializer {
await DataPreference.instance.init(); await DataPreference.instance.init();
DioHttpService(); DioHttpService();
Get.put(HeaderThemeController(), permanent: true); Get.put(HeaderThemeController(), permanent: true);
await _initializeFirebase(); await _runStep(
await _initializeWebFeatures(); label: 'Firebase',
await DeepLinkService().initialize(); 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'); debugPrint('✅ App initialization completed');
} }
...@@ -70,6 +85,25 @@ class AppInitializer { ...@@ -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 /// Setup post-initialization callbacks
static void setupPostInitCallbacks() { static void setupPostInitCallbacks() {
try { try {
......
...@@ -80,6 +80,9 @@ class APIPaths {//sandbox ...@@ -80,6 +80,9 @@ class APIPaths {//sandbox
static const String affiliateBrandGetDetail = "/affiliateBrandGetDetail/1.0.0"; static const String affiliateBrandGetDetail = "/affiliateBrandGetDetail/1.0.0";
static const String campaignInviteFriend = "/campaign/api/v3.0/invite-friend"; static const String campaignInviteFriend = "/campaign/api/v3.0/invite-friend";
static const String inviteFriendCampaigns = "/campaign/api/v3.0/invite-friend/campaigns"; 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 phoneInviteFriend = "/campaign/api/v3.0/invite-friend/invite";
static const String rewardOpportunityGetList = "/rewardOpportunityGetList/1.0.0"; static const String rewardOpportunityGetList = "/rewardOpportunityGetList/1.0.0";
static const String rewardOpportunityOpenRequest = "/rewardOpportunityOpenRequest/1.0.0"; static const String rewardOpportunityOpenRequest = "/rewardOpportunityOpenRequest/1.0.0";
......
class Constants { 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 otpTtl = 180;
static var directionInApp = "IN-APP"; static var directionInApp = "IN-APP";
static var phoneNumberCount = 10; static var phoneNumberCount = 10;
static const timeoutSeconds = 30; static const timeoutSeconds = 30;
static const appStoreId = '1495923300'; static const appStoreId = '1495923300';
static const double webTopPadding = 24.0;
} }
class ErrorCodes { class ErrorCodes {
......
...@@ -19,6 +19,10 @@ import '../../features/health_book/health_book_screen.dart' ...@@ -19,6 +19,10 @@ import '../../features/health_book/health_book_screen.dart'
deferred as health_book show HealthBookScreen; deferred as health_book show HealthBookScreen;
import '../../features/invite_friend_campaign/invite_friend_campaign_screen.dart' import '../../features/invite_friend_campaign/invite_friend_campaign_screen.dart'
deferred as invite_friend show InviteFriendCampaignScreen; 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' import '../../features/quiz_campaign/quiz_campaign_screen.dart'
deferred as survey_question show SurveyQuestionScreen; deferred as survey_question show SurveyQuestionScreen;
import '../../features/device_manager/device_manager_screen.dart' import '../../features/device_manager/device_manager_screen.dart'
...@@ -334,6 +338,32 @@ class InviteFriendDeferredScreen extends StatelessWidget { ...@@ -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 { class OrderMenuDeferredScreen extends StatelessWidget {
const OrderMenuDeferredScreen({super.key}); const OrderMenuDeferredScreen({super.key});
......
...@@ -362,6 +362,32 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient { ...@@ -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 { Future<BaseResponseModel<CheckInDataModel>> rewardOpportunityGetList() async {
String? token = DataPreference.instance.token ?? ""; String? token = DataPreference.instance.token ?? "";
final body = {"access_token": token, 'number_day': '7'}; final body = {"access_token": token, 'number_day': '7'};
...@@ -379,7 +405,6 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient { ...@@ -379,7 +405,6 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient {
} }
Future<BaseResponseModel<TransactionHistoryResponse>> getTransactionHistoryResponse(Json body) async { Future<BaseResponseModel<TransactionHistoryResponse>> getTransactionHistoryResponse(Json body) async {
await Future.delayed(Duration(milliseconds: 3500));
return requestNormal(APIPaths.getTransactionOrderHistory, Method.GET, body, (data) { return requestNormal(APIPaths.getTransactionOrderHistory, Method.GET, body, (data) {
return TransactionHistoryResponse.fromJson(data as Json); return TransactionHistoryResponse.fromJson(data as Json);
}); });
......
...@@ -22,7 +22,7 @@ class DeepLinkService { ...@@ -22,7 +22,7 @@ class DeepLinkService {
_initialized = true; _initialized = true;
debugPrint('🔗 Initializing DeepLinkService...'); debugPrint('🔗 Initializing DeepLinkService...');
await _initBranchSdk(); // await _initBranchSdk();
await _handleInitialLink(); await _handleInitialLink();
_listenLinkStream(); _listenLinkStream();
} }
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/features/home/custom_widget/header_home_widget.dart'; import '../../features/home/custom_widget/header_home_widget.dart';
import 'package:mypoint_flutter_app/features/home/custom_widget/product_grid_widget.dart'; import '../../features/home/custom_widget/product_grid_widget.dart';
import 'package:mypoint_flutter_app/features/pipi/pipi_detail_screen.dart'; import '../../features/pipi/pipi_detail_screen.dart';
import 'package:mypoint_flutter_app/features/voucher/models/product_model.dart'; import '../../features/voucher/models/product_model.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart'; import '../../shared/router_gage.dart';
import '../../shared/widgets/base_view/base_screen.dart'; import '../../shared/widgets/base_view/base_screen.dart';
import '../../shared/widgets/base_view/basic_state.dart'; import '../../shared/widgets/base_view/basic_state.dart';
import '../../app/routing/directional_action_type.dart'; import '../../app/routing/directional_action_type.dart';
......
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),
),
);
}
}
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(),
);
}
}
...@@ -2,10 +2,11 @@ import 'package:flutter/material.dart'; ...@@ -2,10 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:get/get.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_empty_widget.dart';
import 'package:mypoint_flutter_app/shared/widgets/custom_toast_message.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.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/base_screen.dart';
import '../../shared/widgets/base_view/basic_state.dart'; import '../../shared/widgets/base_view/basic_state.dart';
import '../../core/theme/base_color.dart'; import '../../core/theme/base_color.dart';
...@@ -65,9 +66,16 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr ...@@ -65,9 +66,16 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildHeaderInfo(), if (viewModel.inviteFriendDetail.value != null) ...[
SizedBox(height: 56), _buildHeaderInfo(),
_buildInviteCardBox(), SizedBox(height: 56),
_buildInviteCardBox(),
],
if (viewModel.inviteFriendDetail.value == null) ...[
SizedBox(height: 16),
EmptyWidget(isLoading: viewModel.isLoading.value),
SizedBox(height: 16),
],
_buildContactCardBox(), _buildContactCardBox(),
_buildCampaignBox(), _buildCampaignBox(),
Container(color: Colors.grey.shade100, child: SizedBox(height: 64)), Container(color: Colors.grey.shade100, child: SizedBox(height: 64)),
...@@ -109,7 +117,11 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr ...@@ -109,7 +117,11 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
left: 16, left: 16,
right: 16, right: 16,
child: GestureDetector( child: GestureDetector(
onTap: () {}, onTap: () {
Get.toNamed(referralCodeInviteFriendScreen, arguments: {
'title': viewModel.inviteFriendDetail.value?.name ?? 'Mã giới thiệu'
});
},
child: Container( child: Container(
height: 42, height: 42,
decoration: BoxDecoration( decoration: BoxDecoration(
...@@ -120,7 +132,7 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr ...@@ -120,7 +132,7 @@ class _InviteFriendCampaignScreenState extends BaseState<InviteFriendCampaignScr
child: Row( child: Row(
children: [ children: [
const SizedBox(width: 8), const SizedBox(width: 8),
Icon(Icons.person, color: Colors.black54), Icon(Icons.person_add_alt_sharp, color: Colors.black54),
const SizedBox(width: 8), const SizedBox(width: 8),
const Text( const Text(
'Nhập mã giới thiệu bạn bè ở đây', 'Nhập mã giới thiệu bạn bè ở đây',
......
...@@ -50,10 +50,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel { ...@@ -50,10 +50,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel {
onSuccess: (data, _) { onSuccess: (data, _) {
campaignDetail.value = data; campaignDetail.value = data;
}, },
onFailure: (msg, _, _) async { withLoading: false,
// Silent failure for this optional call
},
showAppNavigatorDialog: false,
); );
} }
} }
import 'package:mypoint_flutter_app/shared/navigation/directional_screen.dart';
class RewardInviteItemModel { class RewardInviteItemModel {
String? type; String? type;
String? icon; String? icon;
...@@ -67,6 +69,42 @@ class InviteFriendDetailModel { ...@@ -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 { class CampaignInviteFriendItemModel {
String? id; String? id;
String? name; String? name;
...@@ -77,6 +115,8 @@ class CampaignInviteFriendItemModel { ...@@ -77,6 +115,8 @@ class CampaignInviteFriendItemModel {
RewardInviteItemModel? reward; RewardInviteItemModel? reward;
String? rules; String? rules;
String? inviteLink; String? inviteLink;
String? rewardContent;
CampaignStepsInviteFriendDetailModel? steps;
int? rulesId; int? rulesId;
CampaignInviteFriendItemModel({ CampaignInviteFriendItemModel({
...@@ -89,6 +129,8 @@ class CampaignInviteFriendItemModel { ...@@ -89,6 +129,8 @@ class CampaignInviteFriendItemModel {
this.reward, this.reward,
this.rules, this.rules,
this.inviteLink, this.inviteLink,
this.rewardContent,
this.steps,
this.rulesId, this.rulesId,
}); });
...@@ -105,6 +147,10 @@ class CampaignInviteFriendItemModel { ...@@ -105,6 +147,10 @@ class CampaignInviteFriendItemModel {
: null, : null,
rules: json['rules'], rules: json['rules'],
inviteLink: json['invite_link'], inviteLink: json['invite_link'],
rewardContent: json['reward_content'],
steps: json['steps'] != null
? CampaignStepsInviteFriendDetailModel.fromJson(json['steps'])
: null,
rulesId: json['rules_id'], rulesId: json['rules_id'],
); );
} }
...@@ -129,7 +175,6 @@ class CampaignInviteFriendDetail { ...@@ -129,7 +175,6 @@ class CampaignInviteFriendDetail {
} }
} }
class InviteFriendResponse { class InviteFriendResponse {
final String? sms; final String? sms;
...@@ -146,4 +191,43 @@ class InviteFriendResponse { ...@@ -146,4 +191,43 @@ class InviteFriendResponse {
'sms': sms, '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
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.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 '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) { void showPopupInviteFriendCode(BuildContext context, String qrString) {
final width = MediaQuery.of(context).size.width; 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( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
...@@ -52,25 +99,53 @@ void showPopupInviteFriendCode(BuildContext context, String qrString) { ...@@ -52,25 +99,53 @@ void showPopupInviteFriendCode(BuildContext context, String qrString) {
children: [ children: [
Image.asset('assets/images/ic_pipi_couple.png', height: 72), Image.asset('assets/images/ic_pipi_couple.png', height: 72),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( if (!kIsWeb) ...[
color: Colors.white, RepaintBoundary(
child: QrImageView( key: qrKey,
data: qrString, child: Container(
version: QrVersions.auto, color: Colors.white,
size: width / 1.7, child: QrImageView(
embeddedImage: const AssetImage('assets/images/ic_logo.png'), data: qrString,
embeddedImageStyle: const QrEmbeddedImageStyle(size: Size(40, 40)), 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), InkWell(
Row( borderRadius: BorderRadius.circular(24),
mainAxisAlignment: MainAxisAlignment.center, onTap: saveQrImage,
children: const [ child: Padding(
Icon(Icons.download_outlined, color: Colors.black54), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
SizedBox(width: 4), child: Row(
Text('Lưu ảnh'), 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 SizedBox(height: 8),
const Text('Mã giới thiệu:'), const Text('Mã giới thiệu:'),
Text( Text(
...@@ -88,3 +163,41 @@ void showPopupInviteFriendCode(BuildContext context, String qrString) { ...@@ -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;
}
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),
),
),
],
),
),
);
},
),
],
),
);
}
}
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);
},
);
}
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/core/utils/extensions/string_extension.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/base_screen.dart';
import '../../shared/widgets/base_view/basic_state.dart'; import '../../shared/widgets/base_view/basic_state.dart';
import '../../shared/widgets/custom_navigation_bar.dart';
import '../../shared/widgets/image_loader.dart'; import '../../shared/widgets/image_loader.dart';
import 'models/notification_detail_model.dart'; import 'models/notification_detail_model.dart';
import 'notification_detail_viewmodel.dart'; import 'notification_detail_viewmodel.dart';
...@@ -57,7 +57,7 @@ class _NotificationDetailScreenState extends BaseState<NotificationDetailScreen> ...@@ -57,7 +57,7 @@ class _NotificationDetailScreenState extends BaseState<NotificationDetailScreen>
@override @override
Widget createBody() { Widget createBody() {
return Scaffold( return Scaffold(
appBar: CustomAppBar.back(title: "Chi tiết thông báo"), appBar: CustomNavigationBar(title: 'Chi tiết thông báo'),
body: Obx( body: Obx(
() => Container( () => Container(
color: Colors.grey.shade100, color: Colors.grey.shade100,
......
...@@ -5,6 +5,7 @@ import 'package:mypoint_flutter_app/shared/navigation/directional_screen.dart'; ...@@ -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/core/utils/extensions/num_extension.dart';
import 'package:mypoint_flutter_app/shared/preferences/data_preference.dart'; import 'package:mypoint_flutter_app/shared/preferences/data_preference.dart';
import 'package:mypoint_flutter_app/shared/preferences/point/point_manager.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/base_screen.dart';
import '../../shared/widgets/base_view/basic_state.dart'; import '../../shared/widgets/base_view/basic_state.dart';
import '../../app/routing/directional_action_type.dart'; import '../../app/routing/directional_action_type.dart';
...@@ -76,11 +77,12 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po ...@@ -76,11 +77,12 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
final name = DataPreference.instance.displayName; final name = DataPreference.instance.displayName;
final level = DataPreference.instance.rankName ?? "Hạng Đồng"; final level = DataPreference.instance.rankName ?? "Hạng Đồng";
final email = DataPreference.instance.profile?.workerSite?.email ?? ""; final email = DataPreference.instance.profile?.workerSite?.email ?? "";
final topWebPadding = kIsWeb ? Constants.webTopPadding : 0.0;
return Container( return Container(
height: width * 163 / 375, height: width * 163 / 375 + topWebPadding,
decoration: BoxDecoration(image: DecorationImage(image: NetworkImage(data.background ?? ""), fit: BoxFit.cover)), decoration: BoxDecoration(image: DecorationImage(image: NetworkImage(data.background ?? ""), fit: BoxFit.cover)),
child: Padding( 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( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment