animation added.

main_production_RSG
Sikander Saleem 1 month ago
parent 729b80d4dc
commit e98f666caf

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -14,16 +12,89 @@ import 'package:test_sa/helper/utils.dart';
import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/pages/user/notifications/notifications_page.dart'; import 'package:test_sa/views/pages/user/notifications/notifications_page.dart';
import 'package:test_sa/views/pages/user/notifications/unread_chat_list.dart'; import 'package:test_sa/views/pages/user/notifications/unread_chat_list.dart';
import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart';
import '../../controllers/providers/api/user_provider.dart'; import '../../controllers/providers/api/user_provider.dart';
import '../../models/user.dart'; import '../../models/user.dart';
class AppBarWidget extends StatelessWidget { class AppBarWidget extends StatefulWidget {
final VoidCallback onDrawerPress; // Made nullable final VoidCallback onDrawerPress;
const AppBarWidget({Key? key, required this.onDrawerPress}) : super(key: key); const AppBarWidget({Key? key, required this.onDrawerPress}) : super(key: key);
@override
State<AppBarWidget> createState() => _AppBarWidgetState();
}
class _AppBarWidgetState extends State<AppBarWidget> with TickerProviderStateMixin {
late AnimationController _leftController;
late AnimationController _rightController;
late Animation<Offset> _leftSlideAnimation;
late Animation<double> _leftFadeAnimation;
late Animation<Offset> _rightSlideAnimation;
late Animation<double> _rightFadeAnimation;
@override
void initState() {
super.initState();
// Profile section animation (left to right with fade)
_leftController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_leftSlideAnimation = Tween<Offset>(
begin: const Offset(-0.3, 0.0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _leftController,
curve: Curves.easeOutCubic,
));
_leftFadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _leftController,
curve: Curves.easeIn,
));
// Action icons animation (right to left with fade)
_rightController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_rightSlideAnimation = Tween<Offset>(
begin: const Offset(0.3, 0.0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _rightController,
curve: Curves.easeOutCubic,
));
_rightFadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _rightController,
curve: Curves.easeIn,
));
// Start animations with slight delay between them
_leftController.forward();
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted) _rightController.forward();
});
}
@override
void dispose() {
_leftController.dispose();
_rightController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppBar( return AppBar(
@ -35,7 +106,12 @@ class AppBarWidget extends StatelessWidget {
// mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
CircleAvatar( // Profile section with left-to-right fade animation
SlideTransition(
position: _leftSlideAnimation,
child: FadeTransition(
opacity: _leftFadeAnimation,
child: CircleAvatar(
radius: 24, radius: 24,
backgroundColor: context.isDark ? AppColor.neutral50 : AppColor.neutral40, backgroundColor: context.isDark ? AppColor.neutral50 : AppColor.neutral40,
child: Padding( child: Padding(
@ -48,9 +124,15 @@ class AppBarWidget extends StatelessWidget {
: const Icon(Icons.person, size: 24, color: Colors.white), : const Icon(Icons.person, size: 24, color: Colors.white),
), ),
), ),
).onPress(onDrawerPress), // Handle potential null ).onPress(widget.onDrawerPress),
),
),
8.width, 8.width,
Column( SlideTransition(
position: _leftSlideAnimation,
child: FadeTransition(
opacity: _leftFadeAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4, spacing: 4,
children: [ children: [
@ -64,8 +146,15 @@ class AppBarWidget extends StatelessWidget {
), ),
], ],
).expanded, ).expanded,
),
),
const Spacer(), const Spacer(),
Consumer<SettingProvider>(builder: (context, settingProvider, child) { // Action icons with right-to-left fade animation
SlideTransition(
position: _rightSlideAnimation,
child: FadeTransition(
opacity: _rightFadeAnimation,
child: Consumer<SettingProvider>(builder: (context, settingProvider, child) {
return MenuAnchor( return MenuAnchor(
style: MenuStyle( style: MenuStyle(
backgroundColor: WidgetStateProperty.all(AppColor.background(context)), backgroundColor: WidgetStateProperty.all(AppColor.background(context)),
@ -229,7 +318,15 @@ class AppBarWidget extends StatelessWidget {
), ),
); );
}), }),
),
),
16.width, 16.width,
SlideTransition(
position: _rightSlideAnimation,
child: FadeTransition(
opacity: _rightFadeAnimation,
child: Row(
children: [
if (context.settingProvider.isUserFlowMedical) ...[ if (context.settingProvider.isUserFlowMedical) ...[
("dashboard/chat").toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)).paddingOnly(top: 0, end: 0).onPress(() { ("dashboard/chat").toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)).paddingOnly(top: 0, end: 0).onPress(() {
Navigator.of(context).push(CupertinoPageRoute(builder: (_) => UnReadChatList())); Navigator.of(context).push(CupertinoPageRoute(builder: (_) => UnReadChatList()));
@ -240,6 +337,10 @@ class AppBarWidget extends StatelessWidget {
Navigator.of(context).pushNamed(NotificationsPage.id); Navigator.of(context).pushNamed(NotificationsPage.id);
}), }),
], ],
),
),
),
],
).paddingOnly(start: 16, end: 16); ).paddingOnly(start: 16, end: 16);
}), }),
); );

