Getting Started with Appwrite Realtime for Flutter
Tags: flutter, news, opensource, serverless
Published: 2021-09-04 06:46:19

Realtime service is one of the most sought after features of Appwrite and it's now ready to play with! It's been a while as we already had realtime alpha release and a getting started tutorial to go with it. In this tutorial, we will dive into the details and understand how to develop a Flutter app leveraging Appwrite's realtime capabilities.

## 📝 Prerequisites

In order to continue with this tutorial, you need to have access to an Appwrite console with a project. If you have not already installed Appwrite, please do so. Installing Appwrite is really simple following Appwrite's official [installation docs](https://appwrite.io/docs/installation). Installation should only take around 2 minutes. Once installed, login to your console and **create a new Project**.

## 💾 Setup Database

Once you have logged in to the console and selected your project, from the left sidebar in the dashboard click on the **Database** option to get to the database page.

Once on the database page, click on the **Add Collection** button.

![Create Collection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/032v3z2l3piultfmrre9.png)
 

In the dialog that pops up, set the collection name to **Items** and click on the **Create** button to create the collection, and you will be redirected to the new collection's page where we can define its rules. Define the following rules, then click the **Update** button. Also note down the **Collection ID** from the right side of the settings page as we will need that later in our code.

- **Name**
    - label: Name
    - Key: name
    - Rule Type: Text
    - Required: true
    - Array: false

![Add Collection Rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l0so4ggo1mb4d8vs2eyb.png)
 

In the permissions, set the read and right permission both to `*` so that anyone can read and write.

Now that the collection is created we need to create a user. This user will be used to create sessions when we authenticate with the realtime API.

## ⚙️ Setup Flutter Project and Dependencies

We will begin by creating a new Flutter project. From your terminal in your project folder, type the following command to create a new Flutter project.

```bash
flutter create flappwrite_realtime
```

Then we add the Appwrite's SDK, to do that from your terminal, in your newly created project directory, type the following command:

```bash
cd flappwrite_realtime
flutter pub add appwrite
```

This command will add Appwrite's latest Flutter SDK with realtime service, as a dependency to your Flutter project.

Once you have installed the dependency and run `flutter pub get` you should be ready to use it.

## ➕️ Add Flutter Platforms

To initialize the Appwrite SDK and start interacting with Appwrite services, you first need to add a new Flutter platform to your project. If you are running on Flutter web, you can simply add web platform instead of Flutter platforms. To add a new platform, go to your Appwrite console, select your project, and click the **Add Platform** button on the project Dashboard. Choose either Flutter or web platform.

If you choose web, add **localhost** as the host name. If you choose Flutter, from the dialog, choose one of the tabs, based on which platform you plan to run on. You can add multiple platforms similarly.

If you choose to add a Android platform, on the dialog box add the details. Add your app name and package name. Your package name is generally the `applicationId` in your app-level `build.gradle`
file. You may also find your package name in your `AndroidManifest.xml` file.

![Add Flutter Platform](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0p73qo6b1im45xq0f80.png)
 

By registering a new platform, you are allowing your app to communicate with the Appwrite API.

## 👩‍🔧 Home Page

We will start by creating a simple stateful widget that will list all the items from our items collection, and also allow adding new items as well as deleting existing items. Our Home page will also connect to Appwrite's realtime service and display changes in the items collection by updating the UI as they happen. So, let's create our **HomePage** widget. Modify the code in **lib/main.dart** as follows:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlAppwrite Realtime Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<Map<String, dynamic>> items = [];
  TextEditingController _nameController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FlAppwrite Realtime Demo'),
      ),
      body: ListView(children: [
        ...items.map((item) => ListTile(
              title: Text(item['name']),
            )),
      ]),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          // dialog to add new item
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              title: Text('Add new item'),
              content: TextField(
                controller: _nameController,
              ),
              actions: [
                TextButton(
                  child: Text('Cancel'),
                  onPressed: () => Navigator.of(context).pop(),
                ),
                TextButton(
                  child: Text('Add'),
                  onPressed: () {
                    // add new item
                    final name = _nameController.text;
                    if (name.isNotEmpty) {
                      _nameController.clear();
                      _addItem(name);
                    }
                    Navigator.of(context).pop();
                  },
                ),
              ],
            ),
          );
        },
      ),
    );
  }

  void _addItem(String name) {
    setState(() {
      items.add({'name': name, 'id': DateTime.now().millisecondsSinceEpoch});
    });
  }
}

```

In the **initState** function of the HomePage, we will create and initialize our Appwrite client, as well as subscribe to realtime changes in documents in our **items** collection.

```dart
RealtimeSubscription? subscription;
late final Client client;

initState() {
    super.initState();
    client = Client()
        .setEndpoint('<http://localhost/v1>') // your endpoint
        .setProject('5df5acd0d48c2') //your project id
        ;
    subscribe();
}
```

And in **dispose** method, close the subscription.

```dart
dispose(){
    subscription?.close();
    super.dispose();
}
```

Now let us setup different variables and functions to load the initial data, listen to changes in the collection documents and update the UI to reflect the changes in realtime.

First, initialize our items collection id and and setup a function to load initial data when the application first starts. For that we will also setup Appwrite database service.

```dart
final itemsCollection = "<collectionId>"; //replace with your collection id, which can be found in your collection's settings page.

late final Database database;

@override
void initState() {
    super.initState();
    client = Client()
            .setEndpoint('<http://localhost/v1>') // your endpoint
            .setProject('5df5acd0d48c2') //your project id
        ;
    database = Database(client);
    loadItems();
}


