How to create a private image upload using Appwrite and Flutter
Femi-ige Muyiwa
Posted on December 29, 2022
Public image libraries such as Unsplash have been a breath of fresh air for content creators, enabling access to tons of copyright-free images. Sometimes, though, creators prefer privacy content and want to gatekeep "good" things. This article covers how to securely upload private images to Appwrite using Flutter.
This article demonstrates the following:
- User registration and login using Appwrite
- State management using the provider package in Flutter
- Picking images from storage using the
file_picker
package in Flutter and uploading the images to Appwrite - Setting document-level permission within Appwrite
- Obtaining the images from Appwrite and updating the UI
If you want to jump straight into the project, you can clone the repository containing the code for the project.
Prerequisites
The following requirements apply to follow along:
- Xcode (with developer account for Mac users).
- Either iOS Simulator, Android Studio, or Chrome web browser — to run your application.
- Docker installation (recommended), DigitalOcean droplet, or Gitpod.
- An Appwrite instance. Check out this article for the setup.
- The user interfaces template. Here is a link to the repository containing the UI template.
Setting up the Appwrite project
After setting up an Appwrite instance, head to your browser and type in your IP address
or hostname
. Next, select Create Project
and fill in your desired project name
and ID
.
Note: ID can be auto-generated
After creating a project, scroll down within the Appwrite console and select create platform
. When you get a popup, select Flutter
and choose the operating system you plan to work on
(in this case, choose Android
). Next, specify the application
and package names
(you can find the packaging in your app-level build.gradle
file).
After creating a platform, head to storage to create a bucket. To do that, click on add bucket
, then set the bucket-level permission's read and write access to allow all users (role:all
).
Unlike the other client-side SDKs, the Flutter SDK does not return a URL when previewing a storage file from Appwrite. In this tutorial, you will use a database to store the URL of every new image file upload. Head to the database section and select create database
.
Next, we’ll create a collection. When allowing public access to a collection, use collection-level permissions, preferably the permission role:all
. In this case, use document-level permission, as this allows all the users to share a single collection, but they have access to their documents only. Now, let’s create a required string attribute called url
to store the manually generated URL of each image added to the storage.
Cloning the Flutter UI template and connecting to Appwrite
This section uses a UI template containing user registration and login code. To get it, clone the repository specified in the prerequisites. Check out the official GitHub docs to learn more about cloning a repository.
If you're not new to Appwrite, you can proceed to the next step, but for new readers, this is how to connect a Flutter project to Appwrite for Android and iOS devices.
iOS
First, obtain the bundle ID
by going to the project.pbxproj
file (ios > Runner.xcodeproj > project.pbxproj
) and searching for the PRODUCT_BUNDLE_IDENTIFIER
.
Now, head to the Runner.xcworkspace
folder in the applications iOS folder in the project directory on Xcode. To select the runner target, 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
).
Note: change [PROJECT-ID] to the ID you used when creating the Appwrite project
Getting Started
When you run the cloned repo, you should have a UI that looks like the image below:
Above is a homepage section with an elevated button that satisfies multiple commands depending on the condition. These conditions range from checking if a user is logged in and redirecting you to a login page if false
to selecting an image(s) from your local storage, uploading it to Appwrite, and displaying the images in a grid.
Currently, the template lacks most of the functionality mentioned above. Thus, start by updating two folders (constants and model) within the lib folder with some new files. Starting with the constants folder, create a file called app_constants.dart
, which is required to store some constants that you will use later. Start by pasting the code below into the file:
Image upload
Next, we’ll create two model classes similar to the one in the template folder. Let’s use the first model class to map out the structure of the storage bucket, as we’ll manually create a URL. Thus, there are two essential data from every newly uploaded file: the bucketID
and the uploaded file's ID
.
To use the model class above, we need to be able to pick a file from your local storage and upload it to Appwrite. Thus, head to the privateprovider.dart
file and call the Appwrite storage API
, the model class created earlier, and a PlatformFile
class from the file_picker
package within the ChangeNotifier
class.
Next, create a getter for the model class, then set the value for the model class and the PlatformFile
class to null(initializers). Also, remember to initialize the Appwrite's storage API. Next, create a function to select images from local storage and call it within the conditional statement in the checklogin
function.
Next, create a function to upload the selected file from the local storage to the Appwrite storage bucket. In the function, we’ll need to map the JSON
result to the model class using the jsonEncode
and jsonDecode
functions provided by the dart convert
package. Finally, call the function within the filepicker
function, and it should look like this:
Image retrieval
To retrieve the images from the storage bucket, we need to use the database created earlier and create another model class to map the list of documents from the database. Thus, a model class like the one below is required:
Next, call the Appwrite database API and the model class above. Then, create a getter for the model class and initialize the database API.
After uploading the image to a storage bucket, we’ll "convert" the image into a URL. To do that, create another function, and within the function, create a variable with the value of a query string
created from using the endpoint, bucketid
, fileid
, and projectid
. Next, initialize the Appwrite database createDocument
method and set the data to the variable you created. Call the function in the file picker function so it can trigger the function automatically after uploading the files to the storage bucket.
Now, we’ll create another function to display the images using the listDocuments
method from the database function. Then map through the list of documents with respect to the model class created earlier. For this section, we need to be able to display the images once a user logs in or once they upload a new image. Thus, call the function in the _init
, login
, and createdocument
functions.
Now, the provider is ready for use, and all that is left is updating the home.dart
file with the code below:
The code above consists of a button that triggers a function linked to the checklogin
function within the ChangeNotifier
class (PrivateProvider
). The body uses a GridView.builder
, which creates a grid of containers with respect to the number of items in a list. In this case, it is the list of documents in the database. The containers are decorated using a NetworkImage
using the URL value obtained from the database collection.
Conclusion
This article explains how to use the Appwrite service to register and log in users as well as how to create document-level access to allow users to have permission to access documents particular to them alone. Using that idea, we’ve built a private image upload using the filepicker
package and Appwrite.
For further reading, here is a resource you might find helpful:
Posted on December 29, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.