Skip to content

Commit 2c0d365

Browse files
authored
Merge pull request #259 from powersync-ja/drift-demo-with-riverpod
Drift demo with riverpod
2 parents ddbd53a + 786d2ba commit 2c0d365

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+3480
-1768
lines changed

demos/supabase-todolist-drift/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
*.swp
66
.DS_Store
77
.atom/
8+
.build/
89
.buildlog/
910
.history
1011
.svn/
12+
.swiftpm/
1113
migrate_working_dir/
1214

1315
# IntelliJ related

demos/supabase-todolist-drift/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# PowerSync + Supabase + Drift Flutter Demo: Todo List App
22

33
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.
4+
This demo also uses [riverpod](https://riverpod.dev) to highlight best practices for state management.
45

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

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

5455
- `dart run build_runner build` generates all the required code once.
55-
- `dart run build_runner build --delete-conflicting-outputs` deletes previously generated files and generates the required code once.
56+
- `dart run build_runner build -d` deletes previously generated files and generates the required code once.
5657
- `dart run build_runner watch` watches for changes in your sources and generates code with incremental rebuilds. This is better for development.
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
include: package:flutter_lints/flutter.yaml
22
analyzer:
33
exclude: [lib/**.g.dart]
4+
5+
linter:
6+
rules:
7+
- prefer_relative_imports
+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
-- Create tables
2+
create table
3+
public.lists (
4+
id uuid not null default gen_random_uuid (),
5+
created_at timestamp with time zone not null default now(),
6+
name text not null,
7+
owner_id uuid not null,
8+
constraint lists_pkey primary key (id),
9+
constraint lists_owner_id_fkey foreign key (owner_id) references auth.users (id) on delete cascade
10+
) tablespace pg_default;
11+
12+
create table
13+
public.todos (
14+
id uuid not null default gen_random_uuid (),
15+
created_at timestamp with time zone not null default now(),
16+
completed_at timestamp with time zone null,
17+
description text not null,
18+
completed boolean not null default false,
19+
created_by uuid null,
20+
completed_by uuid null,
21+
list_id uuid not null,
22+
photo_id uuid null,
23+
constraint todos_pkey primary key (id),
24+
constraint todos_created_by_fkey foreign key (created_by) references auth.users (id) on delete set null,
25+
constraint todos_completed_by_fkey foreign key (completed_by) references auth.users (id) on delete set null,
26+
constraint todos_list_id_fkey foreign key (list_id) references lists (id) on delete cascade
27+
) tablespace pg_default;
28+
29+
-- Create publication for powersync
30+
create publication powersync for table lists, todos;
31+
32+
-- Set up Row Level Security (RLS)
33+
-- See https://supabase.com/docs/guides/auth/row-level-security for more details.
34+
alter table public.lists
35+
enable row level security;
36+
37+
alter table public.todos
38+
enable row level security;
39+
40+
create policy "owned lists" on public.lists for ALL using (
41+
auth.uid() = owner_id
42+
);
43+
44+
create policy "todos in owned lists" on public.todos for ALL using (
45+
auth.uid() IN (
46+
SELECT lists.owner_id FROM lists WHERE (lists.id = todos.list_id)
47+
)
48+
);
49+
50+
-- This trigger automatically creates some sample data when a user registers.
51+
-- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details.
52+
create function public.handle_new_user_sample_data()
53+
returns trigger as $$
54+
declare
55+
new_list_id uuid;
56+
begin
57+
insert into public.lists (name, owner_id)
58+
values ('Shopping list', new.id)
59+
returning id into new_list_id;
60+
61+
insert into public.todos(description, list_id, created_by)
62+
values ('Bread', new_list_id, new.id);
63+
64+
insert into public.todos(description, list_id, created_by)
65+
values ('Apples', new_list_id, new.id);
66+
67+
return new;
68+
end;
69+
$$ language plpgsql security definer;
70+
71+
create trigger new_user_sample_data after insert on auth.users for each row execute procedure public.handle_new_user_sample_data();

demos/supabase-todolist-drift/lib/attachments/camera_helpers.dart

-20
This file was deleted.

demos/supabase-todolist-drift/lib/attachments/photo_widget.dart

-132
This file was deleted.

demos/supabase-todolist-drift/lib/widgets/status_app_bar.dart renamed to demos/supabase-todolist-drift/lib/components/app_bar.dart

+16-34
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,32 @@
1-
import 'dart:async';
2-
1+
import 'package:auto_route/auto_route.dart';
32
import 'package:flutter/foundation.dart';
43
import 'package:flutter/material.dart';
4+
import 'package:flutter_riverpod/flutter_riverpod.dart';
55
import 'package:powersync/powersync.dart';
6-
import 'package:supabase_todolist_drift/widgets/fts_search_delegate.dart';
7-
import '../powersync.dart';
8-
9-
class StatusAppBar extends StatefulWidget implements PreferredSizeWidget {
10-
const StatusAppBar({super.key, required this.title});
116

12-
final String title;
7+
import '../powersync/powersync.dart';
8+
import '../screens/search.dart';
139

14-
@override
15-
State<StatusAppBar> createState() => _StatusAppBarState();
10+
final appBar = AppBar(
11+
title: const Text('PowerSync Flutter Demo'),
12+
);
1613

17-
@override
18-
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
19-
}
14+
final class StatusAppBar extends ConsumerWidget implements PreferredSizeWidget {
15+
final Widget title;
2016

21-
class _StatusAppBarState extends State<StatusAppBar> {
22-
late SyncStatus _connectionState;
23-
StreamSubscription<SyncStatus>? _syncStatusSubscription;
24-
25-
@override
26-
void initState() {
27-
super.initState();
28-
_connectionState = db.currentStatus;
29-
_syncStatusSubscription = db.statusStream.listen((event) {
30-
setState(() {
31-
_connectionState = db.currentStatus;
32-
});
33-
});
34-
}
17+
const StatusAppBar({super.key, required this.title});
3518

3619
@override
37-
void dispose() {
38-
super.dispose();
39-
_syncStatusSubscription?.cancel();
40-
}
20+
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
4121

4222
@override
43-
Widget build(BuildContext context) {
44-
final statusIcon = _getStatusIcon(_connectionState);
23+
Widget build(BuildContext context, WidgetRef ref) {
24+
final syncState = ref.watch(syncStatus);
25+
final statusIcon = _getStatusIcon(syncState);
4526

4627
return AppBar(
47-
title: Text(widget.title),
28+
leading: const AutoLeadingButton(),
29+
title: title,
4830
actions: <Widget>[
4931
IconButton(
5032
onPressed: () {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'package:auto_route/auto_route.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:hooks_riverpod/hooks_riverpod.dart';
4+
5+
import '../navigation.gr.dart';
6+
import '../supabase.dart';
7+
import 'app_bar.dart';
8+
9+
final class PageLayout extends ConsumerWidget {
10+
final Widget content;
11+
final Widget? title;
12+
final Widget? floatingActionButton;
13+
final bool showDrawer;
14+
15+
const PageLayout({
16+
super.key,
17+
required this.content,
18+
this.title,
19+
this.floatingActionButton,
20+
this.showDrawer = true,
21+
});
22+
23+
@override
24+
Widget build(BuildContext context, WidgetRef ref) {
25+
return Scaffold(
26+
appBar: StatusAppBar(
27+
title: title ?? const Text('PowerSync Demo'),
28+
),
29+
body: Center(child: content),
30+
floatingActionButton: floatingActionButton,
31+
drawer: showDrawer
32+
? Drawer(
33+
// Add a ListView to the drawer. This ensures the user can scroll
34+
// through the options in the drawer if there isn't enough vertical
35+
// space to fit everything.
36+
child: ListView(
37+
// Important: Remove any padding from the ListView.
38+
padding: EdgeInsets.zero,
39+
children: [
40+
const DrawerHeader(
41+
decoration: BoxDecoration(
42+
color: Colors.blue,
43+
),
44+
child: Text(''),
45+
),
46+
ListTile(
47+
title: const Text('SQL Console'),
48+
onTap: () {
49+
final route = context.topRoute;
50+
if (route.name != SqlConsoleRoute.name) {
51+
context.pushRoute(const SqlConsoleRoute());
52+
}
53+
},
54+
),
55+
ListTile(
56+
title: const Text('Sign Out'),
57+
onTap: () async {
58+
ref.read(authNotifierProvider.notifier).signOut();
59+
},
60+
),
61+
],
62+
),
63+
)
64+
: null,
65+
);
66+
}
67+
}

0 commit comments

Comments
 (0)