Skip to content

Drift demo with riverpod #259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions demos/supabase-todolist-drift/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
Expand Down
3 changes: 2 additions & 1 deletion demos/supabase-todolist-drift/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# PowerSync + Supabase + Drift Flutter Demo: Todo List App

This demo app is an extension of the [Supabase Todo List App](../supabase-todolist/README.md) and showcases how to set up and use the [drift_sqlite_async](https://pub.dev/packages/drift_sqlite_async) library (Drift ORM) with PowerSync.
This demo also uses [riverpod](https://riverpod.dev) to highlight best practices for state management.

Notes about the Drift usage are [further below](#drift).

Expand Down Expand Up @@ -52,5 +53,5 @@ Insert the credentials of your new Supabase and PowerSync projects into `lib/app
The `database.g.dart` file containing the \_$AppDatabase class has to be generated if there are changes made to the `database.dart` file.

- `dart run build_runner build` generates all the required code once.
- `dart run build_runner build --delete-conflicting-outputs` deletes previously generated files and generates the required code once.
- `dart run build_runner build -d` deletes previously generated files and generates the required code once.
- `dart run build_runner watch` watches for changes in your sources and generates code with incremental rebuilds. This is better for development.
4 changes: 4 additions & 0 deletions demos/supabase-todolist-drift/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
include: package:flutter_lints/flutter.yaml
analyzer:
exclude: [lib/**.g.dart]

linter:
rules:
- prefer_relative_imports
71 changes: 71 additions & 0 deletions demos/supabase-todolist-drift/database.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
-- Create tables
create table
public.lists (
id uuid not null default gen_random_uuid (),
created_at timestamp with time zone not null default now(),
name text not null,
owner_id uuid not null,
constraint lists_pkey primary key (id),
constraint lists_owner_id_fkey foreign key (owner_id) references auth.users (id) on delete cascade
) tablespace pg_default;

create table
public.todos (
id uuid not null default gen_random_uuid (),
created_at timestamp with time zone not null default now(),
completed_at timestamp with time zone null,
description text not null,
completed boolean not null default false,
created_by uuid null,
completed_by uuid null,
list_id uuid not null,
photo_id uuid null,
constraint todos_pkey primary key (id),
constraint todos_created_by_fkey foreign key (created_by) references auth.users (id) on delete set null,
constraint todos_completed_by_fkey foreign key (completed_by) references auth.users (id) on delete set null,
constraint todos_list_id_fkey foreign key (list_id) references lists (id) on delete cascade
) tablespace pg_default;

-- Create publication for powersync
create publication powersync for table lists, todos;

-- Set up Row Level Security (RLS)
-- See https://supabase.com/docs/guides/auth/row-level-security for more details.
alter table public.lists
enable row level security;

alter table public.todos
enable row level security;

create policy "owned lists" on public.lists for ALL using (
auth.uid() = owner_id
);

create policy "todos in owned lists" on public.todos for ALL using (
auth.uid() IN (
SELECT lists.owner_id FROM lists WHERE (lists.id = todos.list_id)
)
);

-- This trigger automatically creates some sample data when a user registers.
-- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details.
create function public.handle_new_user_sample_data()
returns trigger as $$
declare
new_list_id uuid;
begin
insert into public.lists (name, owner_id)
values ('Shopping list', new.id)
returning id into new_list_id;

insert into public.todos(description, list_id, created_by)
values ('Bread', new_list_id, new.id);

insert into public.todos(description, list_id, created_by)
values ('Apples', new_list_id, new.id);

return new;
end;
$$ language plpgsql security definer;

create trigger new_user_sample_data after insert on auth.users for each row execute procedure public.handle_new_user_sample_data();
20 changes: 0 additions & 20 deletions demos/supabase-todolist-drift/lib/attachments/camera_helpers.dart

This file was deleted.

132 changes: 0 additions & 132 deletions demos/supabase-todolist-drift/lib/attachments/photo_widget.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,50 +1,32 @@
import 'dart:async';

import 'package:auto_route/auto_route.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:powersync/powersync.dart';
import 'package:supabase_todolist_drift/widgets/fts_search_delegate.dart';
import '../powersync.dart';

class StatusAppBar extends StatefulWidget implements PreferredSizeWidget {
const StatusAppBar({super.key, required this.title});

final String title;
import '../powersync/powersync.dart';
import '../screens/search.dart';

@override
State<StatusAppBar> createState() => _StatusAppBarState();
final appBar = AppBar(
title: const Text('PowerSync Flutter Demo'),
);

@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
final class StatusAppBar extends ConsumerWidget implements PreferredSizeWidget {
final Widget title;

class _StatusAppBarState extends State<StatusAppBar> {
late SyncStatus _connectionState;
StreamSubscription<SyncStatus>? _syncStatusSubscription;

@override
void initState() {
super.initState();
_connectionState = db.currentStatus;
_syncStatusSubscription = db.statusStream.listen((event) {
setState(() {
_connectionState = db.currentStatus;
});
});
}
const StatusAppBar({super.key, required this.title});

@override
void dispose() {
super.dispose();
_syncStatusSubscription?.cancel();
}
Size get preferredSize => const Size.fromHeight(kToolbarHeight);

@override
Widget build(BuildContext context) {
final statusIcon = _getStatusIcon(_connectionState);
Widget build(BuildContext context, WidgetRef ref) {
final syncState = ref.watch(syncStatus);
final statusIcon = _getStatusIcon(syncState);

return AppBar(
title: Text(widget.title),
leading: const AutoLeadingButton(),
title: title,
actions: <Widget>[
IconButton(
onPressed: () {
Expand Down
67 changes: 67 additions & 0 deletions demos/supabase-todolist-drift/lib/components/page_layout.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import '../navigation.gr.dart';
import '../supabase.dart';
import 'app_bar.dart';

final class PageLayout extends ConsumerWidget {
final Widget content;
final Widget? title;
final Widget? floatingActionButton;
final bool showDrawer;

const PageLayout({
super.key,
required this.content,
this.title,
this.floatingActionButton,
this.showDrawer = true,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: StatusAppBar(
title: title ?? const Text('PowerSync Demo'),
),
body: Center(child: content),
floatingActionButton: floatingActionButton,
drawer: showDrawer
? Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(''),
),
ListTile(
title: const Text('SQL Console'),
onTap: () {
final route = context.topRoute;
if (route.name != SqlConsoleRoute.name) {
context.pushRoute(const SqlConsoleRoute());
}
},
),
ListTile(
title: const Text('Sign Out'),
onTap: () async {
ref.read(authNotifierProvider.notifier).signOut();
},
),
],
),
)
: null,
);
}
}
Loading