loadItems() async {
    try {
        final res = await database.listDocuments(collectionId: itemsCollection);
        setState(() {
        items = List<Map<String, dynamic>>.from(res.data['documents']);
        });
    } on AppwriteException catch (e) {
        print(e.message);
    }
}
```

In order to be able to add data to our collection, we must first create a session. Let's add a login function and call it from our `initState` function.

```dart
@override
void initState() {
    super.initState();
    //...
    login();
    // ..
}

login() async {
    try {
      await Account(client).createAnonymousSession();
    } on AppwriteException catch (e) {
      print(e.message);
    }
}
```

Now, we will setup our subscribe function that will listen to changes to documents in our items collection.

```dart
void subscribe() {
    final realtime = Realtime(client);

    subscription = realtime.subscribe([
      'collections.<collectionId>.documents'
    ]); //replace <collectionId> with the ID of your items collection, which can be found in your collection's settings page.

    // listen to changes
    subscription!.stream.listen((data) {
      // data will consist of `event` and a `payload`
      if (data.payload.isNotEmpty) {
        switch (data.event) {
          case "database.documents.create":
            var item = data.payload;
            items.add(item);
            setState(() {});
            break;
          case "database.documents.delete":
            var item = data.payload;
            items.removeWhere((it) => it['\$id'] == item['\$id']);
            setState(() {});
            break;
          default:
            break;
        }
      }
    });
}
```

Finally, let's modify our `_addItem` function to add item to Appwrite's database and see how the view updates in realtime.

```dart
void _addItem(String name) async {
    try {
      await database.createDocument(
        collectionId: itemsCollection,
        data: {'name': name},
        read: ['*'],
        write: ['*']
      );
    } on AppwriteException catch (e) {
      print(e.message);
    }
}
```

Let us also modify our `ListTile` widget to add a delete button that will allow us to delete the item.

```dart
ListTile(
    title: Text(item['name']),
    trailing: IconButton(
        icon: Icon(Icons.delete),
        onPressed: () async {
            await database.deleteDocument(
            collectionId: itemsCollection,
            documentId: item['\$id'],
            );
        },
    ),
)
```

## Complete Example

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlAppwrite Realtime Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<Map<String, dynamic>> items = [];
  TextEditingController _nameController = TextEditingController();
  RealtimeSubscription? subscription;
  late final Client client;
  final itemsCollection = 'COLLECTION_ID';
  late final Database database;

  @override
  void initState() {
    super.initState();
    client = Client()
            .setEndpoint('<http://localhost/v1>') // your endpoint
            .setProject('YOUR_PROJECT_ID') //your project id
        ;
    database = Database(client);
    login();
    loadItems();
    subscribe();
  }

  login() async {
    try {
      await Account(client).createAnonymousSession();
    } on AppwriteException catch (e) {
      print(e.message);
    }
  }

  loadItems() async {
    try {
      final res = await database.listDocuments(collectionId: itemsCollection);
      setState(() {
        items = List<Map<String, dynamic>>.from(res.data['documents']);
      });
    } on AppwriteException catch (e) {
      print(e.message);
    }
  }

  void subscribe() {
    final realtime = Realtime(client);

    subscription = realtime.subscribe([
      'collections.<collectionId>.documents'
    ]); //replace <collectionId> with the ID of your items collection, which can be found in your collection's settings page.

    // listen to changes
    subscription!.stream.listen((data) {
      // data will consist of `event` and a `payload`
      if (data.payload.isNotEmpty) {
        switch (data.event) {
          case "database.documents.create":
            var item = data.payload;
            items.add(item);
            setState(() {});
            break;
          case "database.documents.delete":
            var item = data.payload;
            items.removeWhere((it) => it['\$id'] == item['\$id']);
            setState(() {});
            break;
          default:
            break;
        }
      }
    });
  }

  @override
  void dispose() {
    subscription?.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FlAppwrite Realtime Demo'),
      ),
      body: ListView(children: [
        ...items.map((item) => ListTile(
              title: Text(item['name']),
              trailing: IconButton(
                icon: Icon(Icons.delete),
                onPressed: () async {
                  await database.deleteDocument(
                    collectionId: itemsCollection,
                    documentId: item['\$id'],
                  );
                },
              ),
            )),
      ]),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          // dialog to add new item
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              title: Text('Add new item'),
              content: TextField(
                controller: _nameController,
              ),
              actions: [
                TextButton(
                  child: Text('Cancel'),
                  onPressed: () => Navigator.of(context).pop(),
                ),
                TextButton(
                  child: Text('Add'),
                  onPressed: () {
                    // add new item
                    final name = _nameController.text;
                    if (name.isNotEmpty) {
                      _nameController.clear();
                      _addItem(name);
                    }
                    Navigator.of(context).pop();
                  },
                ),
              ],
            ),
          );
        },
      ),
    );
  }

  void _addItem(String name) async {
    try {
      await database.createDocument(
          collectionId: itemsCollection,
          data: {'name': name},
          read: ['*'],
          write: ['*']);
    } on AppwriteException catch (e) {
      print(e.message);
    }
  }
}

```

## 🥂 Conclusion

I enjoyed a lot writing tutorial and I hope you enjoyed learning and building Flutter application with Appwrite Realtime service. The full source code for this application is available on my [GitHub repository](https://github.com/lohanidamodar/flappwrite_realtime). Feel free to get back to us if you have any queries or comments. Excited to see what the community will build with Flutter and Appwrite Realtime. 

## 🎓 Learn More

- [Getting Started With Flutter](https://appwrite.io/docs/getting-started-for-flutter)
- [Flutter Playground](https://github.com/appwrite/playground-for-flutter)
- [Appwrite Docs](https://appwrite.io/docs)