Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9e41847
Write tutorial to build an ideas tracker app with flutter
blueberry-adii Apr 11, 2026
331a168
Update step 4 and set obscure text to true in password field
blueberry-adii Apr 14, 2026
e7bbbbb
add type and size fields in table column properties
blueberry-adii Apr 14, 2026
8c90904
handle exceptions by wrapping login and register in try-catch block
blueberry-adii Apr 14, 2026
9debd4f
replace print with debugPrint for consistency
blueberry-adii Apr 14, 2026
923f628
add steps to create a database in the console
blueberry-adii Apr 14, 2026
4cbfb29
increase varchar size to 200 as per other tutorials
blueberry-adii Apr 14, 2026
e7e123c
handle exceptions by Ideas provider using try-catch blocks
blueberry-adii Apr 14, 2026
60309e3
fix ID names for database and table for consistency
blueberry-adii Apr 14, 2026
07b5349
mounted check before using context after async gap
blueberry-adii Apr 14, 2026
07654cc
mounted check before using context after async gap
blueberry-adii Apr 14, 2026
f1f57cd
fix double catch blocks by removing empty catch block
blueberry-adii Apr 14, 2026
c07eb4e
fix error handling and UI inconsistencies
blueberry-adii Apr 14, 2026
f72b517
change description to match disable remove button if user not owner
blueberry-adii Apr 14, 2026
f181e6c
move isOwner check inside Button function outside to disable button c…
blueberry-adii Apr 14, 2026
0960f13
replace Styles.disabledButton by adding disabledBackgroundColor to St…
blueberry-adii Apr 14, 2026
f25924e
fix typo: idea.userId to userId
blueberry-adii Apr 14, 2026
5f13426
specify to replace <REGION> in appwrite endpoint
blueberry-adii Apr 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions src/routes/docs/tutorials/flutter/step-1/+page.markdoc
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
---
layout: tutorial
title: Coming soon
description: Learn to build an Flutter app with no backend code using an Appwrite backend.
framework: Flutter
back: /docs/tutorials
category: Mobile and native
title: Build an ideas tracker with Flutter
description: Learn to build a Flutter app with no backend code using an Appwrite backend.
step: 1
draft: true
difficulty: beginner
back: /docs/tutorials
readtime: 15
category: Mobile and native
framework: Flutter
---

Improve the docs, add this guide.
**Idea tracker**: an app to track all the side project ideas that you'll start, but probably never finish.
In this tutorial, you will build Idea tracker with Appwrite and Flutter.

## Concepts {% #concepts %}
This tutorial will introduce the following concepts:

1. Setting up your first project
2. Authentication
3. Databases and tables
4. Queries and pagination

We still don't have this guide in place, but we do have some great news.
The Appwrite docs, just like Appwrite, is completely open sourced.
This means, anyone can help improve them and add new guides and tutorials.

