| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584 |
- import 'dart:io';
- import 'package:flutter/material.dart';
- import 'package:sino_med_cloud/core/utils/logger.dart';
- import 'package:sino_med_cloud/core/utils/toast_utils.dart';
- import 'package:sino_med_cloud/l10n/app_localizations.dart';
- import 'package:sino_med_cloud/media/media.dart';
- /// 媒体功能测试页面
- class MediaTestPage extends StatefulWidget {
- const MediaTestPage({super.key});
- @override
- State<MediaTestPage> createState() => _MediaTestPageState();
- }
- class _MediaTestPageState extends State<MediaTestPage> {
- File? _selectedImage;
- File? _cameraImage;
- bool _isLoading = false;
- /// 获取当前选中的图片(优先使用相册/系统相机,其次使用专业相机)
- File? get _currentImage => _selectedImage ?? _cameraImage;
- @override
- Widget build(BuildContext context) {
- final l10n = AppLocalizations.of(context)!;
- return Scaffold(
- appBar: AppBar(
- title: Text(l10n.mediaTest),
- elevation: 0,
- ),
- body: SingleChildScrollView(
- padding: const EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- // 测试按钮区域
- _buildTestButtons(),
-
- const SizedBox(height: 24),
-
- // 图片显示区域
- _buildImageDisplay(),
-
- const SizedBox(height: 24),
-
- ],
- ),
- ),
- );
- }
- /// 构建测试按钮区域
- Widget _buildTestButtons() {
- final l10n = AppLocalizations.of(context)!;
- return Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- Text(
- l10n.quickTest,
- style: const TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(height: 12),
-
- // 从相册选择
- ElevatedButton.icon(
- onPressed: _isLoading ? null : _testPickFromGallery,
- icon: const Icon(Icons.photo_library),
- label: Text(l10n.pickFromGallery),
- style: ElevatedButton.styleFrom(
- padding: const EdgeInsets.symmetric(vertical: 16),
- ),
- ),
-
- const SizedBox(height: 12),
-
- // 使用系统相机拍照
- ElevatedButton.icon(
- onPressed: _isLoading ? null : _testPickFromCamera,
- icon: const Icon(Icons.camera_alt),
- label: Text(l10n.pickFromCamera),
- style: ElevatedButton.styleFrom(
- padding: const EdgeInsets.symmetric(vertical: 16),
- ),
- ),
-
- const SizedBox(height: 24),
-
- Text(
- l10n.professionalCameraTest,
- style: const TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(height: 12),
-
- // 专业相机拍照
- ElevatedButton.icon(
- onPressed: _isLoading ? null : _testProfessionalCamera,
- icon: const Icon(Icons.camera),
- label: Text(l10n.useProfessionalCamera),
- style: ElevatedButton.styleFrom(
- padding: const EdgeInsets.symmetric(vertical: 16),
- ),
- ),
-
- const SizedBox(height: 24),
-
- Text(
- l10n.imageProcessingTest,
- style: const TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(height: 12),
-
- // 获取图片信息
- ElevatedButton.icon(
- onPressed: _isLoading || _currentImage == null
- ? null
- : _testGetImageInfo,
- icon: const Icon(Icons.info_outline),
- label: Text(l10n.getImageInfo),
- style: ElevatedButton.styleFrom(
- padding: const EdgeInsets.symmetric(vertical: 16),
- ),
- ),
-
- const SizedBox(height: 12),
-
- // 去除 EXIF
- ElevatedButton.icon(
- onPressed: _isLoading || _currentImage == null
- ? null
- : _testRemoveExif,
- icon: const Icon(Icons.security),
- label: Text(l10n.removeExif),
- style: ElevatedButton.styleFrom(
- padding: const EdgeInsets.symmetric(vertical: 16),
- ),
- ),
-
- const SizedBox(height: 12),
-
- // 压缩图片
- ElevatedButton.icon(
- onPressed: _isLoading || _currentImage == null
- ? null
- : _testCompressImage,
- icon: const Icon(Icons.compress),
- label: Text(l10n.compressImage),
- style: ElevatedButton.styleFrom(
- padding: const EdgeInsets.symmetric(vertical: 16),
- ),
- ),
-
- const SizedBox(height: 12),
-
- // 清除图片
- OutlinedButton.icon(
- onPressed: _isLoading ? null : _clearImages,
- icon: const Icon(Icons.clear),
- label: Text(l10n.clearAllImages),
- ),
- ],
- );
- }
- /// 构建图片显示区域
- Widget _buildImageDisplay() {
- final l10n = AppLocalizations.of(context)!;
- return Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- l10n.selectedImage,
- style: const TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(height: 12),
-
- if (_selectedImage == null && _cameraImage == null)
- Container(
- height: 200,
- decoration: BoxDecoration(
- color: Colors.grey[200],
- borderRadius: BorderRadius.circular(8),
- ),
- child: Center(
- child: Text(
- l10n.noImage,
- style: const TextStyle(
- color: Colors.grey,
- fontSize: 16,
- ),
- ),
- ),
- )
- else
- Column(
- children: [
- if (_selectedImage != null) ...[
- Text(
- l10n.galleryOrSystemCamera,
- style: const TextStyle(
- fontSize: 14,
- color: Colors.grey,
- ),
- ),
- const SizedBox(height: 8),
- _buildImagePreview(_selectedImage!),
- const SizedBox(height: 16),
- ],
- if (_cameraImage != null) ...[
- Text(
- l10n.professionalCamera,
- style: const TextStyle(
- fontSize: 14,
- color: Colors.grey,
- ),
- ),
- const SizedBox(height: 8),
- _buildImagePreview(_cameraImage!),
- ],
- ],
- ),
- ],
- );
- }
- /// 构建图片预览
- Widget _buildImagePreview(File imageFile) {
- return Container(
- constraints: const BoxConstraints(maxHeight: 400),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(8),
- border: Border.all(color: Colors.grey[300]!),
- ),
- child: ClipRRect(
- borderRadius: BorderRadius.circular(8),
- child: Image.file(
- imageFile,
- fit: BoxFit.contain,
- errorBuilder: (context, error, stackTrace) {
- return Container(
- height: 200,
- color: Colors.grey[200],
- child: const Center(
- child: Text('图片加载失败'),
- ),
- );
- },
- ),
- ),
- );
- }
- /// 测试从相册选择
- Future<void> _testPickFromGallery() async {
- setState(() {
- _isLoading = true;
- });
- try {
- AppLogger.d('开始测试从相册选择图片...');
- final file = await MediaService.pickFromGallery();
-
- if (file != null) {
- if (!mounted) return;
- final l10n = AppLocalizations.of(context)!;
- setState(() {
- _selectedImage = file;
- _cameraImage = null;
- });
- ToastUtils.showSuccess(l10n.galleryPickSuccess);
- AppLogger.d('相册选择成功: ${file.path}');
- } else {
- if (!mounted) return;
- final l10n = AppLocalizations.of(context)!;
- ToastUtils.show(l10n.userCancelledSelection);
- }
- } on MediaException catch (e) {
- AppLogger.e('相册选择失败', e);
- ToastUtils.showError(e.message);
- } catch (e) {
- AppLogger.e('相册选择异常', e);
- ToastUtils.showError('相册选择失败: ${e.toString()}');
- } finally {
- setState(() {
- _isLoading = false;
- });
- }
- }
- /// 测试使用系统相机拍照
- Future<void> _testPickFromCamera() async {
- setState(() {
- _isLoading = true;
- });
- try {
- AppLogger.d('开始测试使用系统相机拍照...');
- final file = await MediaService.pickFromCamera();
-
- if (file != null) {
- if (!mounted) return;
- final l10n = AppLocalizations.of(context)!;
- setState(() {
- _selectedImage = file;
- _cameraImage = null;
- });
- ToastUtils.showSuccess(l10n.cameraPhotoSuccess);
- AppLogger.d('拍照成功: ${file.path}');
- } else {
- if (!mounted) return;
- final l10n = AppLocalizations.of(context)!;
- ToastUtils.show(l10n.userCancelledPhoto);
- }
- } on MediaException catch (e) {
- AppLogger.e('拍照失败', e);
- ToastUtils.showError(e.message);
- } catch (e) {
- AppLogger.e('拍照异常', e);
- ToastUtils.showError('拍照失败: ${e.toString()}');
- } finally {
- setState(() {
- _isLoading = false;
- });
- }
- }
- /// 测试专业相机
- Future<void> _testProfessionalCamera() async {
- setState(() {
- _isLoading = true;
- });
- CameraService? cameraService;
- try {
- AppLogger.d('开始测试专业相机...');
- cameraService = CameraService();
-
- // 初始化相机
- await cameraService.init();
- ToastUtils.show('相机初始化成功,请拍照');
-
- // 显示拍照对话框
- if (!mounted) return;
- final l10n = AppLocalizations.of(context)!;
- final shouldTakePhoto = await showDialog<bool>(
- context: context,
- builder: (context) => AlertDialog(
- title: Text(l10n.professionalCamera),
- content: Text(l10n.cameraReady),
- actions: [
- TextButton(
- onPressed: () => Navigator.of(context).pop(false),
- child: Text(l10n.cancel),
- ),
- ElevatedButton(
- onPressed: () => Navigator.of(context).pop(true),
- child: const Text('拍照'),
- ),
- ],
- ),
- );
- if (shouldTakePhoto == true && mounted) {
- final file = await cameraService.takePicture();
-
- if (file != null) {
- setState(() {
- _cameraImage = file;
- _selectedImage = null;
- });
- ToastUtils.showSuccess(l10n.professionalCameraSuccess);
- AppLogger.d('专业相机拍照成功: ${file.path}');
- }
- }
- } on MediaException catch (e) {
- AppLogger.e('专业相机失败', e);
- ToastUtils.showError(e.message);
- } catch (e) {
- AppLogger.e('专业相机异常', e);
- ToastUtils.showError('专业相机失败: ${e.toString()}');
- } finally {
- await cameraService?.dispose();
- setState(() {
- _isLoading = false;
- });
- }
- }
- /// 测试获取图片信息
- Future<void> _testGetImageInfo() async {
- final currentImage = _currentImage;
- if (currentImage == null) return;
- setState(() {
- _isLoading = true;
- });
- try {
- AppLogger.d('开始获取图片信息...');
- final info = await MediaService.getImageInfo(currentImage);
-
- final l10n = AppLocalizations.of(context)!;
- final sizeInBytes = info['size'] as int;
- final sizeInKB = (sizeInBytes / 1024).toStringAsFixed(2);
-
- final infoText = '${l10n.imageWidth}: ${info['width']}${l10n.pixels}\n'
- '${l10n.imageHeight}: ${info['height']}${l10n.pixels}\n'
- '${l10n.imageSize}: $sizeInKB ${l10n.kilobytes}\n'
- '${l10n.imageFormat}: ${info['format']}';
-
- if (!mounted) return;
-
- // 使用 Alert 弹窗显示图片信息
- showDialog(
- context: context,
- builder: (context) => AlertDialog(
- title: Text(l10n.imageInfo),
- content: Text(
- infoText,
- style: const TextStyle(
- fontSize: 14,
- fontFamily: 'monospace',
- ),
- ),
- actions: [
- TextButton(
- onPressed: () => Navigator.of(context).pop(),
- child: Text(l10n.confirm),
- ),
- ],
- ),
- );
-
- ToastUtils.showSuccess(l10n.getImageInfoSuccess);
- AppLogger.d('图片信息: $info');
- } on MediaException catch (e) {
- AppLogger.e('获取图片信息失败', e);
- ToastUtils.showError(e.message);
- } catch (e) {
- AppLogger.e('获取图片信息异常', e);
- ToastUtils.showError('获取图片信息失败: ${e.toString()}');
- } finally {
- setState(() {
- _isLoading = false;
- });
- }
- }
- /// 测试去除 EXIF
- Future<void> _testRemoveExif() async {
- final currentImage = _currentImage;
- if (currentImage == null) return;
- setState(() {
- _isLoading = true;
- });
- try {
- AppLogger.d('开始去除 EXIF...');
- final cleanedFile = await MediaService.removeExif(currentImage);
-
- if (!mounted) return;
- final l10n = AppLocalizations.of(context)!;
- setState(() {
- // 更新对应的图片变量
- if (_selectedImage != null) {
- _selectedImage = cleanedFile;
- } else if (_cameraImage != null) {
- _cameraImage = cleanedFile;
- }
- });
-
- ToastUtils.showSuccess(l10n.removeExifSuccess);
- AppLogger.d('EXIF 去除成功: ${cleanedFile.path}');
- } on MediaException catch (e) {
- AppLogger.e('去除 EXIF 失败', e);
- ToastUtils.showError(e.message);
- } catch (e) {
- AppLogger.e('去除 EXIF 异常', e);
- ToastUtils.showError('去除 EXIF 失败: ${e.toString()}');
- } finally {
- setState(() {
- _isLoading = false;
- });
- }
- }
- /// 测试压缩图片
- Future<void> _testCompressImage() async {
- final currentImage = _currentImage;
- if (currentImage == null) return;
- setState(() {
- _isLoading = true;
- });
- try {
- AppLogger.d('开始压缩图片...');
- if (!mounted) return;
- final l10n = AppLocalizations.of(context)!;
- final originalSize = await currentImage.length();
- final compressedFile = await MediaService.compressImage(currentImage);
- final compressedSize = await compressedFile.length();
-
- setState(() {
- // 更新对应的图片变量
- if (_selectedImage != null) {
- _selectedImage = compressedFile;
- } else if (_cameraImage != null) {
- _cameraImage = compressedFile;
- }
- });
-
- final originalSizeKB = (originalSize / 1024).toStringAsFixed(2);
- final compressedSizeKB = (compressedSize / 1024).toStringAsFixed(2);
- final compressionRatio = ((1 - compressedSize / originalSize) * 100).toStringAsFixed(1);
-
- final compressInfo = '${l10n.originalSize}: $originalSizeKB ${l10n.kilobytes}\n'
- '${l10n.compressedSize}: $compressedSizeKB ${l10n.kilobytes}\n'
- '${l10n.compressionRatio}: $compressionRatio%';
-
- if (!mounted) return;
-
- // 使用 Alert 弹窗显示压缩信息
- showDialog(
- context: context,
- builder: (context) => AlertDialog(
- title: Text(l10n.compressImageSuccess),
- content: Text(
- compressInfo,
- style: const TextStyle(
- fontSize: 14,
- fontFamily: 'monospace',
- ),
- ),
- actions: [
- TextButton(
- onPressed: () => Navigator.of(context).pop(),
- child: Text(l10n.confirm),
- ),
- ],
- ),
- );
-
- ToastUtils.showSuccess(l10n.compressImageSuccess);
- AppLogger.d('图片压缩成功: ${compressedFile.path}');
- } on MediaException catch (e) {
- AppLogger.e('压缩图片失败', e);
- ToastUtils.showError(e.message);
- } catch (e) {
- AppLogger.e('压缩图片异常', e);
- ToastUtils.showError('压缩图片失败: ${e.toString()}');
- } finally {
- setState(() {
- _isLoading = false;
- });
- }
- }
- /// 清除所有图片
- void _clearImages() {
- final l10n = AppLocalizations.of(context)!;
- setState(() {
- _selectedImage = null;
- _cameraImage = null;
- });
- ToastUtils.show(l10n.clearAllImagesSuccess);
- }
- }
|