animation added.

main_production_RSG
Sikander Saleem 1 month ago
parent dc893b7836
commit 729b80d4dc

@ -27,10 +27,11 @@ class MultipleSelectionFullScreenDialog<T extends Base> extends StatefulWidget {
_SelectionBottomSheetState createState() => _SelectionBottomSheetState<T>(); _SelectionBottomSheetState createState() => _SelectionBottomSheetState<T>();
} }
class _SelectionBottomSheetState<T extends Base> extends State<MultipleSelectionFullScreenDialog<T>> { class _SelectionBottomSheetState<T extends Base> extends State<MultipleSelectionFullScreenDialog<T>> with SingleTickerProviderStateMixin {
late List<T> _selectedValue; // Now nullable late List<T> _selectedValue; // Now nullable
String query = ""; String query = "";
late AnimationController _animationController;
List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList(); List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList();
@ -40,12 +41,18 @@ class _SelectionBottomSheetState<T extends Base> extends State<MultipleSelection
// This ensures temporary selections don't affect parent until confirmed // This ensures temporary selections don't affect parent until confirmed
_selectedValue = List<T>.from(widget.selectedItem); _selectedValue = List<T>.from(widget.selectedItem);
super.initState(); super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
} }
FocusNode searchFocusNode = FocusNode(); FocusNode searchFocusNode = FocusNode();
@override @override
void dispose() { void dispose() {
_animationController.dispose();
searchFocusNode.dispose(); searchFocusNode.dispose();
super.dispose(); super.dispose();
} }
@ -132,34 +139,67 @@ class _SelectionBottomSheetState<T extends Base> extends State<MultipleSelection
itemCount: filteredList?.length, itemCount: filteredList?.length,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
itemBuilder: (cxt, index) { itemBuilder: (cxt, index) {
// Calculate staggered animation delay
final delay = index * 0.08;
final animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.8),
(delay + 0.3).clamp(0.0, 1.0),
curve: Curves.easeOut,
),
),
);
final slideAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.3),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.8),
(delay + 0.3).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier; bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier;
return CheckboxListTile( return FadeTransition(
value: checkItContains(filteredList![index]), opacity: animation,
dense: true, child: SlideTransition(
controlAffinity: ListTileControlAffinity.leading, position: slideAnimation,
activeColor: AppColor.primary10, child: CheckboxListTile(
checkColor: AppColor.white10, value: checkItContains(filteredList![index]),
// fillColor: WidgetStateProperty.all(AppColor.background(context)), dense: true,
contentPadding: const EdgeInsets.only(left: 16, right: 16), controlAffinity: ListTileControlAffinity.leading,
// activeColor: AppColor.blueStatus(context), activeColor: AppColor.primary10,
side: BorderSide(color: AppColor.icon2Color(context).withValues(alpha: .3), width: 1.0), checkColor: AppColor.white10,
onChanged: (value) { // fillColor: WidgetStateProperty.all(AppColor.background(context)),
// if (checkItContains(filteredList![index]) ?? false) { contentPadding: const EdgeInsets.only(left: 16, right: 16),
// _selectedValue.remove(filteredList![index]); // activeColor: AppColor.blueStatus(context),
// } else { side: BorderSide(color: AppColor.icon2Color(context).withValues(alpha: .3), width: 1.0),
// _selectedValue.add(filteredList![index]); onChanged: (value) {
// } // if (checkItContains(filteredList![index]) ?? false) {
if (value == true) { // _selectedValue.remove(filteredList![index]);
_selectedValue.add(filteredList![index]); // } else {
} else if (value == false) { // _selectedValue.add(filteredList![index]);
_selectedValue.remove(filteredList![index]); // }
} if (value == true) {
searchFocusNode.unfocus(); _selectedValue.add(filteredList![index]);
setState(() {}); } else if (value == false) {
}, _selectedValue.remove(filteredList![index]);
title: Text( }
widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "", searchFocusNode.unfocus();
style: Theme.of(context).textTheme.bodyLarge, setState(() {});
},
title: Text(
widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "",
style: Theme.of(context).textTheme.bodyLarge,
),
),
), ),
); );
}), }),

