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:
@@ -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()))
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user