Skip to content

Commit 3f4cdd4

Browse files
committed
Adopt riverpod in drift demo
1 parent 264f012 commit 3f4cdd4

28 files changed

+1957
-697
lines changed

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,25 @@ import 'dart:async';
22

33
import 'package:camera/camera.dart';
44
import 'package:flutter/material.dart';
5+
import 'package:hooks_riverpod/hooks_riverpod.dart';
56
import 'package:powersync/powersync.dart' as powersync;
67
import 'package:supabase_todolist_drift/attachments/queue.dart';
78
import 'package:supabase_todolist_drift/powersync.dart';
89

9-
class TakePhotoWidget extends StatefulWidget {
10+
class TakePhotoWidget extends ConsumerStatefulWidget {
1011
final String todoId;
1112
final CameraDescription camera;
1213

1314
const TakePhotoWidget(
1415
{super.key, required this.todoId, required this.camera});
1516

1617
@override
17-
State<StatefulWidget> createState() {
18+
ConsumerState<TakePhotoWidget> createState() {
1819
return _TakePhotoWidgetState();
1920
}
2021
}
2122

22-
class _TakePhotoWidgetState extends State<TakePhotoWidget> {
23+
class _TakePhotoWidgetState extends ConsumerState<TakePhotoWidget> {
2324
late CameraController _cameraController;
2425
late Future<void> _initializeControllerFuture;
2526

@@ -50,14 +51,15 @@ class _TakePhotoWidgetState extends State<TakePhotoWidget> {
5051
final XFile photo = await _cameraController.takePicture();
5152
// copy photo to new directory with ID as name
5253
String photoId = powersync.uuid.v4();
53-
String storageDirectory = await attachmentQueue.getStorageDirectory();
54-
await attachmentQueue.localStorage
54+
final queue = await ref.read(attachmentQueueProvider.future);
55+
String storageDirectory = await queue.getStorageDirectory();
56+
await queue.localStorage
5557
.copyFile(photo.path, '$storageDirectory/$photoId.jpg');
5658

5759
int photoSize = await photo.length();
5860

59-
await appDb.addTodoPhoto(widget.todoId, photoId);
60-
await attachmentQueue.saveFile(photoId, photoSize);
61+
await ref.read(driftDatabase).addTodoPhoto(widget.todoId, photoId);
62+
await queue.saveFile(photoId, photoSize);
6163
} catch (e) {
6264
log.info('Error taking photo: $e');
6365
}
Lines changed: 91 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,92 @@
11
import 'dart:io';
22

33
import 'package:flutter/material.dart';
4+
import 'package:flutter_riverpod/flutter_riverpod.dart';
45
import 'package:powersync_attachments_helper/powersync_attachments_helper.dart';
6+
import 'package:riverpod_annotation/riverpod_annotation.dart';
57
import 'package:supabase_todolist_drift/attachments/camera_helpers.dart';
68
import 'package:supabase_todolist_drift/attachments/photo_capture_widget.dart';
79
import 'package:supabase_todolist_drift/attachments/queue.dart';
810
import 'package:supabase_todolist_drift/database.dart';
911

10-
class PhotoWidget extends StatefulWidget {
12+
part 'photo_widget.g.dart';
13+
14+
final class PhotoWidget extends ConsumerWidget {
1115
final TodoItem todo;
1216

13-
PhotoWidget({
14-
required this.todo,
15-
}) : super(key: ObjectKey(todo.id));
17+
const PhotoWidget({super.key, required this.todo});
1618

1719
@override
18-
State<StatefulWidget> createState() {
19-
return _PhotoWidgetState();
20+
Widget build(BuildContext context, WidgetRef ref) {
21+
final photoState = ref.watch(_getPhotoStateProvider(todo.photoId));
22+
if (!photoState.hasValue) {
23+
return Container();
24+
}
25+
26+
final data = photoState.value;
27+
Widget takePhotoButton = ElevatedButton(
28+
onPressed: () async {
29+
final camera = await setupCamera();
30+
if (!context.mounted) return;
31+
32+
if (camera == null) {
33+
const snackBar = SnackBar(
34+
content: Text('No camera available'),
35+
backgroundColor: Colors.red, // Optional: to highlight it's an error
36+
);
37+
38+
ScaffoldMessenger.of(context).showSnackBar(snackBar);
39+
return;
40+
}
41+
42+
Navigator.push(
43+
context,
44+
MaterialPageRoute(
45+
builder: (context) =>
46+
TakePhotoWidget(todoId: todo.id, camera: camera),
47+
),
48+
);
49+
},
50+
child: const Text('Take Photo'),
51+
);
52+
53+
if (data == null) {
54+
return takePhotoButton;
55+
}
56+
57+
String? filePath = data.photoPath;
58+
bool fileIsDownloading = !data.fileExists;
59+
bool fileArchived =
60+
data.attachment?.state == AttachmentState.archived.index;
61+
62+
if (fileArchived) {
63+
return Column(
64+
crossAxisAlignment: CrossAxisAlignment.center,
65+
mainAxisAlignment: MainAxisAlignment.center,
66+
children: [
67+
const Text("Unavailable"),
68+
const SizedBox(height: 8),
69+
takePhotoButton
70+
],
71+
);
72+
}
73+
74+
if (fileIsDownloading) {
75+
return const Text("Downloading...");
76+
}
77+
78+
File imageFile = File(filePath!);
79+
int lastModified = imageFile.existsSync()
80+
? imageFile.lastModifiedSync().millisecondsSinceEpoch
81+
: 0;
82+
Key key = ObjectKey('$filePath:$lastModified');
83+
84+
return Image.file(
85+
key: key,
86+
imageFile,
87+
width: 50,
88+
height: 50,
89+
);
2090
}
2191
}
2292

@@ -29,104 +99,25 @@ class _ResolvedPhotoState {
2999
{required this.photoPath, required this.fileExists, this.attachment});
30100
}
31101

32-
class _PhotoWidgetState extends State<PhotoWidget> {
33-
late String photoPath;
34-
35-
Future<_ResolvedPhotoState> _getPhotoState(photoId) async {
36-
if (photoId == null) {
37-
return _ResolvedPhotoState(photoPath: null, fileExists: false);
38-
}
39-
photoPath = await attachmentQueue.getLocalUri('$photoId.jpg');
40-
41-
bool fileExists = await File(photoPath).exists();
102+
@riverpod
103+
Future<_ResolvedPhotoState> _getPhotoState(Ref ref, String? photoId) async {
104+
if (photoId == null) {
105+
return _ResolvedPhotoState(photoPath: null, fileExists: false);
106+
}
107+
final queue = await ref.read(attachmentQueueProvider.future);
108+
final photoPath = await queue.getLocalUri('$photoId.jpg');
42109

43-
final row = await attachmentQueue.db
44-
.getOptional('SELECT * FROM attachments_queue WHERE id = ?', [photoId]);
110+
bool fileExists = await File(photoPath).exists();
45111

46-
if (row != null) {
47-
Attachment attachment = Attachment.fromRow(row);
48-
return _ResolvedPhotoState(
49-
photoPath: photoPath, fileExists: fileExists, attachment: attachment);
50-
}
112+
final row = await queue.db
113+
.getOptional('SELECT * FROM attachments_queue WHERE id = ?', [photoId]);
51114

115+
if (row != null) {
116+
Attachment attachment = Attachment.fromRow(row);
52117
return _ResolvedPhotoState(
53-
photoPath: photoPath, fileExists: fileExists, attachment: null);
118+
photoPath: photoPath, fileExists: fileExists, attachment: attachment);
54119
}
55120

56-
@override
57-
Widget build(BuildContext context) {
58-
return FutureBuilder(
59-
future: _getPhotoState(widget.todo.photoId),
60-
builder: (BuildContext context,
61-
AsyncSnapshot<_ResolvedPhotoState> snapshot) {
62-
if (snapshot.data == null) {
63-
return Container();
64-
}
65-
final data = snapshot.data!;
66-
Widget takePhotoButton = ElevatedButton(
67-
onPressed: () async {
68-
final camera = await setupCamera();
69-
if (!context.mounted) return;
70-
71-
if (camera == null) {
72-
const snackBar = SnackBar(
73-
content: Text('No camera available'),
74-
backgroundColor:
75-
Colors.red, // Optional: to highlight it's an error
76-
);
77-
78-
ScaffoldMessenger.of(context).showSnackBar(snackBar);
79-
return;
80-
}
81-
82-
Navigator.push(
83-
context,
84-
MaterialPageRoute(
85-
builder: (context) =>
86-
TakePhotoWidget(todoId: widget.todo.id, camera: camera),
87-
),
88-
);
89-
},
90-
child: const Text('Take Photo'),
91-
);
92-
93-
if (widget.todo.photoId == null) {
94-
return takePhotoButton;
95-
}
96-
97-
String? filePath = data.photoPath;
98-
bool fileIsDownloading = !data.fileExists;
99-
bool fileArchived =
100-
data.attachment?.state == AttachmentState.archived.index;
101-
102-
if (fileArchived) {
103-
return Column(
104-
crossAxisAlignment: CrossAxisAlignment.center,
105-
mainAxisAlignment: MainAxisAlignment.center,
106-
children: [
107-
const Text("Unavailable"),
108-
const SizedBox(height: 8),
109-
takePhotoButton
110-
],
111-
);
112-
}
113-
114-
if (fileIsDownloading) {
115-
return const Text("Downloading...");
116-
}
117-
118-
File imageFile = File(filePath!);
119-
int lastModified = imageFile.existsSync()
120-
? imageFile.lastModifiedSync().millisecondsSinceEpoch
121-
: 0;
122-
Key key = ObjectKey('$filePath:$lastModified');
123-
124-
return Image.file(
125-
key: key,
126-
imageFile,
127-
width: 50,
128-
height: 50,
129-
);
130-
});
131-
}
121+
return _ResolvedPhotoState(
122+
photoPath: photoPath, fileExists: fileExists, attachment: null);
132123
}

0 commit comments

Comments
 (0)