17일차는 image_picker 패키지를 사용하여 앱을 만들어 보았다.
학습한 내용
- Permission
- image_picker
- 포토네컷 제작하기
유저에게 특정 기능을 허용하는 것을 물어볼 때 사용하는 것으로 특히 카메라, GPS, 블루투스, 인터넷 연결 등의 사용자에게 영향이 가거나 개인정보를 이용할 수 있다면 반드시 permission을 설정 해야 한다.
안드로이드 앱 권한은 아래의 파일에 추가해야 한다.
Project Folder > android > app > src > main > AndroidManifest.xml
IOS의 앱 권한은 아래의 파일에 추가해야 한다.
Project Folder > iOS > Podfile
entries
entries
는 key
와 value
값을 세트로 매핑하여 꺼낼 수 있는 기능이다.
Map map = {
'Apple' : '사과',
'Banana' : '바나나',
'Kiwi': '키위'
};
final newMap = map.entries.map((entry) {
final key = entry.key;
final value = entry.value;
return '$key는 한글로 $value 입니다.';
});
print(newMap);
// 결과
// (Apple은 한글로 사과 입니다., Banana는 한글로 바나나 입니다., Kiwi는 한글로 키위 입니다.)
entries
를 사용하면 .key
로 키에 접근할 수 있고, .value
로 밸류 값에 접근할 수 있다.
asMap()
리스트에서 인덱스 정보까지 받아서 확인하려면 asMap()
을 사용한다.
List<int> numbers = [10, 20, 30, 40, 50];
final newMap = numbers.asMap();
print(newMap);
//결과
//{0: 10, 1: 20, 2: 30, 3: 40, 4: 50}
세 가지의 메소드를 같이 사용하면 리스트를 맵으로 바꿔 키와 밸류 값에 접근이 가능하다.
List<int> numbers = [10, 20, 30;
final newMap = numbers.asMap().entries.map((entry) {
final index = entry.key;
final value = entry.value;
return 'index가 $index 일 때 값은 $value 입니다.';
});
print(newMap);
//결과
//(index가 0 일 때 값은 10 입니다., index가 1 일 때 값은 20 입니다., index가 2 일 때 값은 30 입니다.)
- image_picker
- 갤러리나 카메라에 접근해 이미지를 불러올 수 있도록 하는 패키지
- IOS는 권한 설정이 필요하다.
- 포토네컷 제작하기
- 추가 사항
image_picker를 사용해 아래의 요구사항을 충족하는 포토네컷 앱을 만들고자 한다.
dependencies:
image_picker: ^0.8.6+1
dependencies
에 image_picker
를 넣어 패키지를 설치했다.
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires Photo Library Permission</string>
image_picker
를 사용하기 위해 권한을 설정해 줘야 한다. 안드로이드 에뮬레이터를 사용하고 있어서 따로 권한 설정을 할 필요가 없긴 하지만 ios의 권한 설정을 위와 같이 해보았다.
import 'package:flutter/material.dart';
import 'package:my_app/page/home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(), //HomePage 호출
);
}
}
main.dart
에서는 HomePage
위젯을 호출한다.
import 'package:flutter/material.dart';
import 'package:my_app/widget/ImageContainer.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text('포토네컷'), //앱바 타이틀
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
),
// 이미지를 선택할 수 있는 4개의 요소를 가진 리스트 뷰
body: ListView.builder(
itemCount: 4,
itemBuilder: ((context, index) => const ImageContainer()), // 이미지가 들어가는 커스텀 위젯을 반환
),
);
}
}
HomePage
에서는 갤러리에서 이미지를 선택하여 띄울 수 있는 리스트 뷰를 생성하는데 총 4개의 요소를 넣어 주었다. ListView.builder
를 사용했으며 반환되는 각 위젯은 ImageContainer
로 커스텀 위젯을 만들어 사용하였다.
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class ImageContainer extends StatefulWidget {
const ImageContainer({super.key});
State<ImageContainer> createState() => _ImageContainerState();
}
class _ImageContainerState extends State<ImageContainer> {
var imagePicker = ImagePicker(); //image picker 객체
XFile? selectedImage; //선택한 이미지
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(40.0, 0, 40.0, 16.0),
//이벤트를 처리하기 위한 위젯
child: GestureDetector(
child: Container(
height: 180,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white12,
image: selectedImage != null // 선택된 이미지가 있을 경우에 출력
? DecorationImage(
fit: BoxFit.cover,
image: FileImage(
File(selectedImage!.path),
),
)
: null,
),
),
// 한 번 클릭 이벤트
onTap: () async {
var image = await imagePicker.pickImage(
source: ImageSource.gallery); // 갤러리에서 이미지를 불러옴
// 이미지가 선택된 경우
if (image != null) {
selectedImage = image; //selectedImage에 불러온 이미지를 저장
setState(() {}); // 화면 다시 그리기
}
},
//더블 클릭 이벤트
onDoubleTap: () => setState(() {
selectedImage = null; //선택된 이미지를 없애고 화면 다시 그리기
}),
),
);
}
}
ImageContainer
는 클릭했을 때 갤러리에서 이미지를 선택해 출력해 줄 수 있는 커스텀 위젯이다. 클릭과 더블클릭 이벤트를 처리하기 위해 GestureDetector
를 사용했고, Container
안에 선택된 이미지가 있을 경우에 그려주었다.
onTap
이벤트에서는 image picker로 갤러리에서 이미지를 불러온다. 이 때 불러온 이미지가 null이 아닐 경우 선택된 이미지에 저장하고 setState()
를 호출해 화면에 이미지를 그려준다.
onDoubleTap
이벤트에서는 setState()
로 빌드를 다시 호출하는데 여기서 선택된 이미지를 null로 저장해 이미지를 지워주었다.
Container
를 클릭하는 이벤트를 넣을 때 GestureDetector
위젯을 사용했다.
결과물 예시에서 보면 눌렀을 때 잉크가 퍼지는 듯한 효과가 있었기 때문에 GetstureDetector
보다는 InkWell
위젯을 사용하는 것이 더 좋았을 것 같다.
리스트에 map((element) => )
을 사용하면 리스트를 돌면서 element
의 값에 접근하여 원하는 형태로 만들 수 있는데 인덱스에는 접근할 수 없다.
asMap().entries.map((e) => )
을 사용하면 e.key
로 인덱스을 사용할 수 있고, e.value
로 값에 접근할 수 있다.
목요일에 졸업식을 다녀오는 바람에 18일차와 17일차를 금요일에 모두 했다. 다행히 17일차 내용이 어렵지 않아서 금방 해낼 수 있었다. 코드를 작성하고 강의를 봤을 때 약간 아쉬운 점이 있긴 했지만 어느정도 만족스러운 코드가 나온것 같다. ㅎㅎ 추가 사항에서 아쉬운 점을 하나 깜빡한게 있는데 이미지가 들어가는 컨테이너가 너무 작아질까봐 스크롤을 사용했지만 원래 과제의 의도는 스크롤이 없는 것이었다... 과제를 진행하기 전에 결과물을 조금 더 열심히 살펴보고 해야겠다. ㅠㅠ