If you see this page, **we're actively looking for contributions to this page**.
Follow our contribution guidelines, open a PR to [our Website repo](https://github.com/appwrite/website), and collaborate with our core team to improve this page.
## Prerequisites {% #prerequisites %}
1. Android, iOS simulators, or a physical device to run the app
2. Have [Dart](https://dart.dev/) and [Flutter SDK](https://flutter.dev/) installed on your computer
3. Basic knowledge of Flutter and Provider.
32 changes: 32 additions & 0 deletions src/routes/docs/tutorials/flutter/step-2/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
layout: tutorial
title: Create app
description: Create a Flutter app project using Appwrite.
step: 2
---

## Create Flutter project {% #create-flutter-project %}

Create a Flutter app with the `flutter create` command.

```sh
flutter create ideas_tracker
cd ideas_tracker
```

## Add dependencies {% #add-dependencies %}

Install the Flutter Appwrite SDK and provider.

```sh
flutter pub add appwrite:17.0.0
flutter pub add provider
```

For iOS, make sure you have CocoaPods installed. Then install the pods to complete the installation:

```
cd ios
pod install
cd ..
```
57 changes: 57 additions & 0 deletions src/routes/docs/tutorials/flutter/step-3/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
layout: tutorial
title: Set up Appwrite
description: Import and initialize Appwrite for your Flutter application.
step: 3
---

## Create project {% #create-project %}

Head to the [Appwrite Console](https://cloud.appwrite.io/console).

{% only_dark %}
![Create project screen](/images/docs/quick-starts/dark/create-project.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/quick-starts/create-project.png)
{% /only_light %}

If this is your first time using Appwrite, create an account and create your first project.

Then, under **Add a platform**, add a Flutter platform (Android/iOS/Linux etc.) with the package/bundle ID `com.example.ideas_tracker`.

{% only_dark %}
![Add a platform](/images/docs/quick-starts/dark/add-platform.png)
{% /only_dark %}
{% only_light %}
![Add a platform](/images/docs/quick-starts/add-platform.png)
{% /only_light %}

You can skip optional steps.

## Initialize Appwrite SDK {% #init-sdk %}

To use Appwrite in our Flutter app, you'll need to find our project ID.
Find your project's ID in the **Settings** page.

{% only_dark %}
![Project settings screen](/images/docs/quick-starts/dark/project-id.png)
{% /only_dark %}
{% only_light %}
![Project settings screen](/images/docs/quick-starts/project-id.png)
{% /only_light %}

Create a new file `lib/appwrite.dart` to hold our Appwrite related code.
Only one instance of the `Client()` should be created per app.
Add the following code to it, replacing `<PROJECT_ID>` with your project ID.

```dart
import 'package:appwrite/appwrite.dart';

final client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.setProject("<PROJECT_ID>");

final account = Account(client);
final db = Databases(client);
```
249 changes: 249 additions & 0 deletions src/routes/docs/tutorials/flutter/step-4/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
---
layout: tutorial
title: Add authentication
description: Add authentication to your Flutter application.
step: 4
---

## User context {% #user-context %}

In Flutter, you can use [provider](https://pub.dev/packages/provider) for state management.

Create a new file `lib/providers/user_provider.dart` and add the following code to it.

```dart
import 'package:appwrite/appwrite.dart';
import 'package:appwrite/models.dart' as models;
import 'package:flutter/material.dart';
import 'package:ideas_tracker/appwrite.dart';

class UserProvider extends ChangeNotifier {
models.User? _current;

models.User? get current => _current;

UserProvider() {
init();
}

Future<void> login(String email, String password) async {
try {
await account.createEmailPasswordSession(
email: email,
password: password,
);
_current = await account.get();
notifyListeners();
debugPrint('Welcome back. You are logged in');
} catch (e) {
rethrow;
}
}

Future<void> logout() async {
try {
await account.deleteSession(sessionId: 'current');
_current = null;
notifyListeners();
debugPrint("Logged out");
} catch(e) {
rethrow;
}
}

Future<void> register(String email, String password) async {
try {
await account.create(userId: ID.unique(), email: email, password: password);
await login(email, password);
notifyListeners();
debugPrint("Account created");
} catch (e) {
rethrow;
}
}

Future<void> init() async {
try {
_current = await account.get();
notifyListeners();
} catch (e) {
_current = null;
notifyListeners();
}
}
}
```

Add the `UserProvider` to `main.dart` to make it accessible throughout the App.

```dart
import 'package:flutter/material.dart';
import 'package:ideas_tracker/providers/user_provider.dart';
import 'package:ideas_tracker/screens/login.dart';
import 'package:provider/provider.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserProvider()),
],
child: const MyApp(),
)
);
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Ideas Tracker",
debugShowCheckedModeBanner: false,
home: Login()
);
}
}
```

Now, you can use `UserProvider` to access the user's data inside any Widget.

## Styling {% #styling %}

To maintain DRY principles, we will move all styling constants to `lib/styles.dart`. Defining these as static class members allows for consistent, reusable widget styling across the entire app.

```dart
import 'package:flutter/material.dart';

class Styles {
static TextStyle heading = TextStyle(fontSize: 24, fontWeight: FontWeight.w600);

static InputDecoration input = InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
borderRadius: BorderRadius.circular(22),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.pink, width: 2),
borderRadius: BorderRadius.circular(22),
),
);

static ButtonStyle button = ElevatedButton.styleFrom(
backgroundColor: Colors.pinkAccent,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 24),
);

static ButtonStyle disabledButton = ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 24),
);
}
```

## Login page {% #login-page %}

Create a new file `lib/screens/login.dart` and add the following code to it.
this page contains a basic form to allow the user to login or register.
Notice how this page utilizes the `UserProvider` to perform login and register actions.

```dart
import 'package:flutter/material.dart';
import 'package:ideas_tracker/providers/user_provider.dart';
import 'package:ideas_tracker/styles.dart';
import 'package:provider/provider.dart';

class Login extends StatefulWidget {

const Login({super.key});

@override
State<Login> createState() => _LoginState();
}

class _LoginState extends State<Login> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();

@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Login or Register", style: Styles.heading),
SizedBox(height: 40,),
TextField(
controller: _emailController,
decoration: Styles.input.copyWith(
hintText: "Email"
),
),
SizedBox(height: 25),
TextField (
controller: _passwordController,
obscureText: true,
decoration: Styles.input.copyWith(
hintText: "Password"
),
),
Comment thread
blueberry-adii marked this conversation as resolved.
SizedBox(height: 25),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
try {
await context.read<UserProvider>().login(
_emailController.text,
_passwordController.text,
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
}
Comment thread
blueberry-adii marked this conversation as resolved.
},
Comment thread
greptile-apps[bot] marked this conversation as resolved.
style: Styles.button,
child: Text("Login")
),
SizedBox(width: 24),
ElevatedButton(
onPressed: () async {
try {
await context.read<UserProvider>().register(
_emailController.text,
_passwordController.text,
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
}
},
style: Styles.button,
child: Text("Register"),
),
],
)
]),
),
)
);
}
}
```
Loading