// lib/ui/widgets/server_error_page.dart import 'package:flutter/material.dart'; class ServerErrorPage extends StatelessWidget { final String? errorMessage; final VoidCallback? onRetry; final String? title; final String? subtitle; final bool showLogo; const ServerErrorPage({ Key? key, this.errorMessage, this.onRetry, this.title, this.subtitle, this.showLogo = true, }) : super(key: key); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 48.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ // PinePods Logo if (showLogo) ...[ ClipRRect( borderRadius: BorderRadius.circular(16), child: Image.asset( 'assets/images/pinepods-logo.png', width: 120, height: 120, fit: BoxFit.contain, errorBuilder: (context, error, stackTrace) { // Fallback if logo image fails to load return Container( width: 120, height: 120, decoration: BoxDecoration( color: Theme.of(context).primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(16), ), child: Icon( Icons.podcasts, size: 64, color: Theme.of(context).primaryColor, ), ); }, ), ), const SizedBox(height: 32), ], // Error Icon Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.errorContainer.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( Icons.cloud_off_rounded, size: 48, color: Theme.of(context).colorScheme.error, ), ), const SizedBox(height: 24), // Title Text( title ?? 'Server Unavailable', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onSurface, ), textAlign: TextAlign.center, ), const SizedBox(height: 12), // Subtitle Text( subtitle ?? 'Unable to connect to the PinePods server', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), ), textAlign: TextAlign.center, ), const SizedBox(height: 20), // Error Message (if provided) if (errorMessage != null && errorMessage!.isNotEmpty) ...[ Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.errorContainer.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all( color: Theme.of(context).colorScheme.error.withOpacity(0.2), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.info_outline, size: 16, color: Theme.of(context).colorScheme.error, ), const SizedBox(width: 6), Text( 'Error Details', style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w600, color: Theme.of(context).colorScheme.error, ), ), ], ), const SizedBox(height: 8), Text( errorMessage!, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onErrorContainer, ), ), ], ), ), const SizedBox(height: 24), ], // Troubleshooting suggestions Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all( color: Theme.of(context).colorScheme.primary.withOpacity(0.2), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.lightbulb_outline, size: 16, color: Theme.of(context).colorScheme.primary, ), const SizedBox(width: 6), Text( 'Troubleshooting Tips', style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w600, color: Theme.of(context).colorScheme.primary, ), ), ], ), const SizedBox(height: 8), _buildTroubleshootingTip(context, '• Check your internet connection'), _buildTroubleshootingTip(context, '• Verify server settings in the app'), _buildTroubleshootingTip(context, '• Ensure the PinePods server is running'), _buildTroubleshootingTip(context, '• Contact your administrator if the issue persists'), ], ), ), const SizedBox(height: 32), // Action Buttons if (onRetry != null) SizedBox( width: double.infinity, child: FilledButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('Retry'), style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), ), ), ), ], ), ); } Widget _buildTroubleshootingTip(BuildContext context, String tip) { return Padding( padding: const EdgeInsets.only(bottom: 4), child: Text( tip, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ); } } /// A specialized server error page for SliverFillRemaining usage class SliverServerErrorPage extends StatelessWidget { final String? errorMessage; final VoidCallback? onRetry; final String? title; final String? subtitle; final bool showLogo; const SliverServerErrorPage({ Key? key, this.errorMessage, this.onRetry, this.title, this.subtitle, this.showLogo = true, }) : super(key: key); @override Widget build(BuildContext context) { return SliverFillRemaining( hasScrollBody: false, child: ServerErrorPage( errorMessage: errorMessage, onRetry: onRetry, title: title, subtitle: subtitle, showLogo: showLogo, ), ); } }