Commit c8abf95b authored by DatHV's avatar DatHV
Browse files

update screen logic

parent fda33894
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/image_loader.dart';
import '../../home/models/my_product_model.dart';
import 'my_product_list_viewmodel.dart';
import 'package:dotted_border/dotted_border.dart';
class MyVoucherListScreen extends StatefulWidget {
const MyVoucherListScreen({super.key});
@override
State<MyVoucherListScreen> createState() => _MyVoucherListScreenState();
}
class _MyVoucherListScreenState extends State<MyVoucherListScreen> {
late final MyProductListViewModel _viewModel;
@override
void initState() {
super.initState();
_viewModel = Get.put(MyProductListViewModel());
}
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: CustomAppBar.back(title: 'Ưu đãi của tôi'),
body: Obx(
() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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)))
else
Expanded(
child: RefreshIndicator(
onRefresh: () async {
_viewModel.freshData(isRefresh: true);
},
child: ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: _viewModel.myProducts.length,
itemBuilder: (_, index) {
if (index >= _viewModel.myProducts.length) {
_viewModel.freshData(isRefresh: false);
return const Center(
child: Padding(padding: EdgeInsets.all(16), child: CircularProgressIndicator()),
);
}
final product = _viewModel.myProducts[index];
return _buildVoucherItem(product);
},
),
),
),
],
),
),
);
}
Widget _buildTab(String title, int index) {
return GestureDetector(
onTap: () => _viewModel.selectTab(index),
child: Obx(
() => Column(
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: _viewModel.selectedTabIndex.value == index ? Colors.red : Colors.black54,
),
),
const SizedBox(height: 4),
if (_viewModel.selectedTabIndex.value == index) Container(height: 2, width: 60, color: Colors.red),
],
),
),
);
}
Widget _buildVoucherItem(MyProductModel product) {
return GestureDetector(
onTap: () {
Get.toNamed(voucherDetailScreen, arguments: {"customerProductId": product.id});
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.all(0),
child: DottedBorder(
color: Colors.redAccent.withOpacity(0.6),
borderType: BorderType.RRect,
radius: const Radius.circular(12),
dashPattern: const [6, 4],
strokeWidth: 1,
child: Container(
padding: const EdgeInsets.all(12),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: loadNetworkImage(
url: product.logo ?? '',
width: 64,
height: 64,
fit: BoxFit.cover,
placeholderAsset: "assets/images/bg_default_11.png",
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.brandName ?? '',
style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.black54, fontSize: 12),
),
const SizedBox(height: 4),
Text(product.title ?? '', style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500)),
const SizedBox(height: 4),
Text('HSD: ${product.expire}', style: const TextStyle(color: Colors.black54, fontSize: 12)),
],
),
),
],
),
),
),
),
);
}
}
......@@ -2,12 +2,12 @@ import 'package:flutter/material.dart';
class HeaderSectionTitle extends StatelessWidget {
final String title;
final VoidCallback? onViewAll; // 👈 Optional
final VoidCallback? onViewAll;
const HeaderSectionTitle({
super.key,
required this.title,
this.onViewAll, // 👈 Nếu null thì không hiển thị button
this.onViewAll,
});
@override
......
......@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/screen/voucher/voucher_list/voucher_list_screen.dart';
import '../../shared/router_gage.dart';
import '../home/header_home_viewmodel.dart';
import 'voucher_tab_viewmodel.dart';
import 'sub_widget/voucher_action_menu.dart';
import 'sub_widget/voucher_item_grid.dart';
......@@ -9,9 +10,16 @@ import 'sub_widget/voucher_item_list.dart';
import 'sub_widget/voucher_section_title.dart';
import '../../widgets/custom_navigation_bar.dart';
class VoucherTabScreen extends StatelessWidget {
class VoucherTabScreen extends StatefulWidget {
const VoucherTabScreen({super.key});
@override
State<VoucherTabScreen> createState() => _VoucherTabScreenState();
}
class _VoucherTabScreenState extends State<VoucherTabScreen> {
final _headerHomeVM = Get.find<HeaderHomeViewModel>();
@override
Widget build(BuildContext context) {
final VoucherTabViewModel viewModel = Get.put(VoucherTabViewModel());
......@@ -20,6 +28,7 @@ class VoucherTabScreen extends StatelessWidget {
appBar: CustomNavigationBar(
title: "Ưu đãi",
showBackButton: false,
backgroundImage: _headerHomeVM.headerData.background ?? "assets/images/bg_header_navi.png",
rightButtons: [
IconButton(
icon: const Icon(Icons.search, color: Colors.white),
......
......@@ -3,11 +3,15 @@ import 'package:get/get.dart';
import 'package:mypoint_flutter_app/screen/news/news_list_screen.dart';
import '../screen/achievement/achievement_list_screen.dart';
import '../screen/game/game_cards/game_card_screen.dart';
import '../screen/location_address/location_address_screen.dart';
import '../screen/login/login_screen.dart';
import '../screen/main_tab_screen/main_tab_screen.dart';
import '../screen/membership/membership_screen.dart';
import '../screen/notification/notification_screen.dart';
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/register_campaign/register_form_input_screen.dart';
import '../screen/setting/setting_screen.dart';
import '../screen/splash/splash_screen.dart';
......@@ -15,6 +19,7 @@ import '../screen/support/support_screen.dart';
import '../screen/transaction/history/transaction_history_detail_screen.dart';
import '../screen/transaction/transaction_detail_screen.dart';
import '../screen/voucher/detail/voucher_detail_screen.dart';
import '../screen/voucher/my_voucher/my_product_list_widget.dart';
import '../screen/voucher/voucher_list/voucher_list_screen.dart';
import '../screen/vplay_game_center/vplay_game_center_screen.dart';
import '../screen/webview/payment_web_view_screen.dart';
......@@ -39,6 +44,11 @@ const campaignDetailScreen = '/campaignDetailScreen';
const newsListScreen = '/newsListScreen';
const achievementListScreen = '/achievementListScreen';
const vplayGameCenterScreen = '/vplayGameCenterScreen';
const myVoucherListScreen = '/myVoucherListScreen';
const personalEditScreen = '/personalEditScreen';
const orderMenuScreen = '/orderMenuScreen';
const locationAddressScreen = '/locationAddressScreen';
const membershipScreen = '/membershipScreen';
class RouterPage {
static List<GetPage> pages() {
......@@ -68,6 +78,11 @@ class RouterPage {
GetPage(name: newsListScreen, page: () => NewsListScreen()),
GetPage(name: achievementListScreen, page: () => AchievementListScreen()),
GetPage(name: vplayGameCenterScreen, page: () => VplayGameCenterScreen()),
GetPage(name: myVoucherListScreen, page: () => MyVoucherListScreen()),
GetPage(name: personalEditScreen, page: () => PersonalEditScreen()),
GetPage(name: orderMenuScreen, page: () => OrderMenuScreen()),
GetPage(name: locationAddressScreen, page: () => LocationAddressScreen()),
GetPage(name: membershipScreen, page: () => MembershipScreen()),
];
}
}
......
......@@ -22,8 +22,8 @@ class CustomBackButton extends StatelessWidget {
children: [
Center(
child: Container(
height: 28,
width: 28,
height: 24,
width: 24,
decoration: BoxDecoration(
border: Border.all(color: BaseColor.second300, width: 1),
borderRadius: BorderRadius.circular(8),
......@@ -33,17 +33,16 @@ class CustomBackButton extends StatelessWidget {
),
Center(
child: IconButton(
icon: Icon(Icons.arrow_back_ios_rounded, color: iconColor ?? BaseColor.second600, size: iconSize ?? 24),
icon: Icon(Icons.arrow_back_ios_rounded, color: iconColor ?? BaseColor.second600, size: iconSize ?? 16),
onPressed:
onPressed ??
() {
if (Get.key.currentState?.canPop() == true) {
Get.back();
// if (Navigator.canPop(context)) {
// Navigator.pop(context);
// } else {
// DataPreference.instance.clearData();
// Get.offAllNamed(onboardingScreen);
// }
} else {
DataPreference.instance.clearData();
Get.offAllNamed(onboardingScreen);
}
},
),
),
......
......@@ -3,11 +3,13 @@ import 'package:flutter/material.dart';
class EmptyWidget extends StatelessWidget {
final String imageAsset;
final String content;
final Size size;
const EmptyWidget({
super.key,
this.imageAsset = 'assets/images/ic_pipi_06.png',
this.content = 'Không có dữ liệu hiển thị',
this.size = const Size(120, 120),
});
@override
......@@ -18,8 +20,8 @@ class EmptyWidget extends StatelessWidget {
children: [
Image.asset(
imageAsset,
width: 120,
height: 120,
width: size.width,
height: size.height,
fit: BoxFit.contain,
),
const SizedBox(height: 16),
......
import 'package:flutter/material.dart';
import 'back_button.dart';
import 'image_loader.dart';
class CustomNavigationBar extends StatelessWidget implements PreferredSizeWidget {
final String title;
......@@ -20,19 +21,32 @@ class CustomNavigationBar extends StatelessWidget implements PreferredSizeWidget
@override
Widget build(BuildContext context) {
final double statusBarHeight = MediaQuery.of(context).padding.top;
final bool isHttp =
backgroundImage != null && (backgroundImage!.startsWith('http://') || backgroundImage!.startsWith('https://'));
return Container(
height: statusBarHeight + kToolbarHeight,
decoration: BoxDecoration(
image: backgroundImage != null
? DecorationImage(
image: AssetImage(backgroundImage!),
fit: BoxFit.cover,
)
: null,
// image: backgroundImage != null
// ? DecorationImage(
// image: AssetImage(backgroundImage!),
// fit: BoxFit.cover,
// )
// : null,
color: backgroundImage == null ? Colors.white : null,
),
child: SafeArea(
child: Stack(
fit: StackFit.expand,
children: [
if (backgroundImage != null)
isHttp
? loadNetworkImage(
url: backgroundImage,
fit: BoxFit.cover,
placeholderAsset: 'assets/images/bg_header_navi.png',
)
: Image.asset(backgroundImage!, fit: BoxFit.cover),
SafeArea(
bottom: false,
child: Stack(
alignment: Alignment.center,
......@@ -40,31 +54,19 @@ class CustomNavigationBar extends StatelessWidget implements PreferredSizeWidget
// Title ở giữa
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
textAlign: TextAlign.center,
),
// Back button bên trái
if (showBackButton)
Positioned(
left: 12,
child: CustomBackButton(),
),
if (showBackButton) Positioned(left: 12, child: CustomBackButton()),
// Buttons bên phải
if (rightButtons != null)
Positioned(
right: 12,
child: Row(
mainAxisSize: MainAxisSize.min,
children: rightButtons!,
Positioned(right: 12, child: Row(mainAxisSize: MainAxisSize.min, children: rightButtons!)),
],
),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class DatePickerField extends StatefulWidget {
final String label;
final DateTime? initialDate;
final Function(DateTime) onDateSelected;
final bool enabled;
const DatePickerField({
super.key,
required this.label,
this.initialDate,
required this.onDateSelected,
this.enabled = true,
});
@override
State<DatePickerField> createState() => _DatePickerFieldState();
}
class _DatePickerFieldState extends State<DatePickerField> {
DateTime? _selectedDate;
@override
void initState() {
super.initState();
_selectedDate = widget.initialDate;
}
Future<void> _pickDate() async {
if (!widget.enabled) return;
final now = DateTime.now();
final picked = await showDatePicker(
context: context,
initialDate: _selectedDate ?? now,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
);
if (picked != null) {
setState(() {
_selectedDate = picked;
});
widget.onDateSelected(picked);
}
}
@override
Widget build(BuildContext context) {
final displayText = _selectedDate != null
? DateFormat('dd/MM/yyyy').format(_selectedDate!)
: widget.label;
return GestureDetector(
onTap: _pickDate,
child: AbsorbPointer(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade400),
borderRadius: BorderRadius.circular(8),
color: widget.enabled ? Colors.white : Colors.grey.shade100,
),
child: Row(
children: [
const Icon(Icons.calendar_today, color: Colors.blueGrey, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
displayText,
style: TextStyle(
fontSize: 16,
color: _selectedDate != null
? Colors.black87
: Colors.grey.shade500,
),
),
),
if (widget.enabled)
const Icon(Icons.expand_more, color: Colors.grey),
],
),
),
),
);
}
}
......@@ -52,6 +52,8 @@ dependencies:
qr_flutter: ^4.0.0
barcode_widget: ^2.0.1
infinite_carousel: ^1.0.3
package_info_plus: ^4.1.0
dotted_border: ^2.0.0
game_miniapp:
path: ../mini_app/game_miniapp
dev_dependencies:
......
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