proxybuy-flutter/lib/widgets/time_selection_bottom_sheet...

228 lines
7.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:lba/res/colors.dart';
import 'package:lba/widgets/button.dart';
import 'dart:math' as math;
class TimeSelectionBottomSheet extends StatefulWidget {
const TimeSelectionBottomSheet({super.key});
@override
State<TimeSelectionBottomSheet> createState() =>
_TimeSelectionBottomSheetState();
}
class _TimeSelectionBottomSheetState extends State<TimeSelectionBottomSheet> {
final List<String> _timeSlots = [];
String? _selectedTimeSlot;
late ScrollController _scrollController;
static const double _itemHeight = 50.0;
static const int _visibleItems = 3;
@override
void initState() {
super.initState();
_generateTimeSlots();
final initialIndex = _timeSlots.length > 1 ? 1 : 0;
if (_timeSlots.isNotEmpty) {
_selectedTimeSlot = _timeSlots[initialIndex];
}
_scrollController =
ScrollController(initialScrollOffset: initialIndex * _itemHeight);
_scrollController.addListener(_onScroll);
}
void _onScroll() {
setState(() {});
}
@override
void dispose() {
_scrollController.removeListener(_onScroll);
_scrollController.dispose();
super.dispose();
}
void _generateTimeSlots() {
final now = DateTime.now();
final endTime = DateTime(now.year, now.month, now.day, 22, 0);
DateTime startTime = DateTime(
now.year,
now.month,
now.day,
now.hour,
now.minute > 30 ? 60 : 30,
);
if (startTime.isAfter(endTime)) return;
_timeSlots.add('');
while (startTime.isBefore(endTime)) {
final slotEnd = startTime.add(const Duration(minutes: 30));
final formattedStart = DateFormat('HH:mm').format(startTime);
final formattedEnd = DateFormat('HH:mm').format(slotEnd);
_timeSlots.add('$formattedStart - $formattedEnd');
startTime = slotEnd;
}
_timeSlots.add('');
}
void _onScrollEnd() {
final currentOffset = _scrollController.offset;
final targetIndex = (currentOffset / _itemHeight).round();
final targetOffset = targetIndex * _itemHeight;
if ((targetOffset - currentOffset).abs() > 0.1) {
_scrollController.animateTo(
targetOffset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
}
if (mounted && _timeSlots.isNotEmpty) {
final finalIndex = targetIndex.clamp(0, _timeSlots.length - 1);
final newSelection = _timeSlots[finalIndex];
if (_selectedTimeSlot != newSelection && newSelection.isNotEmpty) {
setState(() {
_selectedTimeSlot = newSelection;
});
HapticFeedback.lightImpact();
}
}
}
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40,
height: 4,
margin: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: AppColors.greyBorder,
borderRadius: BorderRadius.circular(10),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Select a time', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Divider(color: AppColors.divider),
const SizedBox(height: 8),
Text(
'Philadelphia Honey Pecan Cream Cheese Spread, 7.5 oz Tub available from Now - 10:00 PM Today',
style: TextStyle(color: AppColors.textSecondary, fontSize: 14),
),
const SizedBox(height: 16),
_buildTimePicker(),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
height: 50,
child: Button(
text: 'Confirm',
onPressed: () {
if (_selectedTimeSlot != null && _selectedTimeSlot!.isNotEmpty) {
Navigator.pop(context, _selectedTimeSlot);
}
},
color: AppColors.offerTimer,
),
),
const SizedBox(height: 20),
],
),
),
],
),
);
}
Widget _buildTimePicker() {
if (_timeSlots.length <= 2) {
return const Center(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Text('No available time slots for today.'),
),
);
}
final listHeight = _itemHeight * _visibleItems;
return SizedBox(
height: listHeight,
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: _itemHeight,
bottom: _itemHeight,
left: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
color: AppColors.fillOrder,
borderRadius: BorderRadius.circular(8),
),
),
),
NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification) {
_onScrollEnd();
}
return true;
},
child: ListView.builder(
controller: _scrollController,
padding: EdgeInsets.symmetric(vertical: (listHeight - _itemHeight) / 2),
itemCount: _timeSlots.length,
itemExtent: _itemHeight,
itemBuilder: (context, index) {
final centerOffset = _scrollController.offset;
final itemOffset = index * _itemHeight;
final distance = (itemOffset - centerOffset).abs();
final isSelected = distance < _itemHeight / 2;
final scale = math.max(1.0 - (distance / listHeight) * 0.7, 0.8);
final opacity = math.max(1.0 - (distance / listHeight) * 1.2, 0.3);
return Center(
child: Transform.scale(
scale: scale,
child: Text(
_timeSlots[index],
style: TextStyle(
fontSize: 18,
color: isSelected ? AppColors.textPrimary : AppColors.textSecondary.withOpacity(opacity),
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
),
);
},
),
),
],
),
);
}
}