@ -154,7 +154,6 @@ import 'package:flutter/material.dart';
import 'package:pie_chart/pie_chart.dart'; import 'package:pie_chart/pie_chart.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/dashboard_latest/dashboard_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart';
@ -164,11 +163,53 @@ import 'package:test_sa/new_views/app_style/app_color.dart';
import '../../controllers/providers/api/user_provider.dart'; import '../../controllers/providers/api/user_provider.dart';
import '../../models/enums/user_types.dart'; import '../../models/enums/user_types.dart';
class ProgressFragment extends StatelessWidget { class ProgressFragment extends StatefulWidget {
ProgressFragment({Key? key}) : super(key: key); const ProgressFragment({Key? key}) : super(key: key);
@override
State<ProgressFragment> createState() => _ProgressFragmentState();
}
class _ProgressFragmentState extends State<ProgressFragment> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _fadeAnimation;
late Animation<double> _scaleAnimation;
late UserProvider _userProvider; late UserProvider _userProvider;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.6, curve: Curves.easeIn),
));
_scaleAnimation = Tween<double>(
begin: 0.92,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.8, curve: Curves.easeOutCubic),
));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_userProvider = Provider.of<UserProvider>(context); _userProvider = Provider.of<UserProvider>(context);
@ -200,7 +241,11 @@ class ProgressFragment extends StatelessWidget {
]; ];
} }
return Column( return FadeTransition(
opacity: _fadeAnimation,
child: ScaleTransition(
scale: _scaleAnimation,
child: Column(
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: (359.toScreenWidth / 214.toScreenWidth), aspectRatio: (359.toScreenWidth / 214.toScreenWidth),
@ -300,7 +345,9 @@ class ProgressFragment extends StatelessWidget {
).toShadowContainer(context, borderRadius: 20, paddingObject: const EdgeInsets.symmetric(horizontal: 24, vertical: 16)), ).toShadowContainer(context, borderRadius: 20, paddingObject: const EdgeInsets.symmetric(horizontal: 24, vertical: 16)),
), ),
], ],
).paddingOnly(start: 16, end: 16); ).paddingOnly(start: 16, end: 16),
),
);
}, },
); );
} }

