diff --git a/demos/supabase-todolist/lib/attachments/camera_helpers.dart b/demos/supabase-todolist/lib/attachments/camera_helpers.dart index eea9aa8c..a9528fd5 100644 --- a/demos/supabase-todolist/lib/attachments/camera_helpers.dart +++ b/demos/supabase-todolist/lib/attachments/camera_helpers.dart @@ -1,15 +1,20 @@ import 'package:camera/camera.dart'; import 'package:flutter/widgets.dart'; - -late final CameraDescription? camera; +import 'package:powersync_flutter_demo/powersync.dart'; Future setupCamera() async { // Ensure that plugin services are initialized so that `availableCameras()` // can be called before `runApp()` WidgetsFlutterBinding.ensureInitialized(); // Obtain a list of the available cameras on the device. - final cameras = await availableCameras(); - // Get a specific camera from the list of available cameras. - final camera = cameras.isNotEmpty ? cameras.first : null; - return camera; + try { + final cameras = await availableCameras(); + // Get a specific camera from the list of available cameras. + final camera = cameras.isNotEmpty ? cameras.first : null; + return camera; + } catch (e) { + // Camera is not supported on all platforms + log.warning('Failed to setup camera: $e'); + return null; + } } diff --git a/demos/supabase-todolist/lib/attachments/photo_capture_widget.dart b/demos/supabase-todolist/lib/attachments/photo_capture_widget.dart index 7141215c..a4bad5a9 100644 --- a/demos/supabase-todolist/lib/attachments/photo_capture_widget.dart +++ b/demos/supabase-todolist/lib/attachments/photo_capture_widget.dart @@ -3,15 +3,16 @@ import 'dart:async'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:powersync/powersync.dart' as powersync; -import 'package:powersync_flutter_demo/attachments/camera_helpers.dart'; import 'package:powersync_flutter_demo/attachments/queue.dart'; import 'package:powersync_flutter_demo/models/todo_item.dart'; import 'package:powersync_flutter_demo/powersync.dart'; class TakePhotoWidget extends StatefulWidget { final String todoId; + final CameraDescription camera; - const TakePhotoWidget({super.key, required this.todoId}); + const TakePhotoWidget( + {super.key, required this.todoId, required this.camera}); @override State createState() { @@ -28,7 +29,7 @@ class _TakePhotoWidgetState extends State { super.initState(); _cameraController = CameraController( - camera!, + widget.camera, ResolutionPreset.medium, ); diff --git a/demos/supabase-todolist/lib/attachments/photo_widget.dart b/demos/supabase-todolist/lib/attachments/photo_widget.dart index cd91b0d9..afa523ef 100644 --- a/demos/supabase-todolist/lib/attachments/photo_widget.dart +++ b/demos/supabase-todolist/lib/attachments/photo_widget.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:powersync_flutter_demo/attachments/camera_helpers.dart'; import 'package:powersync_flutter_demo/attachments/photo_capture_widget.dart'; import 'package:powersync_flutter_demo/attachments/queue.dart'; @@ -19,31 +20,58 @@ class PhotoWidget extends StatefulWidget { } } +class _ResolvedPhotoState { + String? photoPath; + bool fileExists; + + _ResolvedPhotoState({required this.photoPath, required this.fileExists}); +} + class _PhotoWidgetState extends State { late String photoPath; - Future> _getPhoto(photoId) async { + Future<_ResolvedPhotoState> _getPhotoState(photoId) async { if (photoId == null) { - return {"photoPath": null, "fileExists": false}; + return _ResolvedPhotoState(photoPath: null, fileExists: false); } photoPath = await attachmentQueue.getLocalUri('$photoId.jpg'); bool fileExists = await File(photoPath).exists(); - return {"photoPath": photoPath, "fileExists": fileExists}; + return _ResolvedPhotoState(photoPath: photoPath, fileExists: fileExists); } @override Widget build(BuildContext context) { return FutureBuilder( - future: _getPhoto(widget.todo.photoId), - builder: (BuildContext context, AsyncSnapshot snapshot) { + future: _getPhotoState(widget.todo.photoId), + builder: (BuildContext context, + AsyncSnapshot<_ResolvedPhotoState> snapshot) { + if (snapshot.data == null) { + return Container(); + } + final data = snapshot.data!; Widget takePhotoButton = ElevatedButton( - onPressed: () { + onPressed: () async { + final camera = await setupCamera(); + if (!mounted) return; + + if (camera == null) { + const snackBar = SnackBar( + content: Text('No camera available'), + backgroundColor: + Colors.red, // Optional: to highlight it's an error + ); + + ScaffoldMessenger.of(context).showSnackBar(snackBar); + return; + } + Navigator.push( context, MaterialPageRoute( - builder: (context) => TakePhotoWidget(todoId: widget.todo.id), + builder: (context) => + TakePhotoWidget(todoId: widget.todo.id, camera: camera), ), ); }, @@ -54,29 +82,25 @@ class _PhotoWidgetState extends State { return takePhotoButton; } - if (snapshot.hasData) { - String filePath = snapshot.data['photoPath']; - bool fileIsDownloading = !snapshot.data['fileExists']; - - if (fileIsDownloading) { - return const Text("Downloading..."); - } - - File imageFile = File(filePath); - int lastModified = imageFile.existsSync() - ? imageFile.lastModifiedSync().millisecondsSinceEpoch - : 0; - Key key = ObjectKey('$filePath:$lastModified'); - - return Image.file( - key: key, - imageFile, - width: 50, - height: 50, - ); + String? filePath = data.photoPath; + bool fileIsDownloading = !data.fileExists; + + if (fileIsDownloading) { + return const Text("Downloading..."); } - return takePhotoButton; + File imageFile = File(filePath!); + int lastModified = imageFile.existsSync() + ? imageFile.lastModifiedSync().millisecondsSinceEpoch + : 0; + Key key = ObjectKey('$filePath:$lastModified'); + + return Image.file( + key: key, + imageFile, + width: 50, + height: 50, + ); }); } } diff --git a/demos/supabase-todolist/lib/main.dart b/demos/supabase-todolist/lib/main.dart index 55e3c33b..63339c69 100644 --- a/demos/supabase-todolist/lib/main.dart +++ b/demos/supabase-todolist/lib/main.dart @@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:powersync_flutter_demo/app_config.dart'; -import 'package:powersync_flutter_demo/attachments/camera_helpers.dart'; import 'package:powersync_flutter_demo/attachments/queue.dart'; import 'package:powersync_flutter_demo/models/schema.dart'; @@ -38,8 +37,6 @@ void main() async { initializeAttachmentQueue(db); } - camera = await setupCamera(); - final loggedIn = isLoggedIn(); runApp(MyApp(loggedIn: loggedIn)); }