import 'package:flutter/foundation.dart'; import 'package:patbond_flutter/core/network/api_exception.dart'; import 'package:patbond_flutter/features/pets/pet_models.dart'; import 'package:patbond_flutter/features/pets/pets_repository.dart'; /// 网络数据四态(第 9 节硬要求:loading / empty / error / retry, /// data 态含空列表判定,页面据 [PetsLoadPhase] + 数据渲染四态)。 enum PetsLoadPhase { initial, loading, ready, error } /// pets feature 状态控制器(开发计划 §4.2 分层: /// Page/Widget → PetsController → PetsRepository → ApiClient)。 /// /// 宠物档案状态自此从 `AppState` 拆出:本 feature 不读写 AppState 的 /// 宠物/疫苗 demo 数据;服务端是唯一事实来源,内存副本仅作展示缓存 /// (无本地持久化——档案数据以 [refresh] 为失效/刷新策略)。 /// /// 本单只承载列表 + 详情主链路状态;体重/疫苗/事件/提醒的页面级 /// 状态由 T2-12~14 按页面直接经 Repository 取数或扩展本控制器。 class PetsController extends ChangeNotifier { PetsController({required this._repository}); final PetsRepository _repository; PetsLoadPhase _phase = PetsLoadPhase.initial; List _pets = const []; ApiException? _lastError; bool _disposed = false; PetsLoadPhase get phase => _phase; /// 当前用户可见宠物(服务端 created_at DESC 排序原样保留)。 List get pets => _pets; /// ready 且列表为空 → 空态(新用户建档引导)。 bool get isEmpty => _phase == PetsLoadPhase.ready && _pets.isEmpty; /// 最近一次加载失败的类型化错误(error 态时非 null)。 ApiException? get lastError => _lastError; /// 加载 / 重试宠物列表。错误不外抛,收敛为 error 态供页面渲染 + retry。 Future refresh() async { _phase = PetsLoadPhase.loading; _lastError = null; _notify(); try { _pets = await _repository.listPets(); _phase = PetsLoadPhase.ready; } on ApiException catch (error) { _lastError = error; _phase = PetsLoadPhase.error; } _notify(); } /// 创建宠物:成功后就地插入列表头(服务端 created_at DESC,新建最前), /// 失败按类型化异常外抛给表单层处理(40903 芯片号冲突等)。 Future createPet(CreatePetRequest request) async { final pet = await _repository.createPet(request); _pets = [pet, ..._pets]; if (_phase != PetsLoadPhase.ready) _phase = PetsLoadPhase.ready; _notify(); return pet; } /// 拉取单只宠物详情并同步列表内存副本。 Future getPet(String petId) async { final pet = await _repository.getPet(petId); _replaceInList(pet); return pet; } /// 更新宠物档案:成功后同步列表副本;40902 版本冲突等外抛, /// 页面提示后应调用 [getPet] 刷新取新 version 重提。 Future updatePet(String petId, UpdatePetRequest request) async { final pet = await _repository.updatePet(petId, request); _replaceInList(pet); return pet; } void _replaceInList(Pet pet) { final index = _pets.indexWhere((item) => item.id == pet.id); if (index == -1) return; _pets = [..._pets]..[index] = pet; _notify(); } void _notify() { if (!_disposed) notifyListeners(); } @override void dispose() { _disposed = true; super.dispose(); } }