import 'dart:ui' as ui; import 'package:flutter/services.dart' show rootBundle; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../api/photo_api.dart'; import '../core/storage/local_storage.dart'; import '../models/photo.dart'; import '../models/watermark.dart'; /// 水印设置(全局共享,改动即落盘) /// /// 跟项目里其他 provider 不同的一点:这个**不涉及网络**,纯粹是设备本地的偏好, /// 所以直接读写 [LocalStorage],没有 api / 没有 refresh 失效那一套。 /// /// 需要 `LocalStorage.instance.init()` 已经跑过(`main.dart` 启动时做了)。 class WatermarkSettingsNotifier extends Notifier { @override WatermarkSettings build() { final raw = LocalStorage.instance.getWatermarkSettings(); if (raw == null) return WatermarkSettings.defaults; // 存坏了就当没存过,用默认值兜住 —— 不能让一段脏 JSON 把页面打不开 try { return WatermarkSettings.fromJson(raw); } catch (_) { return WatermarkSettings.defaults; } } /// 整体替换并落盘。设置页里改开关/颜色走这个。 void update(WatermarkSettings next) { state = next; LocalStorage.instance.saveWatermarkSettings(next.toJson()); } /// 拖动过程中更新位置 —— **只改内存,不落盘**。 /// 拖一次会产生几十上百个事件,每个都写 SharedPreferences 太浪费。 void updatePosition(double x, double y) { state = state.copyWith(posX: x, posY: y); } /// 松手时落盘。 void persistPosition() { LocalStorage.instance.saveWatermarkSettings(state.toJson()); } /// 回到默认位置(左下角) void resetPosition() { update(state.copyWith(posX: null, posY: null)); } } final watermarkSettingsProvider = NotifierProvider( WatermarkSettingsNotifier.new, ); /// 品牌角标用的相机图标(原「水印相机」项目那张,白色描边 + 透明底)。 /// /// 画布要的是 `ui.Image`,不是 widget 用的 `AssetImage`,所以这里自己解码一次。 /// 不加 autoDispose:就一张小图,加载一次全程复用。 final brandIconProvider = FutureProvider((ref) async { final data = await rootBundle.load('assets/images/watermark_camera.png'); final codec = await ui.instantiateImageCodec(data.buffer.asUint8List()); final frame = await codec.getNextFrame(); codec.dispose(); return frame.image; }); /// 首页水印卡片用:只想知道「一共几张、最近一张什么时候」。 /// /// 和 `noteOverviewProvider` 一个思路 —— 只取 1 条拿 total, /// 不为了一个数字把列表页那套分页状态挂到首页上。 final photoOverviewProvider = FutureProvider.autoDispose((ref) { return ref.watch(photoApiProvider).list(page: 1, size: 1); });