import 'package:flutter/material.dart'; typedef ItemWidgetBuilder = Widget Function(BuildContext context, T item, int index); typedef ButtonActionCallback = Future Function(T item); class ReusableListCard extends StatelessWidget { final List items; final Widget emptyWidget; final ItemWidgetBuilder itemBuilder; final ButtonActionCallback onActionPressed; final ButtonActionCallback onViewPressed; final String Function(T item) getPrimaryText; final String Function(T item) getSecondaryText; final String Function(T item)? getInTimeText; final String Function(T item)? getOutTimeText; final bool Function(T item)? isUploading; final String Function(T item)? getButtonText; final Color Function(String buttonText)? getButtonColor; const ReusableListCard({ Key? key, required this.items, required this.emptyWidget, required this.itemBuilder, required this.onActionPressed, required this.onViewPressed, required this.getPrimaryText, required this.getSecondaryText, this.getInTimeText, this.getOutTimeText, this.isUploading, this.getButtonText, this.getButtonColor, }) : super(key: key); @override Widget build(BuildContext context) { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), elevation: 1, child: Padding( padding: const EdgeInsets.all(8.0), child: items.isEmpty ? emptyWidget : Column( children: List.generate(items.length, (index) { final item = items[index]; final buttonText = getButtonText?.call(item) ?? 'Action'; final uploading = isUploading?.call(item) ?? false; final buttonColor = getButtonColor?.call(buttonText) ?? Theme.of(context).primaryColor; return Column( children: [ Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: Theme.of(context).cardColor, ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Custom item widget builder for avatar or image itemBuilder(context, item, index), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( getPrimaryText(item), style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 16, ), ), const SizedBox(height: 4), Text( getSecondaryText(item), style: const TextStyle( fontWeight: FontWeight.w400, fontSize: 14, ), ), if (getInTimeText != null && getOutTimeText != null) Padding( padding: const EdgeInsets.only(top: 8), child: Row( children: [ const Text("In: ", style: TextStyle(fontWeight: FontWeight.w600)), Text(getInTimeText!(item), style: const TextStyle(fontWeight: FontWeight.w600)), const SizedBox(width: 16), const Text("Out: ", style: TextStyle(fontWeight: FontWeight.w600)), Text(getOutTimeText!(item), style: const TextStyle(fontWeight: FontWeight.w600)), ], ), ), const SizedBox(height: 12), Row( children: [ SizedBox( height: 30, child: ElevatedButton( onPressed: () => onViewPressed(item), style: ElevatedButton.styleFrom( backgroundColor: Colors.blueGrey, padding: const EdgeInsets.symmetric(horizontal: 12), textStyle: const TextStyle(fontSize: 12), ), child: const Text('View', style: TextStyle(fontSize: 12)), ), ), const SizedBox(width: 8), SizedBox( height: 30, child: ElevatedButton( onPressed: uploading ? null : () => onActionPressed(item), style: ElevatedButton.styleFrom( backgroundColor: buttonColor, padding: const EdgeInsets.symmetric(horizontal: 12), textStyle: const TextStyle(fontSize: 12), ), child: uploading ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text(buttonText, style: const TextStyle(fontSize: 12)), ), ), ], ), ], ), ), ], ), ), ), if (index != items.length - 1) Divider(color: Colors.grey.withOpacity(0.3), thickness: 1, height: 1), ], ); }), ), ), ); } }