image_picker 설치와 셋팅
- pubspec.yaml 파일에 코드 추가 후 pub get
dependencies: image_picker: ^0.8.4+4
- ios/Runner/info.plist 파일에 코드 추가
- 하단에 추가
- 사용자에게 허락 팝업 띄울 때 보이는 글자들
<key>NSPhotoLibraryUsageDescription</key> <string>사진첩좀 써도 됩니까</string> <key>NSCameraUsageDescription</key> <string>카메라좀 써도 됩니까</string> <key>NSMicrophoneUsageDescription</key> <string>마이크 권한좀 제발</string>
- dart 파일 맨 위에 import 추가
import 'package:image_picker/image_picker.dart'; import 'dart:io';
image_picker 사용법
onPressed: () async { var picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.gallery); }
⇒ 이미지 선택 화면 뜨는 코드
- picker.pickImage(source: ImageSource.camera);
- 사진 선택하는 갤러리 말고 카메라를 띄워줌
- picker.pickVideo(source: ImageSource.gallery);
- 비디오 선택 화면이 뜸
- picker.pickMultiImage(source: ImageSource.gallery);
- 여러 이미지 선택이 가능
→ 고른 이미지 사이즈, 화질 조정도 가능
→ photofilters 패키지 설치하면 이미지에 필터 씌울 수 있음
선택한 이미지 다루기
- 이미지는 용량이 크므로 변수에 저장하기보단 이미지의 경로만 가져와서 변수에 저장하고 사용하는게 일반적임
onPressed: () async { var picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.gallery); if (image != null) { // 선택 안하면 null이므로 체크 setState((){ userImage = File(image.path); }); } }
선택한 이미지를 위젯으로 보여주기
- Image.file(userImage) 사용하면 됨
- 이미지 파일 경로를 넣으면 이미지로 보여주는 위젯
- userImage 변수는 MyApp() 위젯 안에 있으므로 Upload로 변수를 보내서 사용
전체 코드
import 'package:flutter/material.dart'; import './style.dart' as style; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:flutter/rendering.dart'; import 'package:image_picker/image_picker.dart'; import 'dart:io'; void main() { runApp( MaterialApp( theme: style.theme, home: MyApp() )); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { var tab = 0; // 1. 현재 상태 저장 var list = [1, 2, 3]; var map = {'name':'john', 'age':20}; var data = []; var userImage; addData(a) { setState(() { data.add(a); }); } getData() async { var result = await http.get(Uri.parse('https://codingapple1.github.io/app/data.json')); var result2 = jsonDecode(result.body); setState(() { data = result2; }); } @override void initState() { super.initState(); getData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Instagram'), actions: [ IconButton( icon: Icon(Icons.add_box_outlined), onPressed: () async { var picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.gallery); if(image != null) { setState(() { userImage = File(image.path); }); } Navigator.push(context, MaterialPageRoute(builder: (c) => Upload( userImage : userImage) ) ); }, iconSize: 30, ) ]), body: [Home(data : data, addData : addData), Text('샵페이지')][tab], // 2. state에 따라 tab 보이는 상태 변경 bottomNavigationBar: BottomNavigationBar( showSelectedLabels: false, showUnselectedLabels: false, onTap: (i){ setState(() { tab = i; }); }, items: [ BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: '홈'), BottomNavigationBarItem(icon: Icon(Icons.shopping_bag_outlined), label: '샵') ], ), ); } } class Home extends StatefulWidget { const Home({Key? key, this.data, this.addData}) : super(key: key); final data, addData; @override State<Home> createState() => _HomeState(); } class _HomeState extends State<Home> { var scroll = ScrollController(); getMore() async { var result = await http.get(Uri.parse('https://codingapple1.github.io/app/more1.json')); var result2 = jsonDecode(result.body); widget.addData(result2); } @override void initState() { super.initState(); scroll.addListener(() { // 스크롤바 높이 측정 코드 if(scroll.position.pixels == scroll.position.maxScrollExtent) { getMore(); } }); } @override Widget build(BuildContext context) { if (widget.data.isNotEmpty) { return ListView.builder( itemCount: widget.data.length, controller: scroll, itemBuilder: (c, i) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.network(widget.data[i]['image']), Container( constraints: BoxConstraints(maxWidth: 600), padding: EdgeInsets.all(20), width: double.infinity, child: Column( children: [ Text('좋아요 ${widget.data[i]['likes'].toString()}'), Text(widget.data[i]['date']), Text(widget.data[i]['content']) ], ), ) ], ); } ); } else { return Text('로딩중임'); } } } class Upload extends StatelessWidget { const Upload({Key? key, this.userImage}) : super(key: key); final userImage; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.file(userImage), Text('이미지업로드화면'), TextField(), IconButton( onPressed: (){ Navigator.pop(context); }, icon: Icon(Icons.close) ), ], ) ); } }
숙제
→ 발행 버튼 누르면 유저가 입력한 글과 사진 게시물로 보여주기
- 우선 발행 버튼 하나 만들고
- Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( actions: [ IconButton(onPressed: (){}, icon: Icon(Icons.send)) ]), (생략)
- 유저가 입력한 글과 사진을 MyApp 안에 state에 저장해둠
- 유저가 글 입력하면 userContent에 저장되게 만들기
(Upload페이지) TextField(onChanged: (text){ setUserContent(text); })
- 유저가 글 입력하면 userContent에 저장되게 만들기
- class _MyAppState extends State<MyApp> { var tab = 0; var data = []; var userImage; var userContent; setUserContent(a){ setState(() { userContent = a; }); } (생략)
- 발행 버튼 누르면 var data = [] 에 게시물 데이터 하나 추가
- 게시물 데이터엔 유저가 선택한 사진과 글이 들어가면 됨
- list 맨 앞부분에 자료 추가하고 싶으면 data.insert(0, 자료) 사용
class _MyAppState extends State<MyApp> { var tab = 0; var data = []; var userImage; var userContent; addMyData(){ var myData = { 'id': data.length, 'image': userImage, 'likes': 5, 'date': 'July 25', 'content': userContent, 'liked': false, 'user': 'John Kim' }; setState(() { data.insert(0, myData); }); } (생략) - Upload 페이지의 발행 버튼 누르면 저 함수 실행
Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( actions: [ IconButton(onPressed: (){ addMyData(); }, icon: Icon(Icons.send)) ]), (생략)
- 이상한 건 if문으로 예외 처리
- 3번까지 하고 돌리면 콘솔창에 에러남
- type '_File' is not a subtype of type 'String’
- 이미지 보여줄 때 Image.network() 여기에 ‘https://어쩌구’ 이런 거 넣어서 보여주고 있는데 사용자가 선택한 건 File 타입이기 때문에 타입 에러 발생
- 그래서 삼중 연산자를 통해 이미지 경로가 문자이면 무엇을, 그에 아니라면 이것을 보여달라고 코드 구현 - Image.network(widget.data[i]['image']) // -> 이렇게 변경 widget.data[i]['image'].runtimeType == String ? Image.network(widget.data[i]['image']) : Image.file(widget.data[i]['image']),
'Web & Android > Flutter' 카테고리의 다른 글
[Flutter] 위젯 클릭 - GestureDetector (0) | 2023.09.22 |
---|---|
[Flutter] DB 없이 데이터 저장하기 - Shared preferences (0) | 2023.09.22 |
[Flutter] 페이지 나누기 - 라우터 사용 (/) (0) | 2023.09.22 |
[Flutter] 페이지 나누기 - Navigator (0) | 2023.09.22 |
[Flutter] 페이지 나누기 - Tab (0) | 2023.09.22 |