# Quickstart: Flutter
Source: https://sovaria.in/docs/quickstart-flutter

Build the notes app in Flutter: initialise Sovaria with a secure session store, switch screens on sign-in, and list and add the signed-in user's notes.

> **Warning:** **Release pending:** the Flutter/Dart package is not on pub.dev yet, so the install step won't resolve until it is. Ask for early access on [the early-access page](/early-access) and we'll share the package with your team. The code on this page matches the SDK as it will ship (see [Dart SDK](https://sovaria.in/docs/dart-reference)).

A Flutter app for iOS and Android where people sign up, sign in, add notes and see only their own.

## Before you start

Do steps 1 to 3 of the [Quickstart](https://sovaria.in/docs/quickstart) first: create a project, copy its **project URL** and **anon key** ([Quickstart](https://sovaria.in/docs/quickstart#connect)), and create the `notes` table with its row rules ([Quickstart](https://sovaria.in/docs/quickstart#table)). Every signed-in user then sees and changes only their own notes, whatever the app code does.

## 1. Create the app and install

Terminal:
```bash
flutter create notes
cd notes
flutter pub add sovaria flutter_secure_storage
```

## 2. Initialise Sovaria with a secure session store

lib/main.dart:
```dart
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:sovaria/sovaria.dart';

import 'notes_app.dart';

/// Keeps the session in the Keychain (iOS) or Keystore (Android).
class SecureStore implements SessionStorage {
  final _s = const FlutterSecureStorage();
  @override
  Future<String?> read(String key) => _s.read(key: key);
  @override
  Future<void> write(String key, String value) => _s.write(key: key, value: value);
  @override
  Future<void> delete(String key) => _s.delete(key: key);
}

late final Sovaria sovaria;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Restores the saved session (refreshing it if it's about to expire).
  sovaria = await Sovaria.initialize(
    url: 'https://<project-ref>.sovarias.in',
    anonKey: '<anon-key>',
    storage: SecureStore(),
  );
  runApp(const NotesApp());
}
```

## 3. Sign in and work with notes

lib/notes_app.dart:
```dart
import 'package:flutter/material.dart';
import 'package:sovaria/sovaria.dart';

import 'main.dart';

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

  @override
  Widget build(BuildContext context) => MaterialApp(
        home: StreamBuilder<AuthState>(
          // The first event is initialSession with the restored session (or null).
          stream: sovaria.auth.onAuthStateChange,
          builder: (context, snap) => snap.data?.session == null ? const SignInPage() : const NotesPage(),
        ),
      );
}

class SignInPage extends StatefulWidget {
  const SignInPage({super.key});
  @override
  State<SignInPage> createState() => _SignInPageState();
}

class _SignInPageState extends State<SignInPage> {
  final email = TextEditingController(), password = TextEditingController();
  String error = '';

  Future<void> run(Future<Object?> Function() action) async {
    try {
      await action();
    } on SovariaException catch (e) {
      setState(() => error = e.message);
    }
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        body: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
            TextField(controller: email, decoration: const InputDecoration(labelText: 'Email')),
            TextField(controller: password, obscureText: true, decoration: const InputDecoration(labelText: 'Password')),
            FilledButton(
              onPressed: () => run(() => sovaria.auth.signInWithPassword(email: email.text, password: password.text)),
              child: const Text('Sign in'),
            ),
            TextButton(
              onPressed: () => run(() => sovaria.auth.signUp(email: email.text, password: password.text)),
              child: const Text('Create account'),
            ),
            if (error.isNotEmpty) Text(error),
          ]),
        ),
      );
}

class NotesPage extends StatefulWidget {
  const NotesPage({super.key});
  @override
  State<NotesPage> createState() => _NotesPageState();
}

class _NotesPageState extends State<NotesPage> {
  final body = TextEditingController();
  List<Map<String, Object?>> notes = [];

  @override
  void initState() {
    super.initState();
    load();
  }

  // Rows come back as maps keyed by column name. The row rules add "where owner_id = auth.uid()".
  Future<void> load() async {
    final result = await sovaria.db.sql('select id, body from notes order by id desc');
    setState(() => notes = result.rows);
  }

  Future<void> add() async {
    await sovaria.db.sql(r'insert into notes (body) values ($1)', [body.text]);
    body.clear();
    await load();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text('Notes'), actions: [
          IconButton(icon: const Icon(Icons.logout), onPressed: () => sovaria.auth.signOut()),
        ]),
        body: Column(children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(children: [
              Expanded(child: TextField(controller: body, decoration: const InputDecoration(hintText: 'Write a note'))),
              IconButton(icon: const Icon(Icons.add), onPressed: add),
            ]),
          ),
          Expanded(
            child: ListView(children: [for (final n in notes) ListTile(title: Text('${n['body']}'))]),
          ),
        ]),
      );
}
```

- Use a raw string (`r'…'`) for SQL with `$1` so Dart doesn't read it as interpolation.
- `signUp` creates the user and signs them in, unless the project asks people to confirm their email first ([Email and password](https://sovaria.in/docs/email-and-password)).

## 4. Run it

Terminal:
```bash
flutter run
```

## Next steps

- Check who can read and change `notes` before you ship: [Tutorial: prove your table is private before you ship](https://sovaria.in/docs/tutorial-secure-a-table).
- Push new notes to other devices as they happen: [Database change events](https://sovaria.in/docs/realtime-database-changes).
- Deep links, push and attestation: [Flutter quickstart](https://sovaria.in/docs/mobile-flutter).
- Every type and method: [Dart SDK](https://sovaria.in/docs/dart-reference).