@ -1,4 +1,3 @@
import 'package:date_picker_timeline/extra/color.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart';
@ -23,8 +22,25 @@ class RequestCategoryFragment extends StatefulWidget {
State<RequestCategoryFragment> createState() => _RequestCategoryFragmentState(); State<RequestCategoryFragment> createState() => _RequestCategoryFragmentState();
} }
class _RequestCategoryFragmentState extends State<RequestCategoryFragment> { class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with SingleTickerProviderStateMixin {
bool isAscending = false; bool isAscending = false;
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _toggleSort(DashBoardProvider dashboardProvider, UsersTypes? userType) { void _toggleSort(DashBoardProvider dashboardProvider, UsersTypes? userType) {
setState(() { setState(() {
@ -163,7 +179,38 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> {
shrinkWrap: true, shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: context.isTablet() ? 5 : 4, childAspectRatio: 85 / 85, crossAxisSpacing: 8, mainAxisSpacing: 8), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: context.isTablet() ? 5 : 4, childAspectRatio: 85 / 85, crossAxisSpacing: 8, mainAxisSpacing: 8),
itemBuilder: (cxt, index) { itemBuilder: (cxt, index) {
return RequestTypeButton( // 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,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: RequestTypeButton(
label: tabs[index].label, label: tabs[index].label,
loading: requestsProvider.isDetailLoading && requestsProvider.currentListIndex == index, loading: requestsProvider.isDetailLoading && requestsProvider.currentListIndex == index,
isSelected: requestsProvider.currentListIndex == index, isSelected: requestsProvider.currentListIndex == index,
@ -185,6 +232,8 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> {
}, },
icon: tabs[index].icon, icon: tabs[index].icon,
iconColor: tabs[index].iconColor, iconColor: tabs[index].iconColor,
),
),
); );
}, },
itemCount: tabs.length, itemCount: tabs.length,

@ -1,5 +1,3 @@
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart';
@ -19,26 +17,83 @@ import 'package:test_sa/new_views/pages/land_page/requests/ppm_item_view.dart';
import 'package:test_sa/new_views/pages/land_page/requests/recurrent_wo_item_view.dart'; import 'package:test_sa/new_views/pages/land_page/requests/recurrent_wo_item_view.dart';
import 'package:test_sa/new_views/pages/land_page/requests/service_request_item_view.dart'; import 'package:test_sa/new_views/pages/land_page/requests/service_request_item_view.dart';
class RequestCategoryList extends StatelessWidget { class RequestCategoryList extends StatefulWidget {
final List<Data> list; final List<Data> list;
final int totalCount; final int totalCount;
final bool isLoading; final bool isLoading;
const RequestCategoryList(this.list, this.isLoading, this.totalCount, {Key? key}) : super(key: key); const RequestCategoryList(this.list, this.isLoading, this.totalCount, {Key? key}) : super(key: key);
@override
State<RequestCategoryList> createState() => _RequestCategoryListState();
}
class _RequestCategoryListState extends State<RequestCategoryList> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView.separated( return ListView.separated(
itemCount: list.length < totalCount ? list.length + 1 : list.length, itemCount: widget.list.length < widget.totalCount ? widget.list.length + 1 : widget.list.length,
shrinkWrap: true, shrinkWrap: true,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (cxt, index) => 12.height, separatorBuilder: (cxt, index) => 12.height,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == list.length) { if (index == widget.list.length) {
return CircularProgressIndicator(color: AppColor.loadingColor(context), strokeWidth: 1).center; return CircularProgressIndicator(color: AppColor.loadingColor(context), strokeWidth: 1).center;
} }
return _buildRequestItem(list[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.7),
(delay + 0.4).clamp(0.0, 1.0),
curve: Curves.easeOut,
),
),
);
final slideAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.15),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.7),
(delay + 0.4).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: _buildRequestItem(widget.list[index]),
),
);
}, },
); );
} }

