기능은 무엇이 있나? 일단 기본적인 것부터 작성하고 다른 기능은 추후에 추가해보자.
key - value 형태로 값을 저장.Firebase console 에서 프로젝트 만들기
공식 홈페이지 접속 후 Go to console > 프로젝트 만들기
Firestore 데이터베이스 만들기
Cloud Firestore > 데이터베이스 만들기 > 가까운 곳 선택(Seoul)
curl -sL https://firebase.tools | bash
art pub global activate flutterfire_cli
flutterfire configure

스페이스를 입력하여 비활성화 가능
flutter pub add firebase_core
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_firebase_blog_app/firebase_options.dart';
import 'package:flutter_firebase_blog_app/ui/pages/home/home_page.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// ProviderScope 로 앱을 감싸서 RiverPod이 ViewModel 관리할 수 있게 선언
runApp(const ProviderScope(child: MyApp()));
}
// =========================

flutter pub add cloud_firestore

Future<void> getAll() async {
// 1. FirebaseFirestore 객체 가지고오기
FirebaseFirestore firestore = FirebaseFirestore.instance;
// 2. FirebaseFirestore 객체에서 collection 메서드를 통해 posts 컬렉션에 대한 참조 가지고 오기
// 여기까지는 아무런 통신이 이루어 지지 않고 단순히 posts 컬렉션에 대한 참조만 저장!!(posts 컬렉션을 가지고 올거야 하고만 알려주는 단계)
CollectionReference collectionRef = firestore.collection('posts');
// 3. 컬렉션 참조에서 모든 문서(Document) 가지고오기
// 이 때 Firestore에서 데이터 가지고옴
// 결과가 QuerySnapshot 타입으로 전달됨
QuerySnapshot snapshot = await collectionRef.get();
// 4. QuerySnapshot객체 내에 docs 필드에 조회된 문서들의 결과가 들어있는데
// QueryDocumentSnapshot 라는 타입임. 즉 문서 조회 결과
List<QueryDocumentSnapshot> documentSnaphots = snapshot.docs;
// 5. QueryDocumentSnapshot에서 data 메서드를 통해 진짜 데이터 가지고올 수 있음
for (var docSnapshot in documentSnaphots) {
print(docSnapshot.data());
}
}
Future<void> getOneById() async {
// 1. FirebaseFirestore 객체 가지고오기
FirebaseFirestore firestore = FirebaseFirestore.instance;
// 2. FirebaseFirestore 객체에서 collection 메서드를 통해 posts 컬렉션에 대한 참조 가지고 오기
// 여기까지는 아무런 통신이 이루어 지지 않고 단순히 posts 컬렉션에 대한 참조만 저장!!
CollectionReference collectionRef = firestore.collection('posts');
// 3. 컬렉션 참조에서 문서 ID에 대한 문서(Document) 참조 만들기
// 파라미터로 아까 만들때 생성된 ID 값 넣으면 됨
// ID는 중복되면 안됨!!!!!
DocumentReference documentRef = collectionRef.doc('문서 ID');
// 4. 문서 참조의 정보를 기반으로 파이어스토어에서 문서 가지고옴!!!
// DocumentSnapshot 타입. 문서 조회결과
DocumentSnapshot documentSnaphot = await documentRef.get();
// 5. DocumentSnapshot의 data 메서드를 통해 진짜 데이터 가지고올 수 있음
print(documentSnaphot.data());
}
Future<void> insert() async {
// 1. FirebaseFirestore 객체 가지고오기
FirebaseFirestore firestore = FirebaseFirestore.instance;
// 2. FirebaseFirestore 객체에서 collection 메서드를 통해 posts 컬렉션에 대한 참조 가지고 오기
// 여기까지는 아무런 통신이 이루어 지지 않고 단순히 posts 컬렉션에 대한 참조만 저장!!
CollectionReference collectionRef = firestore.collection('posts');
// 3. 컬렉션 참조에서 문서(Document) 참조 만들기
// ID를 파라미터로 넣지 않으면 문서 생성 시 새로운 ID 부여!
DocumentReference documentRef = collectionRef.doc();
// 4. 문서에 넣을 데이터를 Map 타입으로 생성
Map<String, dynamic> data = {
'writer': '김동연',
'title': '블로그 타이틀',
'content': '내용',
'createdAt': DateTime.now().toIso8601String(),
};
// 5. 문서 참조의 set 메서드 안에 생성할 데이터 전달해주면 이때 생성!!!
await documentRef.set(data);
}
Future<void> udpate() async {
// 1. FirebaseFirestore 객체 가지고오기
FirebaseFirestore firestore = FirebaseFirestore.instance;
// 2. FirebaseFirestore 객체에서 collection 메서드를 통해 posts 컬렉션에 대한 참조 가지고 오기
// 여기까지는 아무런 통신이 이루어 지지 않고 단순히 posts 컬렉션에 대한 참조만 저장!!
CollectionReference collectionRef = firestore.collection('posts');
// 3. 컬렉션 참조에서 문서(Document) 참조 만들기
DocumentReference documentRef = collectionRef.doc('수정할 문서의 ID');
// 4. 수정할 데이터를 Map 타입으로 생성
Map<String, dynamic> data = {
'writer': '오상구',
};
// 5. 문서 참조의 set 메서드 안에 생성할 데이터 전달해주면 이때 수정!!!
//업데이트 값 Map형태로 넣어주기
await documentRef.set(data); //id에 해당하는 문서가 없을 때 새로생성
// await docunentRef.update(data); << id에 해당하는 문서가 없을 때 에러발생
}
Future<void> delete() async {
// 1. FirebaseFirestore 객체 가지고오기
FirebaseFirestore firestore = FirebaseFirestore.instance;
// 2. FirebaseFirestore 객체에서 collection 메서드를 통해 posts 컬렉션에 대한 참조 가지고 오기
// 여기까지는 아무런 통신이 이루어 지지 않고 단순히 posts 컬렉션에 대한 참조만 저장!!
CollectionReference collectionRef = firestore.collection('posts');
// 3. 컬렉션 참조에서 문서(Document) 참조 만들기
DocumentReference documentRef = collectionRef.doc('삭제할 문서의 ID');
// 4. 참조하고 있는 문서 삭제!!!
await documentRef.delete();
}
Stream<List<Post>> postListStream() {
// 1. 컬렉션 참조를 만들어줍니다!
final collectionRef = FirebaseFirestore.instance
.collection('posts')
// 여기서 리스트 가지고 올 때 정렬 방식도 정할수 있어요!
// .count 나 .where과 같은 조건으로 가지고 올 수도 있어요!
.orderBy('createdAt', descending: true);
// 2. 참조의 스냅샷 메서드는 변경이 일어날 때마다 스트림에 값을 하나씩 넣어주는 역할을 해요!
final snapshotStream = collectionRef.snapshots();
// 3. 스트림을 가공해서 다른 List<Post> 타입의 스트림으로 만들게요!
return snapshotStream.map(
(event) {
return event.docs.map(
(doc) {
return Post.fromJson({'id': doc.id, ...doc.data()});
},
).toList();
},
);
}
Stream<Post?> postStream(String id) {
final snapshot = FirebaseFirestore.instance.collection('posts').doc(id);
return snapshot.snapshots().map(
(e) {
if (e.data() == null) {
return null;
}
return Post.fromJson({'id': e.id, ...e.data()!});
},
);
}
Future<void> fetchData() async {
// state = await postRepository.getAll();
// 1. 스트림을 받아옵니다
final stream = postRepository.postListStream();
// 2. 스트림의 변경사항을 구독하고 상태를 업데이트 해줍니다!
final streamSubscription = stream.listen(
(newList) {
state = newList;
},
);
// 2. 이 뷰모델이 없어질 때 구독을 끝내주어야 메모리에서 안전하게 제거가 돼요!
ref.onDispose(
() {
streamSubscription.cancel();
},
);
}
post를 뷰모델에서 가져온 값으로 바꾸어 줍니다import 'package:flutter/material.dart';
import 'package:flutter_firebase_blog_app/data/model/post.dart';
import 'package:flutter_firebase_blog_app/ui/pages/detail/detail_view_model.dart';
import 'package:flutter_firebase_blog_app/ui/pages/write/write_page.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class DetailPage extends StatelessWidget {
DetailPage(this.post);
Post post;
Widget build(BuildContext context) {
// Consumer로 감싸주기!
return Consumer(builder: (context, ref, child) {
// 상태 변화를 감지하기 위해!
final state = ref.watch(detailViewModel(post));
// 삭제된 포스트면 로딩창 나오게!!
if (state == null) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
appBar: AppBar(
actions: [
button(Icons.delete, () async {
final result =
await ref.read(detailViewModel(post).notifier).delete();
if (result) {
Navigator.pop(context);
}
}),
button(Icons.edit, () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return WritePage(post: post);
},
));
}),
],
),
body: ListView(
padding: EdgeInsets.only(bottom: 300),
children: [
Image.network(
state.imgUrl,
fit: BoxFit.cover,
),
SizedBox(height: 20),
// 이미지에는 패딩이 적용되지 않기 때문에 아래 영역만 Padding 위젯
// 감싸서 패딩 지정!
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
SizedBox(height: 14),
Text(
state.writer,
style: TextStyle(
fontSize: 16,
),
),
Text(
'${state.createdAt.year}.${state.createdAt.month}.${state.createdAt.day} ${state.createdAt.hour}:${state.createdAt.minute}',
style: TextStyle(
fontWeight: FontWeight.w200,
fontSize: 14,
),
),
SizedBox(height: 14),
Text(
state.content,
style: TextStyle(
fontSize: 16,
),
),
],
),
),
],
),
);
});
}
GestureDetector button(IconData icon, VoidCallback onTap) {
return GestureDetector(
onTap: onTap,
child: Container(
// Icon 크기가 기본 24 이기 때문에 컨테이너 색상을 지정해주지 않으면
// 터치 반경 24x24 됨
color: Colors.transparent,
width: 50,
height: 50, // 중요!!! 사람이 터치할때 터치 잘되게 하는 최소 반경이 44 디바이스 픽셀!
child: Icon(icon),
),
);
}
}
컬렉션 전체 리스트(코드는 위에 참고, 버전에 따라 달라질 수 있음)
Firestore 객체 가지고 오기 → 컬렉션 참조 만들기 → 전체 문서 가져오기
Firestore 객체 가지고 오기 → 컬렉션 참조 만들기 → 문서 참조 만들기 → 읽기,수정,삭제,생성
main.dart 의 main함수 에서 호출해서 테스트

