159 lines
7.7 KiB
Dart

import 'package:flutter/material.dart';
typedef ItemWidgetBuilder<T> = Widget Function(BuildContext context, T item, int index);
typedef ButtonActionCallback<T> = Future<bool> Function(T item);
class ReusableListCard<T> extends StatelessWidget {
final List<T> items;
final Widget emptyWidget;
final ItemWidgetBuilder<T> itemBuilder;
final ButtonActionCallback<T> onActionPressed;
final ButtonActionCallback<T> 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<Color>(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),
],
);
}),
),
),
);
}
}