Add household sharing feature

 Features:
- Create household with 6-character shareable code
- Join existing household with code
- View household members and owner
- Leave household functionality
- Automatic inventory filtering by household
- Persistent household settings across app updates

🔧 Technical changes:
- Added Household model with Hive adapter (typeId: 4)
- Updated AppSettings with userName and currentHouseholdId fields
- Modified InventoryRepository to filter items by household
- Updated Add Item screen to set householdId on new items
- Added HouseholdScreen with full CRUD operations
- Integrated household sharing into Settings navigation

🎯 Behavior:
- Users not in a household see only their personal items
- Users in a household see shared household items
- New items automatically tagged with current household

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-04 14:46:10 -04:00
parent f4be460c3e
commit a360fadc17
9 changed files with 707 additions and 12 deletions

View File

@@ -1,5 +1,6 @@
import 'package:hive/hive.dart';
import '../../../data/local/hive_database.dart';
import '../../settings/models/app_settings.dart';
import '../models/food_item.dart';
import 'inventory_repository.dart';
@@ -7,10 +8,32 @@ import 'inventory_repository.dart';
class InventoryRepositoryImpl implements InventoryRepository {
Future<Box<FoodItem>> get _box async => await HiveDatabase.getFoodBox();
/// Get the current household ID from settings
Future<String?> get _currentHouseholdId async {
final settings = await HiveDatabase.getSettings();
return settings.currentHouseholdId;
}
/// Filter items by current household
/// If user is in a household, only show items from that household
/// If user is not in a household, only show items without a household
List<FoodItem> _filterByHousehold(Iterable<FoodItem> items, String? householdId) {
return items.where((item) {
if (householdId == null) {
// User not in household - show items without household
return item.householdId == null;
} else {
// User in household - show items from that household
return item.householdId == householdId;
}
}).toList();
}
@override
Future<List<FoodItem>> getAllItems() async {
final box = await _box;
return box.values.toList();
final householdId = await _currentHouseholdId;
return _filterByHousehold(box.values, householdId);
}
@override
@@ -41,7 +64,9 @@ class InventoryRepositoryImpl implements InventoryRepository {
@override
Future<List<FoodItem>> getItemsByLocation(Location location) async {
final box = await _box;
return box.values
final householdId = await _currentHouseholdId;
final filteredItems = _filterByHousehold(box.values, householdId);
return filteredItems
.where((item) => item.location == location)
.toList();
}
@@ -49,8 +74,10 @@ class InventoryRepositoryImpl implements InventoryRepository {
@override
Future<List<FoodItem>> getItemsExpiringWithinDays(int days) async {
final box = await _box;
final householdId = await _currentHouseholdId;
final filteredItems = _filterByHousehold(box.values, householdId);
final targetDate = DateTime.now().add(Duration(days: days));
return box.values
return filteredItems
.where((item) =>
item.expirationDate.isBefore(targetDate) &&
item.expirationDate.isAfter(DateTime.now()))
@@ -61,7 +88,9 @@ class InventoryRepositoryImpl implements InventoryRepository {
@override
Future<List<FoodItem>> getExpiredItems() async {
final box = await _box;
return box.values
final householdId = await _currentHouseholdId;
final filteredItems = _filterByHousehold(box.values, householdId);
return filteredItems
.where((item) => item.expirationDate.isBefore(DateTime.now()))
.toList()
..sort((a, b) => a.expirationDate.compareTo(b.expirationDate));
@@ -70,8 +99,10 @@ class InventoryRepositoryImpl implements InventoryRepository {
@override
Future<List<FoodItem>> searchItemsByName(String query) async {
final box = await _box;
final householdId = await _currentHouseholdId;
final filteredItems = _filterByHousehold(box.values, householdId);
final lowerQuery = query.toLowerCase();
return box.values
return filteredItems
.where((item) => item.name.toLowerCase().contains(lowerQuery))
.toList();
}
@@ -79,32 +110,40 @@ class InventoryRepositoryImpl implements InventoryRepository {
@override
Future<int> getItemCount() async {
final box = await _box;
return box.length;
final householdId = await _currentHouseholdId;
final filteredItems = _filterByHousehold(box.values, householdId);
return filteredItems.length;
}
@override
Stream<List<FoodItem>> watchAllItems() async* {
final box = await _box;
yield box.values.toList();
final householdId = await _currentHouseholdId;
yield _filterByHousehold(box.values, householdId);
await for (final _ in box.watch()) {
yield box.values.toList();
final currentHouseholdId = await _currentHouseholdId;
yield _filterByHousehold(box.values, currentHouseholdId);
}
}
@override
Stream<List<FoodItem>> watchExpiringItems(int days) async* {
final box = await _box;
final householdId = await _currentHouseholdId;
final targetDate = DateTime.now().add(Duration(days: days));
yield box.values
final filteredItems = _filterByHousehold(box.values, householdId);
yield filteredItems
.where((item) =>
item.expirationDate.isBefore(targetDate) &&
item.expirationDate.isAfter(DateTime.now()))
.toList();
await for (final _ in box.watch()) {
yield box.values
final currentHouseholdId = await _currentHouseholdId;
final currentFilteredItems = _filterByHousehold(box.values, currentHouseholdId);
yield currentFilteredItems
.where((item) =>
item.expirationDate.isBefore(targetDate) &&
item.expirationDate.isAfter(DateTime.now()))

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import '../../../core/constants/colors.dart';
import '../../../data/local/hive_database.dart';
import '../models/food_item.dart';
import '../controllers/inventory_controller.dart';
import '../services/barcode_service.dart';
@@ -118,6 +119,9 @@ class _AddItemScreenState extends ConsumerState<AddItemScreen> {
Future<void> _saveItem() async {
if (_formKey.currentState!.validate()) {
// Get current household ID from settings
final settings = await HiveDatabase.getSettings();
final item = FoodItem()
..name = _nameController.text.trim()
..barcode = _barcode
@@ -132,6 +136,7 @@ class _AddItemScreenState extends ConsumerState<AddItemScreen> {
..notes = _notesController.text.trim().isEmpty
? null
: _notesController.text.trim()
..householdId = settings.currentHouseholdId
..lastModified = DateTime.now();
try {