feat: order range date filter
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -11,7 +13,7 @@ import '../../../components/button/button.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
import '../../../components/widgets/empty_widget.dart';
|
||||
import '../../../router/app_router.gr.dart';
|
||||
import 'widgets/status_tile.dart';
|
||||
import 'widgets/filter_header_delegate.dart';
|
||||
import 'widgets/order_tile.dart';
|
||||
|
||||
@RoutePage()
|
||||
@@ -38,7 +40,6 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
// Filter state
|
||||
String selectedFilter = 'All';
|
||||
final List<String> filterOptions = ['All', 'Completed', 'Pending'];
|
||||
|
||||
@override
|
||||
@@ -79,13 +80,27 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.background,
|
||||
body: BlocListener<OrderLoaderBloc, OrderLoaderState>(
|
||||
listenWhen: (p, c) => p.status != c.status,
|
||||
listener: (context, state) {
|
||||
context.read<OrderLoaderBloc>().add(
|
||||
OrderLoaderEvent.fetched(isRefresh: true),
|
||||
);
|
||||
},
|
||||
body: MultiBlocListener(
|
||||
listeners: [
|
||||
BlocListener<OrderLoaderBloc, OrderLoaderState>(
|
||||
listenWhen: (p, c) => p.status != c.status,
|
||||
listener: (context, state) {
|
||||
context.read<OrderLoaderBloc>().add(
|
||||
OrderLoaderEvent.fetched(isRefresh: true),
|
||||
);
|
||||
},
|
||||
),
|
||||
BlocListener<OrderLoaderBloc, OrderLoaderState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.dateFrom != current.dateFrom ||
|
||||
previous.dateTo != current.dateTo,
|
||||
listener: (context, state) {
|
||||
context.read<OrderLoaderBloc>().add(
|
||||
OrderLoaderEvent.fetched(isRefresh: true),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
child: BlocBuilder<OrderLoaderBloc, OrderLoaderState>(
|
||||
builder: (context, state) {
|
||||
return NotificationListener<ScrollNotification>(
|
||||
@@ -120,60 +135,36 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
// Pinned Filter Section
|
||||
SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: _FilterHeaderDelegate(
|
||||
child: Container(
|
||||
color: AppColor.background,
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
AppValue.padding,
|
||||
10,
|
||||
AppValue.padding,
|
||||
10,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: filterOptions.map((option) {
|
||||
final index = filterOptions.indexOf(option);
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: index < filterOptions.length - 1
|
||||
? 8
|
||||
: 0,
|
||||
),
|
||||
child: OrderStatusTile(
|
||||
label: option,
|
||||
isSelected: option == selectedFilter,
|
||||
onSelected: (isSelected) {
|
||||
if (isSelected) {
|
||||
setState(() {
|
||||
selectedFilter = option;
|
||||
});
|
||||
if (option.toLowerCase() == 'all') {
|
||||
context.read<OrderLoaderBloc>().add(
|
||||
OrderLoaderEvent.statusChanged(
|
||||
'',
|
||||
),
|
||||
);
|
||||
} else {
|
||||
context.read<OrderLoaderBloc>().add(
|
||||
OrderLoaderEvent.statusChanged(
|
||||
option.toLowerCase(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
delegate: FilterHeaderDelegate(
|
||||
backgroundColor: AppColor.background,
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
AppValue.padding,
|
||||
10,
|
||||
AppValue.padding,
|
||||
10,
|
||||
),
|
||||
startDate: state.dateFrom,
|
||||
endDate: state.dateTo,
|
||||
filterOptions: filterOptions,
|
||||
selectedFilter: state.status,
|
||||
onDateChanged: (startDate, endDate) {
|
||||
if (startDate != null && endDate != null) {
|
||||
log('Date changed');
|
||||
context.read<OrderLoaderBloc>().add(
|
||||
OrderLoaderEvent.rangeDateChanged(
|
||||
startDate,
|
||||
endDate,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
onFilterChanged: (filter) {
|
||||
final status = filter.toLowerCase();
|
||||
|
||||
context.read<OrderLoaderBloc>().add(
|
||||
OrderLoaderEvent.statusChanged(status),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -189,13 +180,13 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
child: Column(
|
||||
children: [
|
||||
// Show filtered transaction count
|
||||
if (selectedFilter != 'All')
|
||||
if (state.status != 'all')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${state.orders.length} ${selectedFilter.toLowerCase()} order${state.orders.length != 1 ? 's' : ''}',
|
||||
'${state.orders.length} ${state.status.toLowerCase()} order${state.orders.length != 1 ? 's' : ''}',
|
||||
style: TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontSize: 14,
|
||||
@@ -210,7 +201,7 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
? EmptyWidget(
|
||||
title: 'Order',
|
||||
message:
|
||||
'No ${selectedFilter.toLowerCase()} orders found',
|
||||
'No ${state.status.toLowerCase()} orders found',
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: state.orders.length,
|
||||
@@ -247,28 +238,3 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
// Custom delegate for pinned filter header
|
||||
class _FilterHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
final Widget child;
|
||||
|
||||
_FilterHeaderDelegate({required this.child});
|
||||
|
||||
@override
|
||||
double get minExtent => 70; // Minimum height when collapsed
|
||||
|
||||
@override
|
||||
double get maxExtent => 70; // Maximum height when expanded
|
||||
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context,
|
||||
double shrinkOffset,
|
||||
bool overlapsContent,
|
||||
) {
|
||||
return child;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../components/field/date_range_picker_field.dart';
|
||||
import 'status_tile.dart';
|
||||
|
||||
class FilterHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
final Color backgroundColor;
|
||||
final EdgeInsets padding;
|
||||
final DateTime? startDate;
|
||||
final DateTime? endDate;
|
||||
final List<String> filterOptions;
|
||||
final String selectedFilter;
|
||||
final Function(DateTime?, DateTime?) onDateChanged;
|
||||
final Function(String) onFilterChanged;
|
||||
|
||||
FilterHeaderDelegate({
|
||||
required this.backgroundColor,
|
||||
required this.padding,
|
||||
required this.startDate,
|
||||
required this.endDate,
|
||||
required this.filterOptions,
|
||||
required this.selectedFilter,
|
||||
required this.onDateChanged,
|
||||
required this.onFilterChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
double get minExtent => 130;
|
||||
|
||||
@override
|
||||
double get maxExtent => 130;
|
||||
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context,
|
||||
double shrinkOffset,
|
||||
bool overlapsContent,
|
||||
) {
|
||||
print('FilterHeaderDelegate build called'); // Debug log
|
||||
return Container(
|
||||
color: backgroundColor,
|
||||
padding: padding,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: DateRangePickerField(
|
||||
maxDate: DateTime.now(),
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
onChanged: (start, end) {
|
||||
print(
|
||||
'onChanged called in FilterHeaderDelegate: $start - $end',
|
||||
); // Debug log
|
||||
onDateChanged(start, end);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: filterOptions.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final option = entry.value;
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: index < filterOptions.length - 1 ? 8 : 0,
|
||||
),
|
||||
child: OrderStatusTile(
|
||||
label: option,
|
||||
isSelected: option == selectedFilter,
|
||||
onSelected: (isSelected) {
|
||||
if (isSelected) {
|
||||
onFilterChanged(option);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant FilterHeaderDelegate oldDelegate) {
|
||||
return oldDelegate.startDate != startDate ||
|
||||
oldDelegate.endDate != endDate ||
|
||||
oldDelegate.selectedFilter != selectedFilter ||
|
||||
oldDelegate.filterOptions.length != filterOptions.length ||
|
||||
oldDelegate.backgroundColor != backgroundColor;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user