You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HMG_Patient_App_New/lib/presentation/lab/alphabeticScroll.dart

236 lines
8.4 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart' show AnimationConfiguration, SlideAnimation, FadeInAnimation;
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/debouncer.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'lab_order_by_test.dart';
class AlphabeticScroll extends StatefulWidget{
final List<String> alpahbetsAvailable;
final List<TestDetails> details;
final AppState appState;
final LabViewModel labViewModel;
final DateRangeSelectorRangeViewModel rangeViewModel;
const AlphabeticScroll({super.key, required this.alpahbetsAvailable, required this.details, required this.appState, required this.labViewModel, required this.rangeViewModel});
@override
State<AlphabeticScroll> createState() => _AlphabetScrollPageState();
}
class _AlphabetScrollPageState extends State<AlphabeticScroll> {
final ItemScrollController itemScrollController = ItemScrollController();
final ScrollOffsetController scrollOffsetController = ScrollOffsetController();
final ItemPositionsListener itemPositionsListener = ItemPositionsListener.create();
final ScrollOffsetListener scrollOffsetListener = ScrollOffsetListener.create();
final ScrollController _scrollController = ScrollController();
Map<String, List<TestDetails>> data = {};
Map<String, num> density = {};
Map<String, double> _offsetMap = {};
Map<String, GlobalKey> _offsetKeys = {};
int _activeIndex = 0; // <-- Highlighted letter
@override
void initState() {
super.initState();
scheduleMicrotask((){
for(var char in widget.alpahbetsAvailable){
data[char] = widget.details.where((element)=>element.description?.toLowerCase().startsWith(char.toLowerCase()) == true).toList();
}
setState((){});
});
itemPositionsListener.itemPositions.addListener((){
final positions = itemPositionsListener.itemPositions.value;
if (positions.isEmpty) return;
// Get FIRST visible item (top-most)
final firstVisible = positions
.where((p) => p.itemTrailingEdge > 0) // visible
.reduce((min, p) =>
p.itemLeadingEdge < min.itemLeadingEdge ? p : min);
if(_activeIndex == firstVisible.index) return ;
setState(() {
_activeIndex = firstVisible.index;
});
print("Active index = $_activeIndex");
});
}
@override
void dispose() {
itemPositionsListener.itemPositions.removeListener((){
});
super.dispose();
}
void _scrollToLetter(String letter) async {
// itemScrollController.jumpTo(index:density[letter]?.toInt()??0, );
itemScrollController.scrollTo(
index: data.keys.toList().indexOf(letter).toInt()??0,
duration: Duration(seconds: 2),
curve: Curves.easeInOutCubic);
// final key = _offsetKeys[letter];
// if (key == null) return;
//
// BuildContext? ctx = key.currentContext;
//
// // Retry until built (max 100ms)
// int retry = 0;
// while (ctx == null && retry < 5) {
// await Future.delayed(Duration(milliseconds: 20));
// ctx = key.currentContext;
// retry++;
// }
//
// if (ctx == null) {
// print("❌ $letter still not built");
// return;
// }
//
// final renderBox = ctx.findRenderObject() as RenderBox;
// final yOffset = renderBox.localToGlobal(Offset.zero).dy;
//
// _scrollController.animateTo(
// _scrollController.offset + yOffset - 80,
// duration: Duration(milliseconds: 400),
// curve: Curves.easeInOut,
// );
}
@override
Widget build(BuildContext context) {
return
SizedBox(
width: MediaQuery.sizeOf(context).width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // Add this
children: [
Expanded(
child: Column(
children: [
SizedBox(
height: (MediaQuery.sizeOf(context).height),
child: _buildList(widget.alpahbetsAvailable)
),
],
),
),
SizedBox(
width: 24.w,
height: MediaQuery.sizeOf(context).height-(70.h+ kToolbarHeight+100.h),
child: Column(
mainAxisSize: MainAxisSize.max, // Add this
mainAxisAlignment: MainAxisAlignment.center, // Changed from center to start
crossAxisAlignment: CrossAxisAlignment.center,
children:List.generate(widget.alpahbetsAvailable.length, (i) {
final isActive = (i == _activeIndex);
return GestureDetector(
onTap: () {
setState(() => _activeIndex = i);
_scrollToLetter(widget.alpahbetsAvailable[i]);
},
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 1.0, end: isActive ? 1.8 : 1.0),
duration: Duration(milliseconds: 120),
curve: Curves.easeOut,
builder: (_, scale, child) {
return Transform.scale(
scale: scale,
child: Opacity(
opacity: isActive ? 1.0 : 0.5,
child: widget.alpahbetsAvailable[i].toUpperCase().toText14(
color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor
),
),
);
},
),
);
}
),
),
),
],
),
);
// );
}
Widget _buildList(List<String> alphabet) {
return ScrollablePositionedList.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemScrollController: itemScrollController,
scrollOffsetController: scrollOffsetController,
itemPositionsListener: itemPositionsListener,
scrollOffsetListener: scrollOffsetListener,
itemCount: data.length,
itemBuilder: (_, index) {
final letter = alphabet[index].toLowerCase();
print("the letter is $letter");
final items = data[letter]!;
return Container(
key: _offsetKeys[letter],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:
List.generate(items.length,(itemIndex)=>
AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
appState: getIt<AppState>(),
onTap: () {
if (items[itemIndex].model != null) {
widget.rangeViewModel.flush();
widget.labViewModel.getPatientLabResult(items[itemIndex].model!, items[itemIndex].description!,
(widget.appState.isArabic() ? items[itemIndex].testDescriptionAr! : items[itemIndex].testDescriptionEn!),"");
}
},
tests: items[itemIndex],
index: itemIndex,
isExpanded: true)),
),
))
// ...items.indexed((item) =>
//
//
//
// )
),
);
},
);
}
}