@ -26,10 +26,11 @@ class SelectionFullScreenDialog<T extends Base> extends StatefulWidget {
_SelectionBottomSheetState createState() => _SelectionBottomSheetState<T>(); _SelectionBottomSheetState createState() => _SelectionBottomSheetState<T>();
} }
class _SelectionBottomSheetState<T extends Base> extends State<SelectionFullScreenDialog<T>> { class _SelectionBottomSheetState<T extends Base> extends State<SelectionFullScreenDialog<T>> with SingleTickerProviderStateMixin {
T? _selectedValue; // Now nullable T? _selectedValue; // Now nullable
String query = ""; String query = "";
late AnimationController _animationController;
List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList(); List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList();
@ -37,12 +38,18 @@ class _SelectionBottomSheetState<T extends Base> extends State<SelectionFullScre
void initState() { void initState() {
_selectedValue = widget.selectedItem; _selectedValue = widget.selectedItem;
super.initState(); super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
} }
FocusNode searchFocusNode = FocusNode(); FocusNode searchFocusNode = FocusNode();
@override @override
void dispose() { void dispose() {
_animationController.dispose();
searchFocusNode.dispose(); searchFocusNode.dispose();
super.dispose(); super.dispose();
} }
@ -129,39 +136,72 @@ class _SelectionBottomSheetState<T extends Base> extends State<SelectionFullScre
itemCount: filteredList?.length, itemCount: filteredList?.length,
padding: EdgeInsets.only(bottom: _selectedValue != null ? 4.toScreenHeight : 48.toScreenHeight), padding: EdgeInsets.only(bottom: _selectedValue != null ? 4.toScreenHeight : 48.toScreenHeight),
itemBuilder: (cxt, index) { itemBuilder: (cxt, index) {
bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier; // Calculate staggered animation delay
return Theme( final delay = index * 0.08;
data: Theme.of(context).copyWith( final animation = Tween<double>(begin: 0.0, end: 1.0).animate(
radioTheme: RadioThemeData( CurvedAnimation(
fillColor: MaterialStateColor.resolveWith((states) { parent: _animationController,
if (states.contains(MaterialState.selected)) { curve: Interval(
return AppColor.primary10; // Active color delay.clamp(0.0, 0.8),
} (delay + 0.3).clamp(0.0, 1.0),
return AppColor.icon2Color(context).withValues(alpha: .5); curve: Curves.easeOut,
}),
), ),
), ),
child: RadioListTile<T>( );
// Specify type for RadioListTile
value: filteredList![index], final slideAnimation = Tween<Offset>(
dense: true, begin: const Offset(0.0, 0.3),
contentPadding: const EdgeInsets.only(left: 16, right: 16), end: Offset.zero,
groupValue: _selectedValue, ).animate(
// activeColor: AppColor.iconColor(context), CurvedAnimation(
// activeColor: AppColor.primary10,colo parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.8),
(delay + 0.3).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier;
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: Theme(
data: Theme.of(context).copyWith(
radioTheme: RadioThemeData(
fillColor: MaterialStateColor.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return AppColor.primary10; // Active color
}
return AppColor.icon2Color(context).withValues(alpha: .5);
}),
),
),
child: RadioListTile<T>(
// Specify type for RadioListTile
value: filteredList![index],
dense: true,
contentPadding: const EdgeInsets.only(left: 16, right: 16),
groupValue: _selectedValue,
// activeColor: AppColor.iconColor(context),
// activeColor: AppColor.primary10,colo
// checkColor: AppColor.white10, // checkColor: AppColor.white10,
hoverColor: Colors.transparent, hoverColor: Colors.transparent,
onChanged: isDisabledItem onChanged: isDisabledItem
? null ? null
: (value) { : (value) {
_selectedValue = value; _selectedValue = value;
searchFocusNode.unfocus(); searchFocusNode.unfocus();
setState(() {}); setState(() {});
}, },
title: Text( title: Text(
widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "", widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "",
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: isDisabledItem ? AppColor.neutral20 : null), style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: isDisabledItem ? AppColor.neutral20 : null),
),
),
), ),
), ),
); );

Loading…
Cancel
Save