水印相机前端

This commit is contained in:
2026-09-28 09:59:43 +08:00
parent 187a7e0911
commit c5b3d81d07
19 changed files with 2761 additions and 3 deletions
+66
View File
@@ -0,0 +1,66 @@
import 'dart:ui' as ui;
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/storage/local_storage.dart';
import '../models/watermark.dart';
/// 水印设置(全局共享,改动即落盘)
///
/// 跟项目里其他 provider 不同的一点:这个**不涉及网络**,纯粹是设备本地的偏好,
/// 所以直接读写 [LocalStorage],没有 api / 没有 refresh 失效那一套。
///
/// 需要 `LocalStorage.instance.init()` 已经跑过(`main.dart` 启动时做了)。
class WatermarkSettingsNotifier extends Notifier<WatermarkSettings> {
@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, WatermarkSettings>(
WatermarkSettingsNotifier.new,
);
/// 品牌角标用的相机图标(原「水印相机」项目那张,白色描边 + 透明底)。
///
/// 画布要的是 `ui.Image`,不是 widget 用的 `AssetImage`,所以这里自己解码一次。
/// 不加 autoDispose:就一张小图,加载一次全程复用。
final brandIconProvider = FutureProvider<ui.Image>((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;
});