Commit f714cdcc authored by DatHV's avatar DatHV
Browse files

update logic, flutter refactor

parent cc202df4
......@@ -4,14 +4,11 @@ import 'package:mypoint_flutter_app/preference/data_preference.dart';
import 'package:mypoint_flutter_app/screen/personal/personal_edit_item_model.dart';
import 'package:mypoint_flutter_app/screen/personal/personal_edit_viewmodel.dart';
import 'package:mypoint_flutter_app/screen/personal/personal_gender.dart';
import 'package:mypoint_flutter_app/widgets/custom_app_bar.dart';
import '../../base/base_screen.dart';
import '../../base/basic_state.dart';
import '../../resources/base_color.dart';
import '../../shared/router_gage.dart';
import '../../widgets/alert/data_alert_model.dart';
import '../../widgets/bottom_sheet_helper.dart';
import '../../widgets/time_picker_widget.dart';
import '../../widgets/custom_navigation_bar.dart';
class PersonalEditScreen extends BaseScreen {
const PersonalEditScreen({super.key});
......@@ -52,7 +49,7 @@ class _PersonalEditScreenState extends BaseState<PersonalEditScreen> with BasicS
onTap: () => FocusScope.of(context).unfocus(),
behavior: HitTestBehavior.translucent,
child: Scaffold(
appBar: CustomAppBar.back(title: "Chỉnh sửa thông tin cá nhân"),
appBar: CustomNavigationBar(title: "Chỉnh sửa thông tin cá nhân"),
body: Obx(() {
List<PersonalEditItemModel> items;
final editDataModel = viewModel.editDataModel.value;
......
......@@ -30,17 +30,6 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
super.initState();
_loadAppInfo();
_headerHomeVM.freshData();
// WidgetsBinding.instance.addPostFrameCallback((_) async {
// if (!mounted) return;
// await PopupManagerViewModel.instance.ensureLoaded();
// final popup = PopupManagerViewModel.instance.getForScreen(DirectionalScreenName.personal.rawValue);
// final id = popup?.id ?? '';
// if (id.isEmpty || popup == null) return;
// await showPopupManagerScreen(
// context,
// modelPopup: popup,
// );
// });
runPopupCheck(DirectionalScreenName.personal);
}
......
import 'package:flutter/material.dart';
import '../../widgets/custom_navigation_bar.dart';
class PointHistoryScreen extends StatefulWidget {
const PointHistoryScreen({super.key});
@override
State<PointHistoryScreen> createState() => _PointHistoryScreenState();
}
class _PointHistoryScreenState extends State<PointHistoryScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomNavigationBar(title: "Lịch sử điểm"),
body: SafeArea(
child: Center(child: Text('Point History Screen')),
),
);
}
}
import 'package:json_annotation/json_annotation.dart';
import 'package:mypoint_flutter_app/directional/directional_screen.dart';
part 'form_input_description_model.g.dart';
@JsonSerializable()
......@@ -10,6 +11,13 @@ class FormInputDescriptionModel {
@JsonKey(name: 'click_action_param')
final String? clickActionParam;
DirectionalScreen? get directional {
return DirectionalScreen.build(
clickActionType: clickActionType,
clickActionParam: clickActionParam,
);
}
FormInputDescriptionModel({
this.title,
this.checkbox,
......
......@@ -4,7 +4,7 @@ import 'package:get/get.dart';
import 'package:mypoint_flutter_app/screen/register_campaign/register_form_input_viewmodel.dart';
import '../../resources/base_color.dart';
import '../../shared/router_gage.dart';
import '../../widgets/custom_app_bar.dart';
import '../../widgets/custom_navigation_bar.dart';
import '../voucher/models/product_model.dart';
import 'input_form_cell.dart';
import 'model/registration_form_package_model.dart';
......@@ -51,10 +51,11 @@ class _RegisterFormInputScreenState extends State<RegisterFormInputScreen> {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: CustomAppBar.back(title: _title),
appBar: CustomNavigationBar(title: _title),
body: Obx(() {
final form = _isConfirmScreen ? _viewModel.form.value?.formConfirm : _viewModel.form.value?.formRegistration;
final inputItem = _viewModel.form.value?.formRegistration?.inputRequired;
_validateForm();
if (form == null) {
return const Center(child: CircularProgressIndicator());
}
......@@ -101,7 +102,13 @@ class _RegisterFormInputScreenState extends State<RegisterFormInputScreen> {
},
),
),
Expanded(child: HtmlWidget(form!.footerDescription!.title!)),
Expanded(child:
GestureDetector(
onTap: () {
form.footerDescription?.directional?.begin();
},
child: HtmlWidget(form!.footerDescription!.title!)),
),
],
),
),
......
......@@ -4,6 +4,7 @@ import 'package:intl/intl.dart';
import 'package:mypoint_flutter_app/screen/topup/topup_viewmodel.dart';
import 'package:mypoint_flutter_app/widgets/custom_navigation_bar.dart';
import 'package:mypoint_flutter_app/widgets/image_loader.dart';
import '../../extensions/debouncer.dart';
import '../../preference/data_preference.dart';
import '../../resources/base_color.dart';
import '../../shared/router_gage.dart';
......@@ -20,6 +21,7 @@ class PhoneTopUpScreen extends StatefulWidget {
class _PhoneTopUpScreenState extends State<PhoneTopUpScreen> {
final TopUpViewModel _viewModel = Get.put(TopUpViewModel());
late final TextEditingController _phoneController;
final _deb = Debouncer(ms: 500);
@override
void initState() {
......@@ -84,7 +86,7 @@ class _PhoneTopUpScreenState extends State<PhoneTopUpScreen> {
keyboardType: TextInputType.phone,
onChanged: (value) {
_viewModel.phoneNumber.value = value;
_viewModel.checkMobileNetwork();
_deb.run(() => _viewModel.checkMobileNetwork());
},
),
),
......@@ -326,12 +328,12 @@ class _PhoneTopUpScreenState extends State<PhoneTopUpScreen> {
),
const Spacer(),
ElevatedButton(
onPressed: () {
onPressed: _viewModel.validatePhoneNumber() ? () {
Get.toNamed(
transactionDetailScreen,
arguments: {"product": product, "quantity": 1, "targetPhoneNumber": _viewModel.phoneNumber.value},
);
},
} : null,
style: ElevatedButton.styleFrom(
backgroundColor: BaseColor.primary500,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
......
......@@ -35,22 +35,29 @@ class TopUpViewModel extends RestfulApiViewModel {
}
}
bool validatePhoneNumber() {
final phone = phoneNumber.value.replaceAll(RegExp(r'\s+'), '');
final regex = RegExp(r'^(0|\+84)(3[2-9]|5[6|8|9]|7[0|6-9]|8[1-5]|9[0-4|6-9])[0-9]{7}$');
return regex.hasMatch(phone);
}
firstLoadTopUpData() async {
showLoading();
await getTopUpBrands();
await checkMobileNetwork();
hideLoading();
_getTopUpBrands();
}
getTopUpBrands() {
_getTopUpBrands() {
showLoading();
client.getTopUpBrands(ProductType.topupMobile).then((response) {
topUpBrands.value = response.data ?? [];
hideLoading();
checkMobileNetwork();
}).catchError((error) {
print('Error fetching brands topup: $error');
hideLoading();
});
}
checkMobileNetwork() {
showLoading();
client.checkMobileNetwork(phoneNumber.value).then((response) {
final brandCode = response.data?.brand ?? '';
final brand = topUpBrands.isNotEmpty
......@@ -60,12 +67,14 @@ class TopUpViewModel extends RestfulApiViewModel {
)
: null;
selectedBrand.value = brand;
hideLoading();
getTelcoDetail();
}).catchError((error) {
final first = topUpBrands.value.firstOrNull;
if (first != null) {
selectedBrand.value = first;
}
hideLoading();
getTelcoDetail();
print('Error checking mobile network: $error');
});
......@@ -75,7 +84,6 @@ class TopUpViewModel extends RestfulApiViewModel {
final code = selectedBrand.value?.code;
final id = selectedBrand.value?.id;
if (code == null || id == null) return;
void makeSelected(List<ProductModel> list) {
bool didSelect = false;
if (selected != null && selected.isNotEmpty) {
......@@ -116,8 +124,8 @@ class TopUpViewModel extends RestfulApiViewModel {
final data = result.data ?? [];
_allValue[code] = data;
products.value = result.data ?? [];
makeSelected(data);
hideLoading();
makeSelected(data);
} catch (error) {
print("Error fetching all products: $error");
hideLoading();
......
......@@ -5,7 +5,7 @@ import 'package:intl/intl.dart';
import '../../base/base_screen.dart';
import '../../base/basic_state.dart';
import '../../resources/base_color.dart';
import '../../widgets/custom_app_bar.dart';
import '../../widgets/custom_navigation_bar.dart';
import '../../widgets/image_loader.dart';
import '../voucher/models/product_model.dart';
import 'model/payment_bank_account_info_model.dart';
......@@ -60,7 +60,7 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w
Widget createBody() {
return Scaffold(
backgroundColor: Colors.grey.shade50,
appBar: CustomAppBar.back(title: "Thông tin thanh toán"),
appBar: CustomNavigationBar(title: "Thông tin thanh toán"),
body: Obx(() {
if (_viewModel.isLoading.value) {
return const Center(child: CircularProgressIndicator());
......
......@@ -48,7 +48,7 @@ class VoucherDetailViewModel extends RestfulApiViewModel {
liked.value = (response.data?.id ?? 0) != 0;
}
} catch (error) {
onShowAlertError?.call("Error toggling favorite: $error");
onShowAlertError?.call(Constants.commonError);
}
}
......
import 'package:json_annotation/json_annotation.dart';
part 'like_product_reponse_model.g.dart';
part 'like_product_response_model.g.dart';
@JsonSerializable()
class LikeProductReponseModel {
class LikeProductResponseModel {
@JsonKey(name: 'like_id')
final int? id;
LikeProductReponseModel({
LikeProductResponseModel({
this.id,
});
factory LikeProductReponseModel.fromJson(Map<String, dynamic> json) => _$LikeProductReponseModelFromJson(json);
Map<String, dynamic> toJson() => _$LikeProductReponseModelToJson(this);
factory LikeProductResponseModel.fromJson(Map<String, dynamic> json) => _$LikeProductResponseModelFromJson(json);
Map<String, dynamic> toJson() => _$LikeProductResponseModelToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'like_product_reponse_model.dart';
part of 'like_product_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
LikeProductReponseModel _$LikeProductReponseModelFromJson(
LikeProductResponseModel _$LikeProductResponseModelFromJson(
Map<String, dynamic> json,
) => LikeProductReponseModel(id: (json['like_id'] as num?)?.toInt());
) => LikeProductResponseModel(id: (json['like_id'] as num?)?.toInt());
Map<String, dynamic> _$LikeProductReponseModelToJson(
LikeProductReponseModel instance,
Map<String, dynamic> _$LikeProductResponseModelToJson(
LikeProductResponseModel instance,
) => <String, dynamic>{'like_id': instance.id};
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../shared/router_gage.dart';
import '../../../widgets/custom_app_bar.dart';
import '../../../widgets/custom_empty_widget.dart';
import '../../../widgets/custom_navigation_bar.dart';
import '../../../widgets/image_loader.dart';
import '../../home/models/my_product_model.dart';
import 'my_product_list_viewmodel.dart';
......@@ -28,15 +28,18 @@ class _MyVoucherListScreenState extends State<MyVoucherListScreen> {
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: CustomAppBar.back(title: 'Ưu đãi của tôi'),
appBar: CustomNavigationBar(title: 'Ưu đãi của tôi'),
body: Obx(
() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [_buildTab('Đang có', 0), _buildTab('Đã sử dụng', 1), _buildTab('Hết hạn', 2)],
),
),
const Divider(height: 1),
if (_viewModel.myProducts.isEmpty)
Expanded(child: EmptyWidget(size: Size(screenWidth / 2, screenWidth / 2)))
......
......@@ -37,7 +37,6 @@ class _VoucherTabScreenState extends State<VoucherTabScreen> with PopupOnInit {
appBar: CustomNavigationBar(
title: "Ưu đãi",
leftButtons: [],
backgroundImage: _headerHomeVM.headerData.background ?? "assets/images/bg_header_navi.png",
rightButtons: [
IconButton(
icon: const Icon(Icons.search, color: Colors.white),
......
......@@ -28,6 +28,7 @@ import '../screen/onboarding/onboarding_screen.dart';
import '../screen/order_menu/order_menu_screen.dart';
import '../screen/pageDetail/campaign_detail_screen.dart';
import '../screen/personal/personal_edit_screen.dart';
import '../screen/point_history/point_history_screen.dart';
import '../screen/quiz_campaign/quiz_campaign_screen.dart';
import '../screen/register_campaign/register_form_input_screen.dart';
import '../screen/setting/setting_screen.dart';
......@@ -94,6 +95,7 @@ const deviceManagerScreen = '/deviceManagerScreen';
const interestCategoriesScreen = '/interestCategoriesScreen';
const myMobileCardListScreen = '/myMobileCardListScreen';
const bankAccountManagerScreen = '/bankAccountManagerScreen';
const pointHistoryScreen = '/pointHistoryScreen';
class RouterPage {
static List<GetPage> pages() {
......@@ -157,6 +159,7 @@ class RouterPage {
GetPage(name: myMobileCardListScreen, page: () => MyMobileCardListScreen()),
GetPage(name: interestCategoriesScreen, page: () => InterestCategoriesScreen()),
GetPage(name: bankAccountManagerScreen, page: () => BankAccountManagerScreen()),
GetPage(name: pointHistoryScreen, page: () => PointHistoryScreen()),
];
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../screen/home/header_home_viewmodel.dart';
import 'back_button.dart';
import 'image_loader.dart';
......@@ -7,45 +9,51 @@ class CustomNavigationBar extends StatelessWidget implements PreferredSizeWidget
final String? backgroundImage;
final List<Widget> leftButtons;
final List<Widget> rightButtons;
final _defaultBgImage = 'assets/images/bg_header_navi.png';
const CustomNavigationBar({
super.key,
required this.title,
this.backgroundImage = "assets/images/bg_header_navi.png",
this.backgroundImage,
this.leftButtons = const [CustomBackButton()],
this.rightButtons = const [],
});
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
final double statusBarHeight = MediaQuery.of(context).padding.top;
final bool isHttp =
backgroundImage != null && (backgroundImage!.startsWith('http://') || backgroundImage!.startsWith('https://'));
if (backgroundImage != null && backgroundImage!.isNotEmpty) {
return _buildAppBar(backgroundImage!, context);
}
final theme = Get.find<HeaderThemeController>();
return Obx(() {
final bg = theme.background.value ?? _defaultBgImage;
return _buildAppBar(bg, context);
});
}
Widget _buildAppBar(String bgImage, BuildContext context) {
final double statusBarHeight = MediaQuery.of(context).padding.top;
final bool isHttp = bgImage.startsWith('http://') || bgImage.startsWith('https://');
return Container(
height: statusBarHeight + kToolbarHeight,
decoration: BoxDecoration(
// image: backgroundImage != null
// ? DecorationImage(
// image: AssetImage(backgroundImage!),
// fit: BoxFit.cover,
// )
// : null,
color: backgroundImage == null ? Colors.white : null,
color: bgImage.isEmpty ? Colors.white : null,
),
child: Stack(
fit: StackFit.expand,
children: [
if (backgroundImage != null)
if (bgImage.isNotEmpty)
isHttp
? loadNetworkImage(
url: backgroundImage,
url: bgImage,
fit: BoxFit.cover,
placeholderAsset: 'assets/images/bg_header_navi.png',
placeholderAsset: _defaultBgImage,
)
: Image.asset(backgroundImage!, fit: BoxFit.cover),
: Image.asset(_defaultBgImage, fit: BoxFit.cover),
SafeArea(
bottom: false,
child: Stack(
......@@ -57,8 +65,7 @@ class CustomNavigationBar extends StatelessWidget implements PreferredSizeWidget
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w800, color: Colors.white),
textAlign: TextAlign.center,
),
// Back button bên trái
// if (showBackButton) Positioned(left: 12, child: CustomBackButton()),
// Buttons bên trái
if (leftButtons.isNotEmpty)
Positioned(left: 12, child: Row(mainAxisSize: MainAxisSize.min, children: leftButtons)),
// Buttons bên phải
......
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