123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- import 'dart:convert';
- import 'dart:io';
- import 'package:auto_route/annotations.dart';
- import 'package:bot_toast/bot_toast.dart';
- import 'package:eitc_erm_dental_flutter/db_util.dart';
- import 'package:eitc_erm_dental_flutter/entity/db/local_patient_info.dart';
- import 'package:eitc_erm_dental_flutter/exts.dart';
- import 'package:eitc_erm_dental_flutter/global.dart';
- import 'package:eitc_erm_dental_flutter/pages/view/widget/preview_operation_view.dart';
- import 'package:eitc_erm_dental_flutter/pages/view/widget/preview_title.dart';
- import 'package:eitc_erm_dental_flutter/pages/view/widget/video_patient_info_bar.dart';
- import 'package:eitc_erm_dental_flutter/sp_util.dart';
- import 'package:flutter/material.dart';
- import 'package:image/image.dart' as img;
- import 'package:path_provider/path_provider.dart';
- import 'package:photo_view/photo_view.dart';
- import '../../funcs.dart';
- ///照片预览页面
- @RoutePage(name: "photoPreviewRoute")
- class PhotoPreviewPage extends StatefulWidget {
- final LocalPatientInfo info;
- final String path;
- final String area;
- final DateTime time;
- final String mobile;
- final String wifi;
- final String deviceModel;
- const PhotoPreviewPage(
- {super.key,
- required this.info,
- required this.path,
- required this.area,
- required this.time,
- required this.mobile,
- required this.deviceModel,
- required this.wifi});
- @override
- State<PhotoPreviewPage> createState() => _PhotoPreviewPageState();
- }
- class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
- bool _isSaving = false;
- bool _isSaved = false;
- @override
- void initState() {
- super.initState();
- }
- @override
- void dispose() {
- super.dispose();
- _deleteCache();
- }
- @override
- Widget build(BuildContext context) {
- Size screenSize = MediaQuery.of(context).size;
- return PopScope(
- canPop: !_isSaving,
- child: Scaffold(
- body: SafeArea(
- child: Container(
- color: Colors.black,
- child: Stack(
- children: [
- PhotoView.customChild(
- childSize: MediaQuery.of(context).orientation ==
- Orientation.landscape
- ? Size(screenSize.width,
- screenSize.height + screenSize.height / 2)
- : null,
- child: Container(
- alignment: Alignment.center,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Image.file(File(widget.path)),
- VideoPatientInfoBar(
- info: widget.info,
- orientation: MediaQuery.of(context).orientation,
- area: widget.area,
- deviceModel: widget.deviceModel,
- time: widget.time,
- ),
- ],
- ),
- )),
- Align(
- alignment: Alignment.topLeft,
- child: PreviewTitle(
- title: getS().previewPhoto,
- ),
- ),
- PreviewOperationView(
- onSave: _onSave,
- onCancel: _onCancel,
- ),
- ],
- ),
- ),
- ),
- ));
- }
- Future<String?> _editPhoto() async {
- int height = 55;
- int fontHeight = 48;
- //读取原图片
- logd("读取原图片,path=${widget.path}");
- img.Command sourceImgCmd = img.Command();
- sourceImgCmd.decodeJpgFile(widget.path);
- await sourceImgCmd.executeThread();
- img.Image? sourceImg = await sourceImgCmd.getImage();
- if (sourceImg == null) {
- logd("读取原图片返回null");
- return null;
- }
- logd("原图片读取成功,image=${sourceImg.toString()}");
- logd("读取名字图片");
- LocalPatientInfo? patientInfo =
- await DbUtil.instance.getLocalPatientById(selectedPatientId);
- //读取名字图片
- img.Image? nameImg;
- img.Command? nameImgCmd;
- if (patientInfo != null && !patientInfo.namePic.isNullOrEmpty) {
- nameImgCmd = img.Command();
- nameImgCmd.decodePng(base64Decode(patientInfo.namePic!));
- await nameImgCmd.executeThread();
- nameImg = await nameImgCmd.getImage();
- logd("名字读片读取成功,image=${nameImg.toString()}");
- } else {
- logd("名字图片不存在,咨询人信息为null或名字图片字段为空");
- }
- //性别
- int gender = getGenderFromIdCard(widget.info.idCardDecrypt);
- String sex = getS().unknown;
- if (gender == 0) {
- sex = getS().male;
- } else if (gender == 1) {
- sex = getS().female;
- }
- //年龄
- int age = getAgeFromIdCard(
- widget.info.idCardDecrypt.isEmpty ? "--" : widget.info.idCardDecrypt);
- //区域
- String toothArea = toothAreaTranslate(widget.area);
- //时间
- String yyyy = widget.time.yyyyMMddHHmmss;
- logd("信息,性别=$sex,年龄=$age,area=$toothArea,时间=$yyyy");
- logd("创建黑条并写入信息");
- int gap = 30;
- //创建黑条
- img.Command blackCmd = img.Command()
- ..createImage(
- width: sourceImg.width,
- height: height,
- )
- ..fill(color: img.ColorRgb8(0, 0, 0))
- ..drawString(
- "/$sex/${getS().xxAge(age < 0 ? 0 : age)} $toothArea $yyyy",
- font: photoFont ?? img.arial48,
- x: (nameImg?.width ?? 0) + gap,
- y: (height - fontHeight) ~/ 2,
- color: img.ColorRgb8(255, 255, 255))
- ..drawString(widget.deviceModel.isEmpty ? "--" : widget.deviceModel,
- font: photoFont ?? img.arial48,
- rightJustify: true,
- //右对齐坐标从右往左算
- x: sourceImg.width - gap,
- y: (height - fontHeight) ~/ 2,
- color: img.ColorRgb8(255, 255, 255));
- if (nameImg != null && nameImgCmd != null) {
- logd("存在名字图片,合并到黑条中");
- blackCmd.compositeImage(nameImgCmd,
- dstX: gap, dstY: (height - nameImg.height) ~/ 2);
- }
- logd("黑条创建成功");
- logd("创建最终图片,合并原图片和黑条");
- //创建一个更大的图片装下原图片和黑条
- img.Command allCmd = img.Command();
- allCmd.createImage(
- width: sourceImg.width, height: sourceImg.height + height);
- allCmd.compositeImage(sourceImgCmd);
- allCmd.compositeImage(blackCmd, dstY: sourceImg.height);
- logd("最终图片创建成功");
- logd("开始保存");
- //保存
- Directory? dir;
- if (Platform.isIOS) {
- dir = await getApplicationDocumentsDirectory();
- } else {
- dir = await getDownloadsDirectory();
- }
- if (dir == null) {
- return null;
- }
- dir = Directory("${dir.path}/${Platform.isIOS ? "jpgimage" : "tempPhoto"}");
- if (!await dir.exists()) {
- await dir.create(recursive: true);
- }
- String prefix = makeFilePrefix(
- name: widget.info.name ?? "",
- idCard: widget.info.idCardDecrypt,
- mobile: widget.mobile,
- area: widget.area,
- wifi: widget.wifi,
- time: widget.time.millisecondsSinceEpoch,
- userId: await SpUtil.getUserId());
- String newPath =
- "${dir.path}/$prefix${widget.time.millisecondsSinceEpoch}.jpg";
- logd("保存路径=$newPath");
- allCmd.encodeJpgFile(newPath);
- await allCmd.executeThread();
- logd("执行完毕");
- return newPath;
- }
- void _onSave() async {
- logd("保存");
- setState(() {
- _isSaving = true;
- });
- var cancelFunc = BotToast.showLoading(clickClose: false, crossPage: false);
- try {
- //编辑并保存照片
- String? path = await _editPhoto();
- if (path == null) {
- setState(() {
- _isSaving = false;
- });
- cancelFunc();
- showToast(text: getS().saveFailed);
- return;
- }
- //通知原生保存图片
- await videoChannel.invokeMethod("savePhoto", [widget.path, path]);
- _isSaved = true;
- cancelFunc();
- setState(() {
- _isSaving = false;
- });
- showToast(text: getS().saveSuccess);
- if (mounted) {
- Navigator.pop(context);
- }
- } catch (e) {
- setState(() {
- _isSaving = false;
- });
- cancelFunc();
- showToast(text: getS().saveFailed);
- loge("保存图片异常", error: e);
- }
- }
- void _onCancel() {
- logd("取消");
- Navigator.pop(context);
- }
- void _deleteCache() {
- if (widget.path.isEmpty && _isSaved) {
- return;
- }
- try {
- fileChannel.invokeMethod("deleteFile", widget.path);
- } catch (e) {
- loge("照片预览删除文件异常", error: e);
- }
- }
- }
|