Building a Mobile App with Appwrite: Auth, DB, and Storage
Learn how to build a full-stack mobile app with Appwrite — authentication, databases, and file storage — from a single Flutter codebase.
Published on • August 18, 2026
AI Assistant

Every app needs the same backend: sign-up and login, a place to store data, and a place to store files. You could stand up a server, a database, and object storage yourself — or use Appwrite, an open-source backend platform that provides Auth, Databases, Storage, Functions, Realtime, and Messaging behind a single API. You can run it self-hosted or on Appwrite Cloud, and its SDKs keep your client code thin.
In this tutorial, you will learn how to build a mobile app with Appwrite from scratch: initialize the SDK, implement email authentication, persist documents, and upload files — all in Flutter. Key technologies: Flutter, the appwrite Dart SDK, Appwrite Auth, Databases, and Storage.
Prerequisites
- An Appwrite Cloud project (or a self-hosted instance)
- Flutter 3.x installed
- A project ID and API endpoint from your Appwrite console
Core Content
Set up the project and SDK
Create a project in the Appwrite console, then add the SDK:
dependencies:
appwrite: ^13.0.0
Initialize the client once, at app startup. The endpoint is https://cloud.appwrite.io/v1 for Appwrite Cloud, or your self-hosted URL:
import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://cloud.appwrite.io/v1')
.setProject('your-project-id');
final account = Account(client);
final databases = Databases(client);
final storage = Storage(client);
Email authentication
Appwrite Auth covers email/password, OAuth, anonymous, and phone. The email flow:
// Sign up — creates the user and starts a session
final user = await account.create(
userId: ID.unique(),
email: emailController.text,
password: passwordController.text,
name: nameController.text,
);
// Sign in
final session = await account.createEmailSession(
email: emailController.text,
password: passwordController.text,
);
// Restore an existing session on app start
try {
await account.get();
} catch (e) {
// No session — show the login screen
}
account.get() is the idiomatic “am I logged in?” check; it throws if there’s no valid session, so a persistent login check is one call.
Store and query documents
Create a database and collection in the console, then use Databases to write and read documents:
// Create a document
final doc = await databases.createDocument(
databaseId: 'main',
collectionId: 'posts',
documentId: ID.unique(),
data: {
'title': 'Hello Appwrite',
'body': 'Created from a Flutter app',
'author': user.name,
},
);
// List documents
final result = await databases.listDocuments(
databaseId: 'main',
collectionId: 'posts',
queries: [Query.orderDesc('createdAt')],
);
for (final item in result.documents) {
print(item.data['title']);
}
Documents are JSON; collection schemas define attributes and permissions, so server-side validation and security live in the console rather than your client.
Upload and fetch files
Storage handles images, videos, and documents with built-in permission rules:
// Upload a file
final uploaded = await storage.createFile(
bucketId: 'avatars',
fileId: ID.unique(),
file: InputFile.fromPath(
path: pickedFile.path,
filename: 'profile.jpg',
),
);
// Build a viewable URL
final fileUrl = Uri.parse(
'https://cloud.appwrite.io/v1/storage/buckets/avatars/files/${uploaded.\$id}/view'
);
Pass the view URL to an Image.network widget, or read the file bytes with storage.getFileDownload.
Enforce permissions with Appwrite rules
Every document and file in Appwrite has role-based permissions — the client can only read/write what the rules allow. A common pattern for user-owned posts:
final doc = await databases.createDocument(
databaseId: 'main',
collectionId: 'posts',
documentId: ID.unique(),
data: {'title': 'Private note', 'owner': user.\$id},
permissions: [
Permission.read(User(user.\$id)),
Permission.update(User(user.\$id)),
Permission.delete(User(user.\$id)),
],
);
This is Appwrite’s real strength for mobile: security is configured declaratively, so a client can never access data it shouldn’t, even though the SDK is calling the API directly.
Listen to changes with Realtime
Appwrite Realtime pushes live updates to subscribed clients — useful for chat, feeds, and presence:
final subscription = client.subscribe(
['databases.main.collections.posts.documents'],
(payload) {
print('Document changed: ${payload.data}');
},
);
// Later, unsubscribe
await subscription.close();
Putting It All Together
A complete app wires the pieces together: Account for sign-up/login and session restore, Databases for structured data with permissions, Storage for files, and Realtime for live updates. Because Appwrite is framework-agnostic, the same project powers a Flutter app, a web app, and server-side functions — and you can migrate from a self-hosted instance to Cloud without changing your code.
Conclusion & Next Steps
You’ve built a mobile app backend with Appwrite: authentication, databases, file storage, permissions, and realtime — all from a thin Flutter client.
Next Steps: add OAuth login (Google, GitHub) with a few lines, use Appwrite Functions for server-side logic like email notifications, and explore the Appwrite MCP server and agent skills for AI-assisted development.
References:
- Appwrite — Documentation. https://appwrite.io/docs
- Appwrite — Flutter quick start. https://appwrite.io/docs/quick-starts/flutter
- Appwrite — Auth product docs. https://appwrite.io/docs/products/auth
- Appwrite — Databases product docs. https://appwrite.io/docs/products/databases