@ -22,9 +22,32 @@ import 'package:test_sa/modules/traf_module/create_traf_request_page.dart';
import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/modules/tm_module/gas_refill/gas_refill_request_form.dart'; import 'package:test_sa/modules/tm_module/gas_refill/gas_refill_request_form.dart';
class CreateRequestTypeBottomSheet extends StatelessWidget { class CreateRequestTypeBottomSheet extends StatefulWidget {
const CreateRequestTypeBottomSheet({super.key}); const CreateRequestTypeBottomSheet({super.key});
@override
State<CreateRequestTypeBottomSheet> createState() => _CreateRequestTypeBottomSheetState();
}
class _CreateRequestTypeBottomSheetState extends State<CreateRequestTypeBottomSheet> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<CreateRequestModel> requestList = CreateRequestModel.requestsList(context); List<CreateRequestModel> requestList = CreateRequestModel.requestsList(context);
@ -40,28 +63,60 @@ class CreateRequestTypeBottomSheet extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
context.translation.selectWorkOrder.heading4(context), context.translation.selectWorkOrder.heading4(context),
GridView( GridView.builder(
padding: const EdgeInsets.only(top: 16, bottom: 0), padding: const EdgeInsets.only(top: 16, bottom: 0),
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 16, mainAxisSpacing: 16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 16, mainAxisSpacing: 16),
children: <Widget>[ itemCount: requestList.length,
for (var request in requestList) itemBuilder: (context, index) {
listCard( // 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,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: listCard(
context: context, context: context,
icon: request.icon, icon: requestList[index].icon,
label: request.title, label: requestList[index].title,
onTap: () { onTap: () {
Navigator.pushReplacementNamed(context, request.routeName); Navigator.pushReplacementNamed(context, requestList[index].routeName);
}, },
), ),
], ),
);
},
), ),
], ],
), ),
), ),
); );
return const Placeholder();
} }
Widget listCard({required String icon, required String label, required VoidCallback onTap, required BuildContext context}) { Widget listCard({required String icon, required String label, required VoidCallback onTap, required BuildContext context}) {

@ -31,12 +31,51 @@ class MyRequestsPage extends StatefulWidget {
State<MyRequestsPage> createState() => _MyRequestsPageState(); State<MyRequestsPage> createState() => _MyRequestsPageState();
} }
class _MyRequestsPageState extends State<MyRequestsPage> { class _MyRequestsPageState extends State<MyRequestsPage> with SingleTickerProviderStateMixin {
AllRequestsProvider? _provider; AllRequestsProvider? _provider;
SearchAllRequestsModel? _search; SearchAllRequestsModel? _search;
late List<Request> requestsList; late List<Request> requestsList;
int selectedRequest = 0; int selectedRequest = 0;
late AnimationController _tabAnimationController;
late Animation<double> _tabContainerAnimation;
late Animation<Offset> _tabContainerSlideAnimation;
int _listAnimationKey = 0; // Key to force list re-animation
@override
void initState() {
super.initState();
_tabAnimationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
// Container entrance animation (faster than individual tabs)
_tabContainerAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _tabAnimationController,
curve: const Interval(0.0, 0.4, curve: Curves.easeOut),
),
);
_tabContainerSlideAnimation = Tween<Offset>(
begin: const Offset(0.0, -0.5),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _tabAnimationController,
curve: const Interval(0.0, 0.5, curve: Curves.easeOutCubic),
),
);
_tabAnimationController.forward();
}
@override
void dispose() {
_tabAnimationController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -190,26 +229,52 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
children: [ children: [
if (!_filterIsEmpty()) _buildActiveFiltersChips(), if (!_filterIsEmpty()) _buildActiveFiltersChips(),
if (_filterIsEmpty()) if (_filterIsEmpty())
SizedBox( FadeTransition(
opacity: _tabContainerAnimation,
child: SlideTransition(
position: _tabContainerSlideAnimation,
child: SizedBox(
height: 50.toScreenHeight, height: 50.toScreenHeight,
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(top: 16, right: 16, left: 16), padding: const EdgeInsets.only(top: 16, right: 16, left: 16),
itemBuilder: (cxt, index) => Container( 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: _tabAnimationController,
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: _tabAnimationController,
curve: Interval(
delay.clamp(0.0, 0.8),
(delay + 0.3).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: Container(
alignment: Alignment.center, alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
// foregroundDecoration: selectedRequest != index
// ? null
// : ShapeDecoration(
// color: (AppColor.red20),
// shape: RoundedRectangleBorder(
// side: BorderSide(width: 1, color: AppColor.blueStatus(context)),
// borderRadius: BorderRadius.circular(7),
// ),
// ),
decoration: ShapeDecoration( decoration: ShapeDecoration(
color: //selectedRequest == index ? color: AppColor.background(context),
AppColor.background(context), //: AppColor.unSelectedButtonColor(context).withOpacity(snapshot.isAllLoading ? 0.7 : 1),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: selectedRequest == index ? AppColor.primary10 : AppColor.borderColor(context)), side: BorderSide(width: 1, color: selectedRequest == index ? AppColor.primary10 : AppColor.borderColor(context)),
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
@ -225,24 +290,32 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
return; return;
} }
if (selectedRequest != index) {
setState(() { setState(() {
selectedRequest = index; selectedRequest = index;
_listAnimationKey++; // Force list to re-animate
}); });
_provider!.reset(); _provider!.reset();
_provider!.getAllRequests(context, typeTransaction: requestsList[selectedRequest].id); _provider!.getAllRequests(context, typeTransaction: requestsList[selectedRequest].id);
}
}), }),
),
);
},
separatorBuilder: (cxt, index) => 8.width, separatorBuilder: (cxt, index) => 8.width,
itemCount: requestsList.length), itemCount: requestsList.length),
), ),
),
),
LazyLoading( LazyLoading(
nextPage: snapshot.nextPage, nextPage: snapshot.nextPage,
onLazyLoad: () async { onLazyLoad: () async {
snapshot.getAllRequests(context, typeTransaction: requestsList[selectedRequest].id, search: _search); snapshot.getAllRequests(context, typeTransaction: requestsList[selectedRequest].id, search: _search);
}, },
child: RequestItemViewList(snapshot.allRequestsAndCount?.requestsDetails ?? [], snapshot.isAllLoading), child: RequestItemViewList(
snapshot.allRequestsAndCount?.requestsDetails ?? [],
snapshot.isAllLoading,
key: ValueKey('request_list_${selectedRequest}_$_listAnimationKey'), // Key changes on every tab click
),
).expanded, ).expanded,
], ],
); );

