first
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/// [ChangeNotifier] that loads, filters, and mutates the in-memory item list.
|
||||
library;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../models/item.dart';
|
||||
import '../services/item_repository.dart';
|
||||
import '../services/storage_service.dart';
|
||||
|
||||
/// App-wide catalog state. Screens call [loadItems], [addItem], and [search].
|
||||
class ItemController extends ChangeNotifier {
|
||||
ItemController({
|
||||
required this._repository,
|
||||
required this._storage,
|
||||
}) {
|
||||
_repository.useRemoteData = _storage.useRemoteData;
|
||||
}
|
||||
|
||||
final ItemRepository _repository;
|
||||
final StorageService _storage;
|
||||
|
||||
List<Item> _items = const [];
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
|
||||
List<Item> get items => List.unmodifiable(_items);
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
bool get useRemoteData => _repository.useRemoteData;
|
||||
int get count => _items.length;
|
||||
|
||||
Future<void> loadItems() async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Small delay so the loading indicator is visible with local data.
|
||||
await Future<void>.delayed(const Duration(milliseconds: 250));
|
||||
_items = await _repository.fetchItems();
|
||||
} catch (error, stackTrace) {
|
||||
_error = 'Could not load items.';
|
||||
debugPrint('ItemController.loadItems failed: $error\n$stackTrace');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setUseRemoteData(bool value) async {
|
||||
if (value == _repository.useRemoteData) return;
|
||||
_repository.useRemoteData = value;
|
||||
await _storage.setUseRemoteData(value);
|
||||
notifyListeners();
|
||||
await loadItems();
|
||||
}
|
||||
|
||||
/// Case-insensitive match against title, subtitle, and category.
|
||||
List<Item> search(String query) {
|
||||
final needle = query.trim().toLowerCase();
|
||||
if (needle.isEmpty) return items;
|
||||
|
||||
return _items.where((item) {
|
||||
return item.title.toLowerCase().contains(needle) ||
|
||||
item.subtitle.toLowerCase().contains(needle) ||
|
||||
item.category.label.toLowerCase().contains(needle);
|
||||
}).toList(growable: false);
|
||||
}
|
||||
|
||||
Item addItem({
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required String description,
|
||||
required ItemCategory category,
|
||||
}) {
|
||||
final created = _repository.createLocal(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
description: description,
|
||||
category: category,
|
||||
);
|
||||
_items = [created, ..._items];
|
||||
notifyListeners();
|
||||
return created;
|
||||
}
|
||||
|
||||
Item? findById(String id) {
|
||||
for (final item in _items) {
|
||||
if (item.id == id) return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/// [ChangeNotifier] that owns [ThemeMode] and persists it locally.
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../services/storage_service.dart';
|
||||
import '../utils/helpers.dart';
|
||||
|
||||
/// Provides [themeMode] to [MaterialApp] and writes changes to storage.
|
||||
class ThemeController extends ChangeNotifier {
|
||||
ThemeController(this._storage) {
|
||||
_themeMode = themeModeFromName(_storage.themeModeName);
|
||||
}
|
||||
|
||||
final StorageService _storage;
|
||||
late ThemeMode _themeMode;
|
||||
|
||||
ThemeMode get themeMode => _themeMode;
|
||||
|
||||
bool get isDark => _themeMode == ThemeMode.dark;
|
||||
|
||||
Future<void> setThemeMode(ThemeMode mode) async {
|
||||
if (mode == _themeMode) return;
|
||||
_themeMode = mode;
|
||||
notifyListeners();
|
||||
await _storage.setThemeModeName(nameFromThemeMode(mode));
|
||||
}
|
||||
|
||||
Future<void> cycleThemeMode() {
|
||||
final next = switch (_themeMode) {
|
||||
ThemeMode.system => ThemeMode.light,
|
||||
ThemeMode.light => ThemeMode.dark,
|
||||
ThemeMode.dark => ThemeMode.system,
|
||||
};
|
||||
return setThemeMode(next);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user