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/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 alpahbetsAvailable; final List 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 createState() => _AlphabetScrollPageState(); } class _AlphabetScrollPageState extends State { final ItemScrollController itemScrollController = ItemScrollController(); final ScrollOffsetController scrollOffsetController = ScrollOffsetController(); final ItemPositionsListener itemPositionsListener = ItemPositionsListener.create(); final ScrollOffsetListener scrollOffsetListener = ScrollOffsetListener.create(); final ScrollController _scrollController = ScrollController(); Map> data = {}; Map density = {}; Map _offsetMap = {}; Map _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( 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 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(); 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(), 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) => // // // // ) ), ); }, ); } }