#100DaysOfCodeChallenge - Crop Management Information System - Day 9

Recap

On Day 8 we solved the PlatformException (PlatformException(no_available_camera, No cameras available for taking pictures., null, null))

Overview

In this post we will discuss how to access the device camera and save photo in cloud storage.

Going Deeper

We will use the device camera to take a photo, next, we will save the photo to cloud storage and finally we will save the url reference of the cloud storage photo to the farmer document on cloud firestore.

Using Device Camera.

class _AddFarmerScreenController extends State<AddFarmerScreen> {
  
  Widget build(BuildContext context) => _AddFarmerScreenView(this);
  FarmerServiceModel farmer = FarmerServiceModel.form();

  final _picker = ImagePicker();
  PickedFile? _pickedFile;
  late File? _farmerProfilePicture;
}

To recap, the class _AddFarmerScreenController() is responsible for the all the logic associated with the class _AddFarmerScreenView(). The latter class is responsible for the layout of the AddFarmerScreen.

The ImagePicker() is from the flutter image_picker package. This class gives us access to the methods we'll need to use the device camera.

PickedFile is returned from the method call that takes a photo from the device camera.

File is the photo that will be stored in cloud storage.

Take Photo Method

void _handleTakePhoto() async {
    _pickedFile = await _picker.getImage(source: ImageSource.camera);
    _farmerProfilePicture = File(_pickedFile!.path);
    setState(() {});
  }

_handleTakePhoto() will be called when the user press the take farmer picture button. _picker.getImage(source: ImageSource.camera) is the method that gives us access to the device camera. The returned photo is a PickedFile. The _pickedFile is converted to a File by File(_pickedFile!.path) and stored in the _farmerProfilePicture variable.

ImageProvider<Object>? _imagePreview() {
    if (_pickedFile != null) {
      if (kIsWeb) {
        return NetworkImage(_pickedFile!.path);
      } else {
        return FileImage(File(_pickedFile!.path));
      }
    } else {
      return null;
    }
  }

_imagePreview() method is assigned to:

CircleAvatar(
  backgroundImage: state._imagePreview(),
  minRadius: 200.0,
),

This widget displays a preview of the photo taken as shown in the screenshot below.
Preview of Farmer Profile

Saving Form Data

void _handleRegisterFarmer() async {
    if (_formkey.currentState!.validate()) {
      _formkey.currentState!.save();
      farmer.saveRegistrationNumber('');
      AddFarmerCommand(context).run(
          farmerServiceModel: farmer,
          context: context,
          farmerProfilePicture: _farmerProfilePicture);
    }
  }

When the form data is saved, the farmer's profile picture is passed to the AddFarmerCommand().run() method along with the other farmer data and the build context.

Saving farmer profile picture to cloud storage.

/// This class is responsible for farmer registration.
class AddFarmerCommand extends BaseCommand {
  AddFarmerCommand(BuildContext c) : super(c);

  /// Calls FarmerService.addFarmer method
  ///
  /// Receives farmer data and buildcontext from widget and pass it to the farmerService.addFarmer and fileService.uploadFarmerProfilePicture method.
  Future<bool> run({
    required FarmerServiceModel farmerServiceModel,
    required File? farmerProfilePicture,
    required BuildContext context,
  }) async {
    bool farmerAddedSuccess = false;

    if (farmerProfilePicture != null) {
      final farmerProfilePictureUrl =
          await fileservice.uploadFarmerProfilePicture(farmerProfilePicture);
      farmerServiceModel.saveProfilePicture(farmerProfilePictureUrl);
    }
    await farmerService
        .addFarmer(farmerServiceModel: farmerServiceModel)
        .then((value) => farmerAddedSuccess = true);
    return farmerAddedSuccess;
  }
}

This method is comprised of two main parts. await fileservice.uploadFarmerProfilePicture() and await farmerService.addFarmer()

fileService.uploadFarmerProfilePicture()

Only if the farmer's profile picture is not null, the fileService.uploadFarmerProfilePicture() method will be called.

class FileStorageService {
  final storage = firebase_storage.FirebaseStorage.instance;

  Future<String?> uploadFarmerProfilePicture(
    File farmerProfilePicture,
  ) async {
    String? farmerProfilePictureUrl;
    try {
      await storage
          .ref()
          .child('farmer_profile_picture')
          .child(basename(farmerProfilePicture.path))
          .putFile(farmerProfilePicture)
          .then((value) async {
        farmerProfilePictureUrl = await value.ref.getDownloadURL();
      });
    } on firebase_storage.FirebaseException catch (e) {
      // e.g, e.code == 'canceled'
      print(e);
    }
    return farmerProfilePictureUrl;
  }
}

The farmerProfilePicture is saved in cloud storage using await storage.ref().child().child.putFile().then() method. It saves the farmerProfilePicture in a farmer_profile_picture folder. It then returns a url reference to the file in the then() method. This reference is stored in cloud firestore along with the remainder of the farmer data when the await farmerService.addFarmer() method is called.

Wrap Up

In this post, we discussed how to save a photo from the device camera in cloud storage and store a url cloud reference of the photo from cloud storage with the remainder of the farmer data in cloud firestore.

Below are screenshots of the process.

Connect with me

Thank you for reading my post. Feel free to subscribe below to join me on the #100DaysOfCodeChallenge or connect with me on LinkedIn and Twitter. You can also buy me a book to show your support.

24