데이터 CRUD가 일어난 뒤 화면에 반영
flutter pub add firebase_storage
Future<void> uploadImage(XFile xFile) async {
try {
// FirebaseStorage 객체 가지고오기
FirebaseStorage storage = FirebaseStorage.instance;
// 스토리지 참조 가지고 오기
Reference storageRef = storage.ref();
// 스토리지 참조의 child 메서드를 사용하면 파일 참조 만들어짐
// 파라미터는 파일 이름!!
// 중복되면 안되니까 현재시간이랑 기존 파일이름 섞을게요!
final imageRef = storageRef
.child('${DateTime.now().microsecondsSinceEpoch}_${xFile.name}');
// 참조가 만들어졌으니 파일 업로드!!
await imageRef.putFile(File(xFile.path));
// 만들어진 파일의 url 가져오기
final url = await imageRef.getDownloadURL();
state = WritePageState(false, url);
} catch (e) {
print(e);
}
}
`image_picker`사용하여 디바이스에 있는 갤러리 접근가능.
```bash
flutter pub add image_picker
ios/Runner/info.plist <key>NSPhotoLibraryUsageDescription</key>
<string>사진 업로드를 위한 라이브러리 권한을 허용해 주세요</string>

< > 형태로 감싸고 HTML과 유사) // 이미지피커 객체 생성!
ImagePicker imagePicker = ImagePicker();
// pickImage 메서드호출해서 가지고옴
// 갤러리 권한만 넣어줬으니 ImageSource.gallery!
// 호출하면 폰에서 앨범이 열림
// 사용자가 사진을 선택하지 않을 떈 null
// 선택하면 XFile이라는 타입으로 돌려주는데
// 사진의 경로를 가지고 있는 path, 경로에서 바이트 데이터를 얻을 수 있는 readAsBytes() 메서드 등이 있음
XFile? xFile = await imagePicker.pickImage(source: ImageSource.gallery);
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: () async {
final xFile = await ImagePicker()
.pickImage(source: ImageSource.gallery);
if (xFile != null) {
// 뷰모델에 넘겨주기!
vm.uploadImage(xFile);
}
},
child: Container(
width: 100,
height: 100,
color: Colors.grey,
child: Icon(Icons.image),
),
),
),
Future<void> uploadImage(XFile xFile) async {
try {
// FirebaseStorage 객체 가지고오기
FirebaseStorage storage = FirebaseStorage.instance;
// 스토리지 참조 가지고 오기
Reference storageRef = storage.ref();
// 스토리지 참조의 child 메서드를 사용하면 파일 참조 만들어짐
// 파라미터는 파일 이름!!
// 중복되면 안되니까 현재시간이랑 기존 파일이름 섞을게요!
final imageRef = storageRef
.child('${DateTime.now().microsecondsSinceEpoch}_${xFile.name}');
// 참조가 만들어졌으니 파일 업로드!!
await imageRef.putFile(File(xFile.path));
// 만들어진 파일의 url 가져오기
final url = await imageRef.getDownloadURL();
state = WritePageState(false, url);
} catch (e) {
print(e);
}
}
Future<void> uploadImage(XFile? xFile) async {
}