0
import 'dart:convert';
import 'dart:io';
import 'package:class_camera_v1/helper/image_helper.dart';
import 'package:flutter/material.dart';
import 'package:gal/gal.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
// path 패키지는 파일 경로를 다루는 유틸리티 패키지
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
File? _selectedImage;
String statusMessage = "사진을 선택하거나 촬영하세요";
bool _isLoding = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('카메라 앱'),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: SafeArea(
child: Column(
children: [
Container(
width: double.infinity,
color: Colors.grey[300],
padding: const EdgeInsets.all(16.0),
child: Text(
statusMessage,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
Expanded(
child: Container(
width: double.infinity,
margin: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8.0),
),
child: _isLoding
? Center(child: CircularProgressIndicator())
: _selectedImage != null
? ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.file(_selectedImage!,
fit: BoxFit.cover))
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.image_outlined,
size: 80,
color: Colors.grey,
),
SizedBox(height: 16),
Text(
"이미지가 없습니다",
style: TextStyle(
fontSize: 20,
color: Colors.grey,
),
),
],
),
),
),
Container(
padding: const EdgeInsets.all(16.0),
child: Wrap(
spacing: 12.0,
runSpacing: 8.0,
children: [
ElevatedButton.icon(
onPressed: _isLoding ? null : _takePhoto,
icon: Icon(Icons.camera_alt),
label: const Text("카메라"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
),
ElevatedButton.icon(
onPressed: _isLoding ? null : _pickImageFromGallery,
icon: Icon(Icons.photo_library),
label: const Text("갤러리"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
),
ElevatedButton.icon(
onPressed: (_selectedImage != null && _isLoding == false)
? _saveImageToGallery
: null,
icon: Icon(Icons.photo_library),
label: const Text("저장"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
),
ElevatedButton.icon(
onPressed: (_selectedImage != null && _isLoding == false)
? _uploadToServer
: null,
icon: Icon(Icons.photo_library),
label: const Text("서버로 전송"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.amber,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
),
],
),
)
],
),
),
),
);
}
// 카메라 사진 촬영
void _takePhoto() async {
setState(() {
_isLoding = true;
statusMessage = "카메라를 준비중...";
});
try {
// 갤러리 접근 권한 확인 요청
if (await Gal.hasAccess() == false) {
await Gal.requestAccess();
}
// 카메라로 사진 촬영
XFile? image = await ImagePicker().pickImage(
source: ImageSource.camera,
imageQuality: 85,
);
if (image != null) {
setState(() {
statusMessage = "카메라로 사진 촬영을 했습니다.";
_isLoding = false;
_selectedImage = new File(image.path);
print("촬영된 사진 임시 경로 : ${image.path}");
});
} else {
setState(() {
statusMessage = "사진 촬영이 취소되었습니다";
_isLoding = false;
});
}
} catch (e) {
setState(() {
statusMessage = "카메라 사용 중 오류가 발생했습니다";
_isLoding = false;
print("카메라 오류 발생 : ${e}");
});
}
}
// 갤러리에서 이미지 선택
void _pickImageFromGallery() async {
setState(() {
_isLoding = true;
statusMessage = "갤러리 여는 중...";
});
try {
// 갤러리 접근 권한 확인 요청
if (await Gal.hasAccess() == false) {
await Gal.requestAccess();
}
// 갤러리에 접근 요청
XFile? pickedImage = await ImagePicker().pickImage(
source: ImageSource.gallery,
imageQuality: 85,
);
if (pickedImage != null) {
setState(() {
_selectedImage = File(pickedImage.path);
statusMessage = "갤러리에서 이미지를 선택 했습니다.";
_isLoding = false;
});
} else {
setState(() {
statusMessage = "이미지 선택이 취소 되었습니다.";
_isLoding = false;
});
}
} catch (e) {
setState(() {
statusMessage = "갤러리 접근 중 오류 발생";
_isLoding = false;
});
print("갤러리 열기 오류 확인 : ${e}");
}
}
void _saveImageToGallery() async {
if (_selectedImage == null) return;
setState(() {
_isLoding = true;
statusMessage = "이미지를 갤러리에 저장중...";
});
try {
// 갤러리 접근 권한 확인 요청
if (await Gal.hasAccess() == false) {
await Gal.requestAccess();
}
// 갤러리에 저장
Gal.putImage(_selectedImage!.path);
setState(() {
_isLoding = false;
statusMessage = "갤러리에 이미지 저장 완료";
});
// 3초 후에 원래 메세지로 복원
Future.delayed(const Duration(seconds: 3), () {
// mounted 비동기 작업의 결과를 위젯에 반영하기 전에
// 위젯이 여전히 유효한 상태인지 확인
if (mounted) {
// 이 속성은 위젯 트리에 여전히 연결이 되어 있는지 확인
// 이유는 dispose 호출된 이후에
// 개발자가 setState() 메서드를 호출하면 오류가 발생
setState(() {
statusMessage = "사진을 선택하거나 촬영하세요";
});
}
});
} catch (e) {
setState(() {
statusMessage = "이미지 저장 중 오류 발생";
_isLoding = false;
});
}
}
// 서버로 이미지 전송
Future<void> _uploadToServer() async {
// 방어적 코드 작성
if (_selectedImage == null) return;
setState(() {
_isLoding = true;
statusMessage = "서버로 업로드 중 ...";
});
// 서버측으로 통신하는 코드
final result = ImageHelper.uploadToServer(_selectedImage!);
setState(() {
_isLoding = false;
statusMessage = "사진 업로드 완료";
});
}
@override
void dispose() {
super.dispose();
// 일반적으로 자원 연결 해제 코드 작성해 준다.
}
}'Flutter' 카테고리의 다른 글
| 구글 Map API 사용 (0) | 2025.08.20 |
|---|---|
| 카메라 및 이미지 다루기 4단계 (0) | 2025.08.20 |
| 카메라 다루기 2단계 (0) | 2025.08.20 |
| 카메라 다루기 1단계 (0) | 2025.08.20 |
| Flutter Key 및 상태 관리의 기본 개념 - 성능 최적화 팁 (0) | 2025.08.20 |