import 'package:flutter/material.dart'; import '../theme/app_theme.dart'; class EntryPanel extends StatelessWidget { const EntryPanel({ super.key, required this.title, required this.subtitle, required this.child, }); final String title; final String subtitle; final Widget child; @override Widget build(BuildContext context) => Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: AppTheme.inputSurface, borderRadius: BorderRadius.circular(24), border: Border.all(color: const Color(0xFFCEE6DF)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( title, style: const TextStyle(fontSize: 25, fontWeight: FontWeight.w700), ), const SizedBox(height: 8), Text( subtitle, style: const TextStyle(color: AppTheme.muted, height: 1.5), ), const SizedBox(height: 24), child, ], ), ); } class EntryMessage extends StatelessWidget { const EntryMessage({ super.key, required this.message, required this.hasError, }); final String? message; final bool hasError; @override Widget build(BuildContext context) => message == null ? const SizedBox.shrink() : Padding( padding: const EdgeInsets.only(top: 16), child: Semantics( liveRegion: true, child: Text( message!, style: TextStyle( color: hasError ? Theme.of(context).colorScheme.error : AppTheme.teal, ), ), ), ); } class EntryNumberField extends StatelessWidget { const EntryNumberField({ super.key, required this.controller, required this.label, required this.unit, required this.validator, }); final TextEditingController controller; final String label; final String unit; final FormFieldValidator validator; @override Widget build(BuildContext context) => TextFormField( controller: controller, keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: InputDecoration(labelText: label, suffixText: unit), validator: validator, ); } class EntryFieldGrid extends StatelessWidget { const EntryFieldGrid({super.key, required this.children}); final List children; @override Widget build(BuildContext context) => LayoutBuilder( builder: (context, constraints) { final columns = constraints.maxWidth >= 600 ? 2 : 1; final width = (constraints.maxWidth - (columns - 1) * 16) / columns; return Wrap( spacing: 16, runSpacing: 16, children: children .map((child) => SizedBox(width: width, child: child)) .toList(), ); }, ); } /// Lazily builds saved entries below the input form. class SavedEntries extends StatelessWidget { const SavedEntries({ super.key, required this.title, required this.count, required this.emptyText, required this.itemBuilder, }); final String title; final int count; final String emptyText; final IndexedWidgetBuilder itemBuilder; @override Widget build(BuildContext context) => DecoratedSliver( decoration: BoxDecoration( color: AppTheme.inventorySurface, borderRadius: BorderRadius.circular(24), ), sliver: SliverPadding( padding: const EdgeInsets.all(24), sliver: SliverMainAxisGroup( slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(bottom: 20), child: Text( '$title ($count)', style: const TextStyle( color: Colors.white, fontSize: 23, fontWeight: FontWeight.w700, ), ), ), ), if (count == 0) SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(vertical: 24), child: Text( emptyText, style: const TextStyle(color: Color(0xFFB9CCD9), height: 1.5), ), ), ) else SliverList.separated( itemCount: count, itemBuilder: itemBuilder, separatorBuilder: (context, index) => const SizedBox(height: 12), ), ], ), ), ); } class SavedEntryCard extends StatelessWidget { const SavedEntryCard({ super.key, required this.title, required this.subtitle, required this.details, this.notes = '', }); final String title; final String subtitle; final List details; final String notes; @override Widget build(BuildContext context) => Card( margin: EdgeInsets.zero, elevation: 0, color: Colors.white, surfaceTintColor: Colors.transparent, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( title, style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 18), ), const SizedBox(height: 6), Text( subtitle, style: const TextStyle(color: AppTheme.muted, height: 1.5), ), const SizedBox(height: 16), Wrap( spacing: 8, runSpacing: 8, children: details .map( (detail) => Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 8, ), decoration: BoxDecoration( color: AppTheme.inputSurface, borderRadius: BorderRadius.circular(8), ), child: Text( detail, style: const TextStyle(color: AppTheme.teal), ), ), ) .toList(), ), if (notes.isNotEmpty) ...[ const SizedBox(height: 14), Text( notes, style: const TextStyle(color: AppTheme.muted, height: 1.5), ), ], ], ), ), ); }