--- phase: 02-household-creation plan: 04 type: execute wave: 3 depends_on: [02-03, 02-01] files_modified: - lib/features/household/presentation/pages/create_household_page.dart - lib/features/household/presentation/pages/join_household_page.dart - lib/features/household/presentation/pages/household_list_page.dart - lib/features/household/presentation/widgets/invite_code_dialog.dart - lib/features/household/presentation/widgets/household_card.dart autonomous: true must_haves: truths: - "Users can create households with validation and feedback" - "Users can join households using 8-character invite codes" - "Household management UI provides clear navigation and state feedback" - "Invite code generation works with proper security and expiration" - "All household operations handle loading states and errors gracefully" artifacts: - path: "lib/features/household/presentation/pages/create_household_page.dart" provides: "Household creation interface" contains: "CreateHouseholdPage", "form validation", "create button" - path: "lib/features/household/presentation/pages/join_household_page.dart" provides: "Household joining interface with invite code input" contains: "JoinHouseholdPage", "invite code field", "join button" - path: "lib/features/household/presentation/pages/household_list_page.dart" provides: "Household overview and management" contains: "HouseholdListPage", "household cards", "create/join buttons" - path: "lib/features/household/presentation/widgets/invite_code_dialog.dart" provides: "Invite code generation and display" contains: "InviteCodeDialog", "code display", "share functionality" - path: "lib/features/household/presentation/widgets/household_card.dart" provides: "Household display component" contains: "HouseholdCard", "member count", "role display" key_links: - from: "lib/features/household/presentation/pages/create_household_page.dart" to: "lib/features/household/providers/household_provider.dart" via: "Riverpod consumer" pattern: "ref.read.*householdProvider" - from: "lib/features/household/presentation/pages/household_list_page.dart" to: "lib/providers/auth_provider.dart" via: "authentication state" pattern: "ref.watch.*authProvider" --- Create household management UI components for creating, joining, and managing households with proper validation and user feedback. Purpose: Provide intuitive interfaces for all household operations from SHARE-01 through SHARE-05 with consistent UX and error handling. Output: Complete household management UI ready for integration with navigation system. @~/.opencode/get-shit-done/workflows/execute-plan.md @~/.opencode/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md # UI Pattern References @lib/features/authentication/presentation/pages/signup_page.dart @lib/features/authentication/presentation/pages/login_page.dart @lib/features/authentication/presentation/widgets/auth_button.dart Create Household Card Widget lib/features/household/presentation/widgets/household_card.dart Create household card component following auth UI patterns: ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../domain/entities/household_entity.dart'; class HouseholdCard extends ConsumerWidget { final HouseholdEntity household; final VoidCallback? onTap; final VoidCallback? onGenerateInvite; final VoidCallback? onLeave; final bool showActions; const HouseholdCard({ Key? key, required this.household, this.onTap, this.onGenerateInvite, this.onLeave, this.showActions = true, }) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: ListTile( leading: CircleAvatar( backgroundColor: Theme.of(context).colorScheme.primary, child: Text( household.name.isNotEmpty ? household.name[0].toUpperCase() : 'H', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), title: Text( household.name, style: Theme.of(context).textTheme.titleMedium, ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), Text('${household.members.length} member${household.members.length != 1 ? 's' : ''}'), if (household.currentInvite != null) Text( 'Invite active', style: TextStyle( color: Theme.of(context).colorScheme.primary, fontSize: 12, ), ), ], ), trailing: showActions ? _buildActions(context) : null, onTap: onTap, ), ); } Widget _buildActions(BuildContext context) { return PopupMenuButton( onSelected: (value) { switch (value) { case 'invite': onGenerateInvite?.call(); break; case 'leave': onLeave?.call(); break; } }, itemBuilder: (context) => [ if (household.isCurrentUserOwner || household.members.any(m => m.isCurrentUser && m.role.name != 'viewer')) const PopupMenuItem( value: 'invite', child: Row( children: [ Icon(Icons.person_add), SizedBox(width: 8), Text('Generate Invite'), ], ), ), if (!household.isCurrentUserOwner || household.members.length > 1) const PopupMenuItem( value: 'leave', child: Row( children: [ Icon(Icons.exit_to_app), SizedBox(width: 8), Text('Leave Household'), ], ), ), ], ); } } ``` Follow Material Design card patterns and authentication UI consistency. Include role-based action visibility (only owners/editors can generate invites). flutter analyze lib/features/household/presentation/widgets/household_card.dart passes Household card component displays household information with appropriate actions Create Invite Code Dialog lib/features/household/presentation/widgets/invite_code_dialog.dart Create dialog for generating and sharing invite codes: ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/services.dart'; import '../../domain/models/household_models.dart'; import '../../providers/household_provider.dart'; class InviteCodeDialog extends ConsumerStatefulWidget { final String householdId; final String householdName; const InviteCodeDialog({ Key? key, required this.householdId, required this.householdName, }) : super(key: key); @override ConsumerState createState() => _InviteCodeDialogState(); } class _InviteCodeDialogState extends ConsumerState { InviteCodeModel? _inviteCode; bool _isLoading = false; String? _error; @override void initState() { super.initState(); _generateInviteCode(); } Future _generateInviteCode() async { setState(() { _isLoading = true; _error = null; }); try { final user = ref.read(authProvider).user; if (user == null) throw Exception('User not authenticated'); final inviteCode = await ref.read(householdProvider.notifier) .generateInviteCode(widget.householdId, user.id); setState(() { _inviteCode = inviteCode; _isLoading = false; }); } catch (e) { setState(() { _error = e.toString(); _isLoading = false; }); } } @override Widget build(BuildContext context) { return AlertDialog( title: Text('Invite to ${widget.householdName}'), content: Column( mainAxisSize: MainAxisSize.min, children: [ if (_isLoading) const Column( children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Generating invite code...'), ], ) else if (_error != null) Column( children: [ Icon( Icons.error, color: Theme.of(context).colorScheme.error, size: 48, ), const SizedBox(height: 16), Text( 'Failed to generate invite code', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 8), Text(_error!), const SizedBox(height: 16), ElevatedButton( onPressed: _generateInviteCode, child: const Text('Try Again'), ), ], ) else if (_inviteCode != null) Column( children: [ Text( 'Share this code with household members:', style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceVariant, borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Text( _inviteCode!.code, style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, letterSpacing: 2, ), ), const SizedBox(height: 8), Text( 'Expires: ${DateFormat('MMM dd, yyyy').format(_inviteCode!.expiresAt)}', style: Theme.of(context).textTheme.bodySmall, ), ], ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _copyToClipboard(_inviteCode!.code), icon: const Icon(Icons.copy), label: const Text('Copy Code'), ), ), const SizedBox(width: 8), Expanded( child: OutlinedButton.icon( onPressed: () => _shareInvite(_inviteCode!.code), icon: const Icon(Icons.share), label: const Text('Share'), ), ), ], ), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Close'), ), ], ); } Future _copyToClipboard(String code) async { await Clipboard.setData(ClipboardData(text: code)); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Invite code copied to clipboard')), ); } } Future _shareInvite(String code) async { // Share functionality would be implemented here await _copyToClipboard(code); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Invite code copied - paste to share')), ); } } } ``` Include proper loading states, error handling, and copy-to-clipboard functionality. Follow Material Design dialog patterns from authentication flows. flutter analyze lib/features/household/presentation/widgets/invite_code_dialog.dart passes Invite code dialog provides code generation, display, and sharing functionality Create Create Household Page lib/features/household/presentation/pages/create_household_page.dart Create household creation page following authentication UI patterns: ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/services.dart'; import '../../providers/household_provider.dart'; import '../../domain/exceptions/household_exceptions.dart'; class CreateHouseholdPage extends ConsumerStatefulWidget { const CreateHouseholdPage({Key? key}) : super(key: key); @override ConsumerState createState() => _CreateHouseholdPageState(); } class _CreateHouseholdPageState extends ConsumerState { final _formKey = GlobalKey(); final _nameController = TextEditingController(); bool _isLoading = false; String? _error; @override void dispose() { _nameController.dispose(); super.dispose(); } Future _createHousehold() async { if (!_formKey.currentState!.validate()) return; setState(() { _isLoading = true; _error = null; }); try { final user = ref.read(authProvider).user; if (user == null) throw Exception('User not authenticated'); final household = await ref.read(householdProvider.notifier) .createHousehold(_nameController.text.trim(), user.id); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Household "${household.name}" created successfully!'), backgroundColor: Colors.green, ), ); Navigator.of(context).pop(household); } } catch (e) { setState(() { _error = _getErrorMessage(e); _isLoading = false; }); } } String _getErrorMessage(dynamic error) { if (error is HouseholdValidationException) { return error.message; } else if (error is HouseholdOperationException) { return error.message; } return 'Failed to create household. Please try again.'; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Create Household'), backgroundColor: Theme.of(context).colorScheme.surface, foregroundColor: Theme.of(context).colorScheme.onSurface, elevation: 0, ), body: SafeArea( child: Padding( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 32), Icon( Icons.home, size: 64, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 24), Text( 'Create a New Household', style: Theme.of(context).textTheme.headlineSmall, textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( 'Start tracking your shared inventory with family or roommates', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), ), textAlign: TextAlign.center, ), const SizedBox(height: 48), TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Household Name', hintText: 'e.g., The Smith Family', prefixIcon: Icon(Icons.home), ), textInputAction: TextInputAction.done, autofillHints: const [AutofillHints.name], validator: (value) { if (value == null || value.trim().isEmpty) { return 'Please enter a household name'; } if (value.trim().length > 100) { return 'Household name too long (max 100 characters)'; } return null; }, onFieldSubmitted: (_) => _createHousehold(), ), const SizedBox(height: 24), if (_error != null) Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of(context).colorScheme.errorContainer, borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon( Icons.error, color: Theme.of(context).colorScheme.error, ), const SizedBox(width: 8), Expanded( child: Text( _error!, style: TextStyle( color: Theme.of(context).colorScheme.error, ), ), ), ], ), ), const SizedBox(height: 24), ElevatedButton( onPressed: _isLoading ? null : _createHousehold, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Create Household'), ), const SizedBox(height: 16), OutlinedButton( onPressed: () => Navigator.of(context).pop(), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), child: const Text('Cancel'), ), ], ), ), ), ), ); } } ``` Follow authentication page patterns for consistency. Include proper validation, loading states, and error handling. flutter analyze lib/features/household/presentation/pages/create_household_page.dart passes Create household page provides form validation and user feedback 1. Household card component displays household information with role-based actions 2. Invite code dialog handles generation, display, copying, and sharing 3. Create household page follows authentication UI patterns with proper validation 4. All components handle loading states and errors appropriately 5. UI is consistent with existing authentication flows Household management UI components are complete with proper validation, error handling, and user feedback ready for integration. After completion, create `.planning/phases/02-household-creation/02-04-SUMMARY.md` with household UI components implementation summary