Flutter Firebase Storage & Store(db)

강정우·2023년 8월 15일
0

Flutter&Dart

목록 보기
62/87
post-thumbnail

Firebase Storage

  • 앞서 설치한 firebase SDK에서 제공해주는 Image Storage 기능이다.

  • 사실 이 기능은 back-end가 구축되어있다면 신경쓸 필요가 없는 부분이라 넘어가려했는데 기왕 firebase SDK 설치한거 auth 부터 storeage, notification 까지 한 번 뽕을 뽑아보자

  • 우선 Storage를 시작하면 Rule를 바꿔줘야한다.

  • write 부분을 request.auth가 null이 아닐 때 즉, 사용자가 validate할 때 저장을 허용하겠다는 뜻이다.

설치

  • 이제 관련 패키지를 설치해보자. 첫번째로는 DB관련 storage 패키지와 앞서 사용한 적이 있는 Image_picker 패키지이다.
flutter pub add firebase_storage
flutter pub add image_picker

UI 설계

class UserImagePicker extends StatefulWidget {
  const UserImagePicker({super.key, required this.onPickedImage});

  final void Function(File pickedImage) onPickedImage;

  
  State<UserImagePicker> createState() {
    return _UserImagePickerState();
  }
}
  • 부모로 부터 function을 넘겨받아서 값을 전달해주고 해당 state로 인하여 UI(Circle Avartar)가 변경되어야하기 때문에 StatefulWidget으로 작성해주어야한다.

  • drop drilling을 위한 함수를 생성해준다.

class _UserImagePickerState extends State<UserImagePicker> {
  File? _pickedImageFile;

  void _pickImage() async {
    final pickedImage = await ImagePicker()
        .pickImage(source: ImageSource.camera, imageQuality: 50, maxWidth: 150);
    if (pickedImage == null) {
      return;
    }
    setState(() {
      _pickedImageFile = File(pickedImage.path);
    });

    widget.onPickedImage(_pickedImageFile!);
  }

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        CircleAvatar(
          radius: 40,
          backgroundColor: Colors.grey,
          foregroundImage:
              _pickedImageFile != null ? FileImage(_pickedImageFile!) : null,
        ),
        TextButton.icon(
          onPressed: _pickImage,
          icon: const Icon(Icons.image),
          label: Text(
            'Add Image',
            style: TextStyle(color: Theme.of(context).colorScheme.primary),
          ),
        ),
      ],
    );
  }
}
  1. CircleAvatar 를 이요해서 background color를 grey로 잡아주면 될 듯 하다. 그리고 foreground image를 주어 이미지 null이 아닐 때 background를 덮어버리면 될 듯 하다.

  2. TextButton.icon() 메서드를 사용해서 Add Image 기능을 구현하면 될 듯 하다.

  3. TextButton의 onPressed 속성값은 ImagePicker와 function을 사용한 drop drilling을 이용하면 될 듯 하다.

사용