@ -1,5 +1,4 @@
//request tab page //request tab page
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/context_extension.dart';
@ -16,7 +15,6 @@ import 'package:test_sa/modules/loan_module/pages/loan_equipment_item_view.dart'
import 'package:test_sa/modules/medical_gas_inspection/medical_gas_inspection_item_view.dart'; import 'package:test_sa/modules/medical_gas_inspection/medical_gas_inspection_item_view.dart';
import 'package:test_sa/modules/tm_module/tasks/task_request_item_view.dart'; import 'package:test_sa/modules/tm_module/tasks/task_request_item_view.dart';
import 'package:test_sa/modules/traf_module/traf_request_item_view.dart'; import 'package:test_sa/modules/traf_module/traf_request_item_view.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/pages/land_page/requests/device_item_view.dart'; import 'package:test_sa/new_views/pages/land_page/requests/device_item_view.dart';
import 'package:test_sa/new_views/pages/land_page/requests/gas_refill_item_view.dart'; import 'package:test_sa/new_views/pages/land_page/requests/gas_refill_item_view.dart';
import 'package:test_sa/new_views/pages/land_page/requests/ppm_item_view.dart'; import 'package:test_sa/new_views/pages/land_page/requests/ppm_item_view.dart';
@ -24,67 +22,141 @@ import 'package:test_sa/new_views/pages/land_page/requests/recurrent_wo_item_vie
import 'package:test_sa/new_views/pages/land_page/requests/service_request_item_view.dart'; import 'package:test_sa/new_views/pages/land_page/requests/service_request_item_view.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
class RequestItemViewList extends StatelessWidget { class RequestItemViewList extends StatefulWidget {
List<RequestsDetails> list; final List<RequestsDetails> list;
bool isLoading; final bool isLoading;
RequestItemViewList(this.list, this.isLoading, {Key? key}) : super(key: key); const RequestItemViewList(this.list, this.isLoading, {Key? key}) : super(key: key);
@override
State<RequestItemViewList> createState() => _RequestItemViewListState();
}
class _RequestItemViewListState extends State<RequestItemViewList> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return (list.isEmpty && !isLoading) return (widget.list.isEmpty && !widget.isLoading)
? NoDataFound(message: context.translation.noDataFound).center ? NoDataFound(message: context.translation.noDataFound).center
: ListView.separated( : ListView.separated(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
shrinkWrap: true, shrinkWrap: true,
itemBuilder: (cxt, index) { itemBuilder: (cxt, index) {
if (isLoading) return const SizedBox().toRequestShimmer(cxt, isLoading); if (widget.isLoading) return const SizedBox().toRequestShimmer(cxt, widget.isLoading);
// 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,
),
),
);
switch (list[index].transactionType) { Widget itemWidget;
switch (widget.list[index].transactionType) {
case 1: case 1:
return ServiceRequestItemView(requestDetails: list[index]); itemWidget = ServiceRequestItemView(requestDetails: widget.list[index]);
// case 1: break;
// return InternalAuditItemView(requestDetails: list[index]);
case 2: case 2:
return GasRefillItemView(requestDetails: list[index]); itemWidget = GasRefillItemView(requestDetails: widget.list[index]);
break;
case 3: case 3:
return DeviceItemView(requestDetails: list[index]); itemWidget = DeviceItemView(requestDetails: widget.list[index]);
break;
case 4: case 4:
return PpmItemView(requestDetails: list[index]); itemWidget = PpmItemView(requestDetails: widget.list[index]);
break;
case 5: case 5:
return RecurrentWoItemView(requestDetails: list[index]); itemWidget = RecurrentWoItemView(requestDetails: widget.list[index]);
break;
case 6: case 6:
return TaskRequestItemView(requestDetails: list[index]); itemWidget = TaskRequestItemView(requestDetails: widget.list[index]);
break;
case 7: case 7:
return TaskRequestItemView(requestDetails: list[index]); itemWidget = TaskRequestItemView(requestDetails: widget.list[index]);
break;
case 8: case 8:
return InventorySessionItemView(requestDetails: list[index]); itemWidget = InventorySessionItemView(requestDetails: widget.list[index]);
break;
case 9: case 9:
return TrafRequestItemView(requestDetails: list[index]); itemWidget = TrafRequestItemView(requestDetails: widget.list[index]);
//Need to verify the type No.. break;
case 10: case 10:
return EquipmentInternalAuditItemView(requestDetails: list[index]); itemWidget = EquipmentInternalAuditItemView(requestDetails: widget.list[index]);
break;
case 11: case 11:
return SystemInternalAuditItemView(requestDetails: list[index]); itemWidget = SystemInternalAuditItemView(requestDetails: widget.list[index]);
break;
case 12: case 12:
return LoanEquipmentItemView(requestDetails: list[index]); itemWidget = LoanEquipmentItemView(requestDetails: widget.list[index]);
break;
case 13: case 13:
return AssetDeliveryItemView(requestDetails: list[index]); itemWidget = AssetDeliveryItemView(requestDetails: widget.list[index]);
break;
case 14: case 14:
return MedicalGasInspectionItemView(requestDetails: list[index]); itemWidget = MedicalGasInspectionItemView(requestDetails: widget.list[index]);
break;
case 15: case 15:
return DemoRequestItemView(requestDetails: list[index]); itemWidget = DemoRequestItemView(requestDetails: widget.list[index]);
break;
case 16: case 16:
return IncidentItemView(requestDetails: list[index]); itemWidget = IncidentItemView(requestDetails: widget.list[index]);
break;
default: default:
Container( itemWidget = Container(
height: 100, height: 100,
width: double.infinity, width: double.infinity,
color: Colors.grey, color: Colors.grey,
); );
} }
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: itemWidget,
),
);
}, },
separatorBuilder: (cxt, index) => 12.height, separatorBuilder: (cxt, index) => 12.height,
itemCount: isLoading ? 6 : list.length); itemCount: widget.isLoading ? 6 : widget.list.length);
} }
} }

