/// Renders the Read State View widget for the FocusFlow Flutter UI. library; import 'package:flutter/material.dart'; import '../controllers/scheduler_read_controller.dart'; class ReadStateView extends StatelessWidget { const ReadStateView({ required this.controller, required this.title, required this.emptyTitle, required this.emptyMessage, required this.dataBuilder, super.key, }); final UiReadController controller; final String title; final String emptyTitle; final String emptyMessage; final Widget Function(BuildContext context, T value) dataBuilder; @override Widget build(BuildContext context) { return AnimatedBuilder( animation: controller, builder: (context, _) { return switch (controller.state) { SchedulerReadLoading() => const Center( child: CircularProgressIndicator(), ), SchedulerReadData(value: final value) => dataBuilder( context, value, ), SchedulerReadEmpty(value: final value) => EmptyStatePanel( title: emptyTitle, message: emptyMessage, child: dataBuilder(context, value), ), SchedulerReadFailure(failure: final failure, error: final error) => FailureStatePanel( title: 'Could not load $title', code: failure?.detailCode ?? failure?.code.name ?? 'unexpected', technicalDetail: error?.toString(), onRetry: controller.retry, ), }; }, ); } } class EmptyStatePanel extends StatelessWidget { const EmptyStatePanel({ required this.title, required this.message, this.child, super.key, }); final String title; final String message; final Widget? child; @override Widget build(BuildContext context) { final panel = Padding( padding: const EdgeInsets.all(16), child: Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), Text(message), ], ), ), ), ); final content = child; if (content == null) { return Center(child: panel); } return Column( children: [ Expanded(child: content), panel, ], ); } } class FailureStatePanel extends StatelessWidget { const FailureStatePanel({ required this.title, required this.code, required this.onRetry, this.technicalDetail, super.key, }); final String title; final String code; final String? technicalDetail; final Future Function() onRetry; @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(16), child: Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), Text('Status: $code'), if (technicalDetail != null) ...[ const SizedBox(height: 8), Text(technicalDetail!), ], const SizedBox(height: 12), FilledButton.icon( onPressed: () async { await onRetry(); }, icon: const Icon(Icons.refresh), label: const Text('Retry'), ), ], ), ), ), ), ); } }