import 'package:flutter/material.dart'; class FeaturePage extends StatelessWidget { final String title; final String subtitle; final Widget child; final Widget? trailing; const FeaturePage({ super.key, required this.title, required this.subtitle, required this.child, this.trailing, }); @override Widget build(BuildContext context) { return SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.w900, color: const Color(0xFF0F172A), ), ), Text( subtitle, style: const TextStyle(color: Color(0xFF64748B)), ), ], ), ), if (trailing != null) trailing!, ], ), const SizedBox(height: 16), Expanded(child: child), ], ), ), ); } } class FeatureEmptyPanel extends StatelessWidget { final IconData icon; final String title; final String message; final Widget? action; const FeatureEmptyPanel({ super.key, required this.icon, required this.title, required this.message, this.action, }); @override Widget build(BuildContext context) { return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 360), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 48, color: const Color(0xFF64748B)), const SizedBox(height: 12), Text( title, textAlign: TextAlign.center, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w800), ), const SizedBox(height: 6), Text( message, textAlign: TextAlign.center, style: const TextStyle(color: Color(0xFF64748B), height: 1.35), ), if (action != null) ...[ const SizedBox(height: 16), action!, ], ], ), ), ); } } class FeatureErrorPanel extends StatelessWidget { final String message; final VoidCallback? onRetry; const FeatureErrorPanel({ super.key, required this.message, this.onRetry, }); @override Widget build(BuildContext context) { return Center( child: Container( width: double.infinity, constraints: const BoxConstraints(maxWidth: 420), padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: const Color(0xFFFEF2F2), borderRadius: BorderRadius.circular(18), border: Border.all(color: const Color(0xFFFECACA)), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, color: Color(0xFFDC2626), size: 34), const SizedBox(height: 10), Text( message, textAlign: TextAlign.center, style: const TextStyle(color: Color(0xFF991B1B), height: 1.35), ), if (onRetry != null) ...[ const SizedBox(height: 12), FilledButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('Coba lagi'), ), ], ], ), ), ); } }