child: Column(
  mainAxisSize: MainAxisSize.min,
  children: [
  if (!_isLogin)
UserImagePicker(
  onPickedImage: (pickedImage) {
  _selectedImage = pickedImage;
},
  ),
    TextFormField(
      
    ...
  • 위 UI에서 작성한 UserImagePicker 위젯에서 매개변수로 값을 넘겨받아 저장한다.
class _AuthScreenState extends State<AuthScreen> {
  final _form = GlobalKey<FormState>();

  var _isLogin = true;
  var _enteredEmail = '';
  var _enteredPassword = '';
  File? _selectedImage;
  var _isAuthenticating = false;

  void _submit() async {
    final isValid = _form.currentState!.validate();

    if (!isValid || !_isLogin && _selectedImage == null) {
      return;
    }

    _form.currentState!.save();

    try {
      setState(() {
        _isAuthenticating = true;
      });
      if (_isLogin) {
        final userCredentials = await _firebase.signInWithEmailAndPassword(
            email: _enteredEmail, password: _enteredPassword);
      } else {
        final userCredentials = await _firebase.createUserWithEmailAndPassword(
            email: _enteredEmail, password: _enteredPassword);
        final storageRef = FirebaseStorage.instance
            .ref()											// <= 여기 ref
            .child('user_images')
            .child('${userCredentials.user!.uid}.jpg');
        await storageRef.putFile(_selectedImage!);
        final imageUrl = await storageRef.getDownloadURL();
        print(imageUrl);
      }
    } on FirebaseAuthException catch (e) {
      if (e.code == 'email-already-in-use') {
        // 실제로는 각 error code 마다 에러 메시지를 작성해주면 좋겠지? 하지만 여기서는 생략!
      }
      ScaffoldMessenger.of(context).clearSnackBars();
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(e.message ?? 'Authentication Fail'),
        ),
      );
      setState(() {
        _isAuthenticating = false;
      });
    }
  }

  
  Widget build(BuildContext context) {
  1. selecectedImage 변수를 사용하기 위해 File type의 변수를 optional로 생성한다.
  2. 제출 버튼에 유효성 검사("if문")를 진행한다.
  3. 만약 사용자가 join일 경우,

  • Authentication과 Storage는 별개이기 때문에 코드를 따로 작성해주어야한다.
    참고로 이때 코드에서 ref는 firebase cloud storage의 ref객체이다.
  1. .child() 매서드는 / 와 같은 개념이다.
  2. putFile() fireBase에 실제로 저장하는 메서드이다.
  3. getDownloadURL() cloud에 저장한 image의 주소를 저장하는 메서드이다.


  • 그런데 사실 정확하게 알아야할 것이 있는데 우리가 저장한 것은 usermeta data(image의 이름, 사용자 id 등)이지 진짜 blob 형식의 image가 아니라는 것이다.
    이는 storage와 data base의 차이이다.
    따라서 이제 Storage가 아닌 real time data base가 아닌 firebase stroage 대해 알아보겠다.

firebase storage

  • 그럼 일반적인 realtime database와는 무슨 차이인가? => 조금 더 elaborate하고 복잡하다.

  • 그리고 만약 당신이 firebase console에서 fire store를 생성하였는데 혹시 자동을 google cloude console로 안내를 해주지 않는다면 다음과 같은 절차를 따르면 된다.

  1. google에 google cloude console를 입력한다.
    1-1. 본인의 prj에 들가져있어야한다.
  2. 검색창에 datastore entity를 입력한다.
  3. 2번째 data store로 전환(?) 2번째걸 누른다.
  4. 다시 fifebase console로 돌아와 새로고침을 누른다.

  • 전환한다.

  • 그리고 해당 firestore 역시 규칙에 request.auth != null로 로그인을 해야만 이용할 수 있도록 규칙을 수정해준다.
    하지만 이렇게만 해두면 타 사용자의 chat 내용까지 모두 불러 읽어보기 때문에 여기서는 firebase fire store security rules를 검색하여 추가적인 규칙을 더 넣어줘야한다.(back-end가 있다면 사실 모두 필요없지만...)

설치

flutter pub add cloud_firestore
  • 콘솔에 추가해주고 패키지를 설치했다면 restart가 아닌 완전 종류 후 다시 시작해줘야한다.

  • 그럼 위와같이 minSDK가 아닌 직접 버전을 19로 명시하라고 나와있다. (23.08.15 기준) 그럼 명시 해주고, 추가적으로 multiDexEnable true를 명시해주면 된다.

multiDex?

  • multiDexDenable이란 원래 flutter는 하나의 Dex 파일만을 지향했다. 하지만 파일의 코드와 resource가 증함에 따라 하나의 Dex 파일로는 다 담지 못 하자 크기제한을 극복하고자 multiDex의 개념을 도입한 것이다.

Dex 파일?

  • DEX 파일은 안드로이드 앱의 실행 가능한 코드와 리소스를 포함하고 있는 파일로, Dalvik 가상 머신이나 ART와 같은 실행 환경에서 사용되어 안드로이드 앱을 실행하는 데 필요한 정보를 담고 있다.

  • 최적화: DEX 파일은 안드로이드 앱의 실행을 최적화하기 위해 최적화된 바이트 코드 형식이다. Dalvik 가상 머신은 DEX 파일을 더 효율적으로 실행할 수 있도록 설계되어 있다.

  • 압축 및 분할: DEX 파일은 압축되어 있으며, 안드로이드 앱이 다양한 디바이스 및 환경에서 실행될 수 있도록 여러 개의 DEX 파일로 분할될 수 있다. 이렇게 분할된 DEX 파일들은 앱의 크기를 최적화하고, 다양한 Android 버전 및 기기에서 호환성을 유지하는데 도움을 준다.

  • 실행: 안드로이드 앱이 실행될 때, DEX 파일은 안드로이드 시스템에 의해 로드되고 실행된다. 앱의 코드와 리소스는 DEX 파일에서 추출되어 실제로 실행되는 프로세스에 사용된다.

  • ART로의 전환: 안드로이드 5.0 (Lollipop) 버전부터는 Dalvik 가상 머신 대신 ART(Android Runtime)가 사용되며, ART는 AOT(Ahead-Of-Time) 컴파일을 통해 앱을 실행한다. ART는 DEX 파일을 미리 네이티브 기계 코드로 변환하여 실행 속도를 향상시킨다.

사용

await FirebaseFirestore.instance
  .collection('users')
  .doc('userCredentials.user!.uid')
  .set({
  'username': _enteredUsername,
  'email': _enteredEmail,
  'image_url': imageUrl,
});
  • 역시 firebasestore또한 여타 다른 firebase sdk package처럼 instance를 생성후 collection() 으로 콜렉션을 만든다.
    여기서 collection은 약간 folder의 느낌이다.

  • 그리고 해당 collection에 각 유저마다 데이터를 map 형태로 저장하기 위해 doc() 메서드로 생성해주고
    set() 함수로 Map 데이터를 넘겨주면 된다.

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글