/// Performance-optimized GridView implementation /// /// Features: /// - Automatic RepaintBoundary for grid items /// - Optimized scrolling physics /// - Responsive column count /// - Efficient caching and preloading /// - Proper key management for widget identity import 'package:flutter/material.dart'; import '../constants/performance_constants.dart'; /// Optimized GridView.builder with performance enhancements class OptimizedGridView extends StatelessWidget { final List items; final Widget Function(BuildContext context, T item, int index) itemBuilder; final ScrollController? scrollController; final EdgeInsets? padding; final bool shrinkWrap; final ScrollPhysics? physics; final double? crossAxisSpacing; final double? mainAxisSpacing; final double? childAspectRatio; final int? crossAxisCount; final bool useRepaintBoundary; const OptimizedGridView({ super.key, required this.items, required this.itemBuilder, this.scrollController, this.padding, this.shrinkWrap = false, this.physics, this.crossAxisSpacing, this.mainAxisSpacing, this.childAspectRatio, this.crossAxisCount, this.useRepaintBoundary = true, }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final screenHeight = MediaQuery.of(context).size.height; return GridView.builder( controller: scrollController, padding: padding ?? const EdgeInsets.all(12), shrinkWrap: shrinkWrap, // Optimized physics for smooth scrolling physics: physics ?? const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), // Performance optimization: preload items cacheExtent: PerformanceConstants.getCacheExtent(screenHeight), itemCount: items.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount ?? PerformanceConstants.getGridColumnCount(screenWidth), crossAxisSpacing: crossAxisSpacing ?? PerformanceConstants.gridSpacing, mainAxisSpacing: mainAxisSpacing ?? PerformanceConstants.gridSpacing, childAspectRatio: childAspectRatio ?? PerformanceConstants.productCardAspectRatio, ), itemBuilder: (context, index) { final item = items[index]; final child = itemBuilder(context, item, index); // Wrap in RepaintBoundary for better performance return useRepaintBoundary ? RepaintBoundary( // Use ValueKey for stable widget identity key: ValueKey('grid_item_$index'), child: child, ) : child; }, ); } } /// Optimized GridView for products class ProductGridView extends StatelessWidget { final List products; final Widget Function(BuildContext context, T product, int index) itemBuilder; final ScrollController? scrollController; final VoidCallback? onScrollEnd; const ProductGridView({ super.key, required this.products, required this.itemBuilder, this.scrollController, this.onScrollEnd, }); @override Widget build(BuildContext context) { final controller = scrollController ?? ScrollController(); // Add scroll listener for infinite scroll if (onScrollEnd != null) { controller.addListener(() { if (controller.position.pixels >= controller.position.maxScrollExtent - 200) { onScrollEnd!(); } }); } return OptimizedGridView( items: products, itemBuilder: itemBuilder, scrollController: controller, childAspectRatio: PerformanceConstants.productCardAspectRatio, ); } } /// Optimized GridView for categories class CategoryGridView extends StatelessWidget { final List categories; final Widget Function(BuildContext context, T category, int index) itemBuilder; final ScrollController? scrollController; const CategoryGridView({ super.key, required this.categories, required this.itemBuilder, this.scrollController, }); @override Widget build(BuildContext context) { return OptimizedGridView( items: categories, itemBuilder: itemBuilder, scrollController: scrollController, childAspectRatio: PerformanceConstants.categoryCardAspectRatio, ); } } /// Optimized sliver grid for use in CustomScrollView class OptimizedSliverGrid extends StatelessWidget { final List items; final Widget Function(BuildContext context, T item, int index) itemBuilder; final double? crossAxisSpacing; final double? mainAxisSpacing; final double? childAspectRatio; final int? crossAxisCount; final bool useRepaintBoundary; const OptimizedSliverGrid({ super.key, required this.items, required this.itemBuilder, this.crossAxisSpacing, this.mainAxisSpacing, this.childAspectRatio, this.crossAxisCount, this.useRepaintBoundary = true, }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; return SliverGrid( delegate: SliverChildBuilderDelegate( (context, index) { final item = items[index]; final child = itemBuilder(context, item, index); return useRepaintBoundary ? RepaintBoundary( key: ValueKey('sliver_grid_item_$index'), child: child, ) : child; }, childCount: items.length, ), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount ?? PerformanceConstants.getGridColumnCount(screenWidth), crossAxisSpacing: crossAxisSpacing ?? PerformanceConstants.gridSpacing, mainAxisSpacing: mainAxisSpacing ?? PerformanceConstants.gridSpacing, childAspectRatio: childAspectRatio ?? PerformanceConstants.productCardAspectRatio, ), ); } } /// Empty state widget for grids class GridEmptyState extends StatelessWidget { final String message; final IconData icon; final VoidCallback? onRetry; const GridEmptyState({ super.key, required this.message, this.icon = Icons.inventory_2_outlined, this.onRetry, }); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(32.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: 80, color: Colors.grey[400], ), const SizedBox(height: 16), Text( message, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Colors.grey[600], ), textAlign: TextAlign.center, ), if (onRetry != null) ...[ const SizedBox(height: 24), ElevatedButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('Retry'), ), ], ], ), ), ); } } /// Loading state for grid class GridLoadingState extends StatelessWidget { final int itemCount; const GridLoadingState({ super.key, this.itemCount = 6, }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final crossAxisCount = PerformanceConstants.getGridColumnCount(screenWidth); return GridView.builder( padding: const EdgeInsets.all(12), physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, crossAxisSpacing: PerformanceConstants.gridSpacing, mainAxisSpacing: PerformanceConstants.gridSpacing, childAspectRatio: PerformanceConstants.productCardAspectRatio, ), itemCount: itemCount, itemBuilder: (context, index) { return const GridShimmerItem(); }, ); } } /// Shimmer item for grid loading state class GridShimmerItem extends StatelessWidget { const GridShimmerItem({super.key}); @override Widget build(BuildContext context) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 3, child: Container( decoration: BoxDecoration( color: Colors.grey[200], borderRadius: const BorderRadius.vertical( top: Radius.circular(12), ), ), ), ), Expanded( flex: 2, child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: 16, width: double.infinity, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(4), ), ), const SizedBox(height: 8), Container( height: 14, width: 80, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(4), ), ), ], ), ), ), ], ), ); } }