How to upload images in Flutter using Appwrite
Tags: hackmamba, flutter, appwrite, appdevelopment
Published: 2022-11-30 07:35:23

Avatar background images and user profiles are examples of application features that developers use image uploads to scale. The project may sometimes be more significant in scope, such as a collection of photos accessible to the public or a developer's desire to learn about reactive systems.

This tutorial explains how to use [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) functions in reactive systems by creating an image upload and retrieval application. We will demonstrate how to select a picture from local storage, upload it to an Appwrite console, retrieve the list of all the images from the server, and update the UI.

[Here](https://github.com/muyiwexy/image_uploader/tree/image_uploader_branch) is the link to the GitHub repository containing all the code.

## Prerequisites 

To follow along with this tutorial, the following requirements apply:

- [Xcode](https://developer.apple.com/xcode/) (with developer account for Mac users).
- Either [IOS Simulator](https://developer.apple.com/documentation/xcode/running-your-app-in-the-simulator-or-on-a-device), [Android Studio](https://developer.android.com/studio), or [Chrome web browser](https://support.google.com/chrome/answer/95346?hl=en-GB&co=GENIE.Platform%3DDesktop) — to run our application.
- Docker installation (recommended), [DigitalOcean droplet](https://marketplace.digitalocean.com/apps/appwrite), or [Gitpod](https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod).
- An Appwrite instance. Check out this [article](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9) for the setup.
- The user interfaces template. [Here](https://github.com/muyiwexy/image_uploader/tree/master) is a link to the repository containing the UI template.


## Setting up the Appwrite project

To set up a project, we’ll type in the `host or IP address` of our device in the browser to open an appwrite console. Then, click on `create project` and give it a project name and ID (ID can be autogenerated).

![createproject1](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666094943722_Screenshot+38.png)
![createproject2](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666094943768_Screenshot+39.png)


In the home section of the Appwrite console, we will select `create platform,` and in the popup, select Flutter. Within the Flutter section, choose `Android` and input the `application name and package name` (we can find them in our app-level `build.gradle` file by heading to `android>app folder`).

![createplatform1](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666094857668_Screenshot+4.png)
![createplatform2](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666094857850_Screenshot+5.png)
![createplatform3](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666094926445_Screenshot+42.png)


Next, we want to set up a storage bucket. Head to the storage section and click on `add bucket`. Then, we’ll set the `bucket-level permission``'``s read and write access` to allow all users (role:all).

![create bucket](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666095016423_Screenshot+32.png)
![set bucket permission](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666095325771_Screenshot+54.png)


Finally, we want to create a database. To do so, we’ll navigate to the database section and click on `add database`. Then, create a collection and set the `collection-level permission``'``s read and write access` to allow all users (role:all). We will also create a required string attribute to store the `URL` of each image added to the storage.

![create database 1](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666095075539_Screenshot+28.png)
![create database 2](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666095075657_Screenshot+29.png)
![set database permission](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666095075850_Screenshot+30.png)

## Cloning the Flutter UI template and connecting to Appwrite

In this section, we will need to clone the [repository](https://github.com/muyiwexy/image_uploader/tree/master) specified in the prerequisites. To do that, click on the link to the repository, and within the repository, click on `code` and download it as a .zip file. To use the other methods, check out the GitHub [docs](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) on how to clone a repository.

![clone repo 1](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666095416354_Screenshot+52.png)
![clone repo 2](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666095416491_Screenshot+53.png)


Next, to utilize our Appwrite storage and database, we need to connect our Flutter project to the console. Doing this varies among devices. Thus, we will show the process of connecting to Android and iOS devices.

i**OS**
First, we will need to obtain the `bundle ID`, which we can do by going to the `project.pbxproj` file (`ios > Runner.xcodeproj > project.pbxproj`) and searching for our `PRODUCT_BUNDLE_IDENTIFIER`.

Now, we will head to the `Runner.xcworkspace` folder in the applications iOS folder in the project directory on [Xcode](https://developer.apple.com/xcode/). Now, we want to select the runner target, and to do this; we will choose the Runner project in the Xcode project navigator and then find the Runner target. Next, select `General` and `IOS 11.0` in the `deployment info section` as the target.

**Android**
To do this, copy the `XML` script below and paste it below the activity tag in the `Androidmanifest.xml` file ( to find this file, head to `android > app > src > main`).


{% embed https://gist.github.com/muyiwexy/6865c1b381146f8e2c324772dd3f2a16.js %}

> Note: change [PROJECT-ID] to the id you used when creating the Appwrite project


## Getting started

The cloned repository contains an `authprovider` folder, the `homepage.dart` file, and the `main.dart` file in the `lib` folder. When we run it, we should have a UI that looks like the image below:

![repo UI demo](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666022006501_file.jpeg)


As we can see above, there are two buttons (`select file and retrieve`) and a display area that shows the images uploaded to the database in a grid format. Currently, the template lacks functionality; thus, we will work on that in the coming sections.

Before we begin, we will need to add the following dependencies to the `pubspec.yaml` file:

- provider: ^6.0.3
- file_picker: ^4.5.1
- appwrite: ^6.0.0
![pubspec.yaml file](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1667382311811_Screenshot+65.png)


Next, within the lib folder, we will create two different folders; constants and models. In the constant folder, we will create a file called `app_constants.dart` and will add the code below to the file:


{% embed https://gist.github.com/muyiwexy/c30259a4818fa4ccdb8df9c518e6f189 %}

To get the `IP address` for a physical emulator, we will connect our device running the Appwrite instance and physical emulator to the same wifi device and type the command below in the terminal:


```javascript
ipconfig
```

The command above will bring out the IPs of the devices connected to our PC. 

**Creating the image upload feature**
We will need to create a model class to convert the response received when we send a file to the Appwrite storage bucket. We do this because, unlike the web SDK, Appwrite does not return a link when getting a file preview. Thus, we will have to create a URL manually for every file added to the storage bucket, and we will need two data (`bucketId` and `fileid`). 

Now, within the `model` folder, we will create a file called `fileurl.dart` and add the code below to it:


{% embed https://gist.github.com/muyiwexy/3aac424cb808b2cefeaf5ce829224ac9 %}

The code above creates a class called `UploadedFiles` with some required properties followed by some constructors. It then creates a `fromJson` and `toJson` method for serialization.

To create our application's core logic, we will update the code in the `imageprovider.dart` file in the template. Using the `provider package`, we can affect the state of our UI. Before we can do that, we need to update the `imageprovider.dart` file with the code below:


{% embed https://gist.github.com/muyiwexy/351dae7d6d7038d64f52e58da7daa36f %}

In the code above, we have a `ChangeNotifier` class. Within it, we called the `Appwrite client, account, storage` API, a `PlatformFile` (from the `file_picker` package), and also the model class we created earlier. Next, we called a `getter` for the model class and created a function called `_initialize()`. Within the `_initialize()` function, we set the `_selectedfile` and `_uploadedFiles` to null (initializers), initialize the Appwrite's API, and call the `_getaccount` function, which conditionally creates an `anonymous user session`.

Next, we will use the `file_picker` package within the `filepicker` function to select image files from our device storage. The function calls the `Filepicker` method and checks whether it returns null. If false, it will return the `result.files.first`.

{% embed https://gist.github.com/muyiwexy/d3f0d6684388926c0bdf31e9ca1373ba
 %}

As specified in the code above, we will need to create a function called `_uploadfile()`. After picking the file, we’ll use the `Appwrite storage API` to get the image selected by the path and upload it to the file bucket within our Appwrite storage. We will then check if the `id` is present, and if true, we will map the value of the uploaded image file to the model class; if false, we do nothing.


{% embed https://gist.github.com/muyiwexy/6865c1b381146f8e2c324772dd3f2a16 %}

**Creating the image retrieval feature**
Now that we can upload images to the server, we want to be able to get the image from the Appwrite server. As mentioned earlier, we created a model class to manually create a `URL`. Before we delve into that aspect, we will need to create a model class to map the data sent to the database.


{% embed https://gist.github.com/muyiwexy/b7526189b324d0c3839dbe964216a910
 %}

Now, we will call the Appwrite database API and the model class we created above, followed by a `getter` for the model class. Next, we will initialize the database API and call the `createdocument()` function in the `filepicker` function (add it before `notifylisteners`). The `createdocument()` will handle uploading the URL we created to the database.

The `createdocument()` function contains a manually created URL generated from an interpolation of other data stores (variables, constants, model classes), and here is how it looks:


```javascript
var url='${Appconstants.endpoint}/storage/buckets/${uploadedFiles!.bucketId}/files/${uploadedFiles!.id}/preview?project=${Appconstants.projectid}';
```

After, we will call the `createDocument` method from the Appwrite database function and set the data key and value pair to the attribute name created in the collection and our manually created `URL`, respectively.

Finally, we will create a future asynchronous function that will list the documents in our collection and map them to the model class `List<DocModel>`.

{% embed https://gist.github.com/muyiwexy/34bea18c4a456dedc64d9022204223a7 %}

**Consuming the service**
We want to use some of the functions from our `ChangeNotifier` class within our UI. On the `homepage.dart` file of the UI template, we wrapped the body property in a `Consumer` class (from the provider library). Thus, we can call functions and getters from the `ChangeNotifier` class to the UI. Thus, we will set the value of the `NetworImage` as below:


```javascript
image: NetworkImage(
   state.docmodel![index].url!)
    ),
```

Finally, here is how our application looks:

![result](https://paper-attachments.dropboxusercontent.com/s_01ED1A3B30AD8240F4108CE59E69A6AC5C89829E2CD8AD8F00F0B9D561EEF39D_1666096461042_ezgif.com-gif-maker+7.gif)

## Conclusion

In this tutorial, we created an Appwrite service to store uploaded image files, built custom URLs for each uploaded image using the File list object, created a database collection for the URL, and retrieved the URL to map it to the user interface. Here are some related resources you might find useful”

- [Appwrite databases API](https://appwrite.io/docs/client/databases?sdk=flutter-default?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Appwrite storage API](https://appwrite.io/docs/client/storage?sdk=flutter-default?utm_source=hackmamba&utm_medium=hackmamba-blog)

Thanks for reading, and happy coding!