Hi i found it quite surprising when I found not much information about this topic on stackoverflow and the internet.
I try to build a Photo Gallery similar to this: Go Gallery Gif (source https://www.youtube.com/watch?v=yVS56cLJDgU)
The user can take pictures and I only want to show the pictures in the gallery which were taken with my App.
So here is what I did:
This is the Entity which will be stored to the objectbox-database.
PhotoEntity.dart
@Entity()
class PhotoEntity {
int id;
String description;
String photoPath;
@Property(type: PropertyType.date)
@Index()
DateTime date;
PhotoEntity(
{this.id = 0,
this.description = 'no description',
required this.photoPath,
required this.date});
ImageProvider getImageProvider() {
return FileImage(File(photoPath));
}
}
Here is a helper class to communicate with the objectbox-databse
DatabaseController.dart
/*
* Singleton for Database
*
* NOTE: Before using the DatabaseController wait for its initialization with:
* await DatabaseController().initializationDone;
*/
class DatabaseController {
// create Singleton instance DatabaseController
static final DatabaseController _controller = DatabaseController._internal();
// variable for waiting on initialization
late Future _initialized;
Future get initializationDone => _initialized;
Store? _store;
late Box<PhotoEntity> boxPhotoEntity;
Query<PhotoEntity>? _allPhotoEntitiesQuery;
Stream<Query<PhotoEntity>>? _allPhotoEntitiesQueryWatched;
DatabaseController._internal() {
_initialized = create();
}
factory DatabaseController() {
return _controller;
}
Future<void> create() async {
_store = await openStore();
boxPhotoEntity = _store!.box<PhotoEntity>();
_allPhotoEntitiesQuery = boxPhotoEntity.query().build();
_allPhotoEntitiesQueryWatched = boxPhotoEntity.query().watch().asBroadcastStream();
}
void close() {
_store?.close();
_store = null;
}
void addOrUpdatePhotoEntity(PhotoEntity photoEntity) {
_storeNullCheck();
boxPhotoEntity .put(photoEntity);
}
List<PhotoEntity> getAllPhotoEntities() {
_storeNullCheck();
return _allPhotoEntitiesQuery!.find();
}
Stream<Query<PhotoEntity>> watchPhotoEntities() {
_storeNullCheck();
return _allPhotoEntitiesQueryWatched!;
}
Query<PhotoEntity> getAllPhotoEntitiesQuery() {
_storeNullCheck();
return _allPhotoEntitiesQuery!;
}
void _storeNullCheck() {
if (_store == null) {
if (kDebugMode) {
print(
'ERROR - you should call DatabaseController.create() before using it OR you shouldnt use the controller after closing it!');
}
}
}
}
Once the user has taken an Image it gets stored to the ApplicationDocumentsDirectory and a PhotoEntity with the correspoing path gets stored in the objectbox-database.
For displaying the Gallery I use a StreamBuilder in order to get notified and update the Gallery accordingly once changes were made to the database.
late Stream<Query<PhotoEntity>> queryStream;
@override
void initState() {
queryStream = DatabaseController().watchPhotoEntities();
super.initState();
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
initialData: DatabaseController().getAllPhotoEntitiesQuery(),
stream: queryStream,
builder: (context, AsyncSnapshot<Query<PhotoEntity>> snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error ${snapshot.error}'));
}
if (!snapshot.hasData) {
return const Center(child: Text('No data'));
}
return const GalleryGridView();
},
);
}
The GalleryGridView looks like this (and here is the problem):
@override
Widget build(BuildContext context) {
List<PhotoEntity> photoEntities = DatabaseController().getAllPhotoEntities();
return GridView.builder(
itemBuilder: (BuildContext context, int index) {
return Image(image: photoEntities[index].getImageProvider());
},
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
);
}
DatabaseController().getAllPhotoEntities() which returns every single PhotoEntity in the database takes obviously way too much memory and time to return.
So what are my possibilities to implement the desired behaviour? Some thoughts I had about this:
- With objectbox I can get all the PhotoEntities as a stream. But how do I turn a stream into a GridView?
- objectbox offers to set an offset and a limit before calling find(). How would I implement a Widget that makes use of that in order to achieve pagination?
It would be very kind if someone would take the time and give me some advise. I have the feeling that I'm missing something very obvious because I would assume this is a very common usecase.