@ -35,7 +35,7 @@ class MyAssetsPage extends StatefulWidget {
_MyAssetsPageState createState() => _MyAssetsPageState(); _MyAssetsPageState createState() => _MyAssetsPageState();
} }
class _MyAssetsPageState extends State<MyAssetsPage> { class _MyAssetsPageState extends State<MyAssetsPage> with SingleTickerProviderStateMixin {
late AssetProvider _devicesProvider; late AssetProvider _devicesProvider;
late UserProvider _userProvider; late UserProvider _userProvider;
late SettingProvider _settingProvider; late SettingProvider _settingProvider;
@ -45,6 +45,7 @@ class _MyAssetsPageState extends State<MyAssetsPage> {
DeviceSearch? _searchAsset; DeviceSearch? _searchAsset;
bool showListView = true; bool showListView = true;
late AnimationController _animationController;
TextEditingController numberController = TextEditingController(); TextEditingController numberController = TextEditingController();
TextEditingController snController = TextEditingController(); TextEditingController snController = TextEditingController();
@ -59,12 +60,18 @@ class _MyAssetsPageState extends State<MyAssetsPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
} }
@override @override
void dispose() { void dispose() {
numberController.dispose(); numberController.dispose();
snController.dispose(); snController.dispose();
_animationController.dispose();
_devicesProvider.reset(); _devicesProvider.reset();
super.dispose(); super.dispose();
} }
@ -193,29 +200,96 @@ class _MyAssetsPageState extends State<MyAssetsPage> {
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
child: showListView child: showListView
? ListView.separated( ? ListView.separated(
key: const ValueKey('list_view'),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemCount: _searchableList.length, itemCount: _searchableList.length,
separatorBuilder: (listContext, itemIndex) => 12.height, separatorBuilder: (listContext, itemIndex) => 12.height,
itemBuilder: (listContext, itemIndex) { itemBuilder: (listContext, itemIndex) {
return AssetItemListView( // Calculate staggered animation delay
final delay = itemIndex * 0.08;
final animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.7),
(delay + 0.4).clamp(0.0, 1.0),
curve: Curves.easeOut,
),
),
);
final slideAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.15),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.7),
(delay + 0.4).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: AssetItemListView(
device: _searchableList[itemIndex], device: _searchableList[itemIndex],
onPressed: (device) { onPressed: (device) {
widget.fromBottomBar ? Navigator.of(context).pushNamed(AssetDetailPage.id, arguments: device.id) : Navigator.of(context).pop(device); widget.fromBottomBar ? Navigator.of(context).pushNamed(AssetDetailPage.id, arguments: device.id) : Navigator.of(context).pop(device);
// Navigator.of(context).pushNamed(AssetDetailPage.id, arguments: device.id);
}, },
),
),
); );
}, },
) )
: GridView.builder( : GridView.builder(
key: const ValueKey('grid_view'),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 188 / 261, crossAxisSpacing: 12, mainAxisSpacing: 12), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 188 / 261, crossAxisSpacing: 12, mainAxisSpacing: 12),
itemCount: _searchableList.length, itemCount: _searchableList.length,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return AssetItemGridView( // 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,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: AssetItemGridView(
device: _searchableList[index], device: _searchableList[index],
onPressed: (device) { onPressed: (device) {
widget.fromBottomBar ? Navigator.of(context).pushNamed(AssetDetailPage.id, arguments: device.id) : Navigator.of(context).pop(device); widget.fromBottomBar ? Navigator.of(context).pushNamed(AssetDetailPage.id, arguments: device.id) : Navigator.of(context).pop(device);
}, },
),
),
); );
}, },
), ),

Loading…
Cancel
Save