Flutter: Add Array to Firestore with a Button - arrays

I am working on developing an app and am currently in the account creation part. I want users to tap on buttons, and the text would be added to a Firestore database that I have created.
Currently, my problem is that I am not sure how to go about setting that up. I have repositories and blocs created for other components of the account creation, but I am struggling to figure out how to create buttons that will store the text as an array in Firestore. I have attached an image of what my screen will look like, my Firestore screen, as well as the code of my database repository and the chunk of code I have thus far with creating buttons that will store the text. The code for my button does NOT work, so I was hoping that I could get some help :):
Database Repository:
class DatabaseRepository extends BaseDatabaseRepository {
final FirebaseFirestore _firebaseFirestore = FirebaseFirestore.instance;
#override
Stream<User> getUser(String userId) {
return _firebaseFirestore
.collection('Users')
.doc(userId)
.snapshots()
.map((snap) => User.fromSnapshot(snap));
}
#override
Stream <List<User>> getUsers(
String userId
) {
return _firebaseFirestore
.collection('Users').snapshots().map((snap) {
return snap.docs.map((doc) => User.fromSnapshot(doc)).toList();
});
}
#override
Future<void> updateUserPictures(User user, String imageName) async{
String downloadURL = await StorageRepository().getDownloadURL(user, imageName);
return _firebaseFirestore
.collection('Users')
.doc(user.id)
.update({
'imageUrls': FieldValue.arrayUnion([downloadURL])});
}
#override
Future<void> UpdateUser(User user) async{
return _firebaseFirestore
.collection('Users')
.doc(user.id)
.update(user.toMap())
.then((value) =>
print('User document updated.'));
}
#override
Future<void> createUser(User user) async{
await _firebaseFirestore
.collection('Users')
.doc(user.id)
.set(user.toMap());
}
}
Button code (thus far):
Widget _button (event) => TextButton(
onPressed: () async {
DocumentReference documentReference = FirebaseFirestore.instance.collection('Users').doc();
DocumentSnapshot doc = await documentReference.get();
List Interests = doc.data('Interests');
if (Interests.contains(event == true) ) {}
},
child: Text(event,
style: GoogleFonts.montserrat()));

Related

How do I create my second Data Table in This Database?

I have one data table named "visits" and its working fine. Now I am trying to create another data table name "bookings". But I can not do it. Is there any one who can help me to create the new data table? This my my DBHelper Class code:
DB Helper Class:
======================
import 'package:mrhandyman_admin/models/visit_list_model.dart';
import 'package:sqflite/sqflite.dart';
class DBHelper {
static Database? _db;
static final int _version = 1;
static final String _tableName="visits";
static Future<void> initDb() async {
if (_db != null) {
return;
}
try {
String _path = await getDatabasesPath() + 'visits.db';
_db = await openDatabase(
_path,
version: _version,
onCreate: (db, version) async {
print("Creating a new entry");
return await db.execute(
"CREATE TABLE $_tableName("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"customerName STRING, customerPhone STRING,"
"moveFromAddress STRING, moveToAddress STRING,"
"visitDate STRING, visitTime STRING, remind INTEGER,"
"isCompleted INTEGER)",
);
},
);
} catch (e) {
print(e);
}
}
static Future<int> insert(VisitListModel? visitListModel) async {
print("insert function called");
return await _db?.insert(_tableName, visitListModel!.toJson())??1;
}
static Future<List<Map<String, dynamic>>> query() async {
print("query function called");
return await _db!.query(_tableName);
}
static delete(VisitListModel visitListModel) async {
return await _db!.delete(_tableName, where: 'id=?', whereArgs: [visitListModel.id]);
}
static update(int id) async{
return await _db!.rawUpdate('''
UPDATE visits
SET isCompleted = ?
WHERE id =?
''', [1, id]
);
}
}
Visit Controller
import 'package:get/get.dart';
import 'package:mrhandyman_admin/database/db_helper.dart';
import 'package:mrhandyman_admin/models/visit_list_model.dart';
class VisitListController extends GetxController{
#override
void onReady() {
super.onReady();
}
var visitList = <VisitListModel>[].obs;
Future<int> addVisit({VisitListModel? visitListModel}) async {
return await DBHelper.insert(visitListModel);
}
//get all data from table
void getVisits() async {
List<Map<String, dynamic>> visits = await DBHelper.query();
visitList.assignAll(visits.map((data) => new VisitListModel.fromJson(data)).toList());
}
void delete(VisitListModel visitListModel) async {
await DBHelper.delete(visitListModel);
getVisits();
}
void markTaskCompleted(int id) async {
await DBHelper.update(id);
getVisits();
}
}
Hello, I have one data table named "visits" and its working fine. Now I am trying to create another data table name "bookings". But I can not do it. Is there any one who can help me to create the new data table? This my my DBHelper Class code:

LateInitializationError - lifecyle of flutter

I am learning Flutter and right now testing an app to use SQLite. I wanted to use firebase in the long run, so I am building codes to store the token to the DB, but I think that I didn't quite get the process.
The LateInitializationError happens, but then it also does print the token so I guess I messed up the lifecycle?
So my question is,
why does the error happen and then print out the token?
how can I store the tokenValue data into the DB?
been 2 months and still not getting in the hang of flutter...
+Edit
This is the Error Message after putting an empty value
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building Builder:
Class 'Future<dynamic>' has no instance method 'insertToken'.
Receiver: Instance of 'Future<dynamic>'
Tried calling: insertToken(Instance of 'Token')
The relevant error-causing widget was:
MaterialApp file:///C:/Users/katej/Desktop/firem6_demo/lib/main.dart:17:5
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 _MyAppState.initState (package:firem6_demo/main.dart:49:15)
#2 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4711:57)
#3 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4548:5)
... Normal element mounting (166 frames)
...
====================================================================================================
I/flutter ( 9579): (token)
I/flutter ( 9579): []
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building Builder:
Class 'Future<dynamic>' has no instance method 'insertToken'.
Receiver: Instance of 'Future<dynamic>'
Tried calling: insertToken(Instance of 'Token')
The relevant error-causing widget was:
MaterialApp file:///C:/Users/katej/Desktop/firem6_demo/lib/main.dart:17:5
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 _MyAppState.initState (package:firem6_demo/main.dart:49:15)
#2 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4711:57)
#3 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4548:5)
... Normal element mounting (4 frames)
...
This is the main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MaterialApp(home: MyApp()),
);
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late FirebaseMessaging messaging;
late String tokenValue;
void copyToken() {
(() {
if (tokenValue != null) {
Clipboard.setData(ClipboardData(text: tokenValue));}
}());
}
#override
void initState() {
messaging = FirebaseMessaging.instance;
messaging.getToken().then((value) {
tokenValue = value!;
copyToken();
print(tokenValue);
});
tokenDb();
var user1 = Token(token: tokenValue);
tokenDb().insertToken(user1);
tokenDb().updateToken(user1);
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "DemoApp",
home: BottomBarScreen(),
routes: {
MonitoringScreen.routeName: (context) => MonitoringScreen(),
GuideScreen.routeName: (context) => GuideScreen(),
PracticeScreen.routeName: (context) => PracticeScreen(),
SettingsScreen.routeName: (context) => SettingsScreen()
},
);
}
}
And this is the db_test.dart which has tokenDb() function in it.
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
tokenDb() async {
final database = openDatabase(
join(await getDatabasesPath(), 'token_list.db'),
onCreate: (db, version) {
return db.execute(
"CREATE TABLE tokens (token INTEGER PRIMARY KEY)",
);
},
version: 1,
);
Future<void> insertToken(Token token) async {
final Database db = await database;
await db.insert(
'tokens',
token.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<Token>> tokens() async {
final Database db = await database;
final List<Map<String, dynamic>> maps = await db.query('tokens');
return List.generate(maps.length, (i) {
return Token(
token: maps[i]['token']
);
});
}
Future<void> updateToken(Token token) async {
final db = await database;
await db.update(
'tokens',
token.toMap(),
where: "id = ?",
whereArgs: [token.token],
);
}
Future<void> deleteToken(int id) async {
final db = await database;
await db.delete(
'tokens',
where: "id = ?",
whereArgs: [id],
);
}
print(await tokens());
}
class Token {
String? token;
Token({this.token});
Map<String, dynamic> toMap() {
final map = Map<String, dynamic>();
map['token'] = token;
return map;
}
factory Token.fromMap(Map<String, dynamic> map) {
return Token(token: map['token']);
}
}
Inside the Main initState(), the tokenDb().insertToken(user1) does not work; the insertToken is not pointing to the function in the tokenDb.
thank you!
From
late String tokenValue;
To
String tokenValue = '';
Would resolve it.

How to display image from AWS s3 using spring boot and react?

How to display images from amazon s3 bucket in react?
Images are not being displayed using amazon s3,spring boot 2.5.3,react.
I have tested the end points in postman, it works.
React: Using Axios to connect to backend and MyDropZone
Using axios to make a post request, I can confirm that images are being saved in s3.
This is the reponse after making a post request, for the first 2 user.
[
{
"userId":"565c6cbe-7833-482d-b0d4-2edcd7fc6163",
"userName":"John",
"imageUrl":"pexels-photo-3147528.jpeg"
},
{
"userId":"3c776990-38e8-4de4-b7c6-7875c0ebb20f",
"userName":"Anthony",
"imageUrl":"pexels-photo-3147528.jpeg"
},
{
"userId":"bcac9cf2-5508-4996-953e-b18afe866581",
"userName":"Peter",
"imageUrl":null
}
]
React:
import './App.css';
import axios from 'axios';
import { useState, useEffect,useCallback } from 'react';
import {useDropzone} from 'react-dropzone'
//
const UserProfiles = () => {
const [userProfiles,setUserProfiles]=useState([])
const fetchUserProfiles=() => {
axios.get('http://localhost:5000/api/v1/users').then((response) => {
console.log(response.data)
setUserProfiles(response.data)
})
}
useEffect(() => {
fetchUserProfiles();
}, [])
return userProfiles.map((profile,index) => {
return (
<div key={index}>
<MyDropZone userId={profile.userId}></MyDropZone>
<h3>{profile.userId}</h3>
{
profile.userId ? (<img src={`http://localhost:5000/api/v1/users/${profile.userId}/image/download`} /> ) : <h5>No profile Image Uploaded</h5>
}
</div>
);
})
}
function MyDropZone({userId}) {
const onDrop = useCallback(acceptedFiles => {
// Do something with the files
console.log(acceptedFiles[0])
const file=acceptedFiles[0]
//Form-data
const formData = new FormData()
formData.append('file', file)
//Make a post req
axios.post(`http://localhost:5000/api/v1/users/${userId}/image/upload`, formData, {
headers: {
'Content-Type':'multipart/form-data'
}
}).then((response) => {
console.log(response)
console.log("Uploaded")
}).catch((error) => {
console.log(error)
})
}, [])
const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
{
isDragActive ?
<p>Drop the files here ...</p> :
<p>Drag 'n' drop some files here, or click to select files</p>
}
</div>
)
}
function App() {
return (
<div className="App">
<UserProfiles ></UserProfiles>
</div>
);
}
export default App;
The image is not being loaded in the UI.
{
profile.userId ? (<img src={`http://localhost:5000/api/v1/users/${profile.userId}/image/download`} /> ) : <h5>No profile Image Uploaded</h5>
}
When I go to this http://localhost:5000/api/v1/users/565c6cbe-7833-482d-b0d4-2edcd7fc6163/image/download URL via browser.
It has a response with
ÿØÿàJFIFHHÿâICC_PROFILElcmsmntrRGB XYZ Ü)9acspAPPLöÖÓ-lcms descü^cprt\wtpthbkpt|rXYZgXYZ¤bXYZ¸rTRCÌ#gTRCÌ#b
Update Added Backend code.
controller
#GetMapping(path = "{userId}/image/download")
public byte[] downloadUserProfileImage(#PathVariable("userId") UUID userId) {
return userProfileService.downloadUserProfileImage(userId);
}
Service:
private UserProfile getUserProfileOrThrow(UUID userId) {
UserProfile userProfile = userProfileRepository.getUserProfiles()
.stream()
.filter(profile -> profile.getUserId().equals(userId)).findFirst().orElseThrow(() -> new IllegalStateException("User does not exist" + userId)
);
return userProfile;
}
public byte[] downloadUserProfileImage(UUID userId) {
UserProfile userProfile=getUserProfileOrThrow(userId);
String path = String.format("%s/%s",
BucketName.PROFILE_IMAGE.getBucketName(),
userProfile.getUserId());
return userProfile.getImageUrl()
.map(key -> fileStore.download(path, key))
.orElse(new byte[0]);
}
FileStore:
#Service
public class FileStore {
private final AmazonS3 s3;
#Autowired
public FileStore(AmazonS3 s3) {
this.s3 = s3;
}
public void save(String path,
String fileName,
Optional<Map<String, String>> optionalMetadata,
InputStream inputStream) {
ObjectMetadata metadata = new ObjectMetadata();
optionalMetadata.ifPresent(map -> {
if (!map.isEmpty()) {
map.forEach(metadata::addUserMetadata);
}
});
try {
s3.putObject(path, fileName, inputStream, metadata);
} catch (AmazonServiceException e) {
throw new IllegalStateException("Failed to store file to s3", e);
}
}
public byte[] download(String path, String key) {
try {
S3Object object = s3.getObject(path, key);
return IOUtils.toByteArray(object.getObjectContent());
} catch (AmazonServiceException | IOException e) {
throw new IllegalStateException("Failed to download file to s3", e);
}
}
}
Amazon s3 config:
#Configuration
public class AmazonConfig {
#Bean
public AmazonS3 s3(){
AWSCredentials awsCredentials=new BasicAWSCredentials("my-credentials","my-secret-key");
return AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(Regions.AP_SOUTH_1)
.build();
}
}
UserProfile:
public class UserProfile {
private final UUID userId;
private final String userName;
private String imageUrl;
//This might be null
public Optional<String> getImageUrl() {
return Optional.ofNullable(imageUrl);
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
//getters & setters
}
When I was facing the same issue, I had to return the object.getObjectContent() image in a Base64 format.
Afterwards, when displaying the data in the front-end, you can try and to this:
<img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
You can try this Base64 decoder to see if your Base64 data is correct or not.
That implies that you make the GET call beforehand, save the result and then display the base64 string in the img src
UPDATE:
Depending on your approach, in order to download the images for each user profile, inside the .map, you can have a function that downloads the picture for each profile.
const fetchUserProfileImage = async (userProfileId) => {
return axios.get(`http://localhost:5000/api/v1/users/${profile.userId}/image/download`)
}
return userProfiles.map(async (profile,index) => {
const userProfileImageBase64 = await fetchUserProfileImage(profile.userId)
return (
<div key={index}>
<MyDropZone userId={profile.userId}></MyDropZone>
<h3>{profile.userId}</h3>
{
profile.userId ? (<img src={`data:image/png;base64, ${userProfileImageBase64}`}/> ) : <h5>No profile Image Uploaded</h5>
}
</div>
);
})
Or if you don't like to wait inside the .map, you can try to download all of the images before rendering the body and mapping them to the already existing user in the userProfiles state.
Or, the best approach imo is to add another profileImageSrc field to the User class and save it there whenever you upload an image in the backend. Then you don't have to make extra calls and just consume the data received when fetching the userProfiles
The simplest trick is to transform your byte arrays into an actual image
#GetMapping(path ="/download/{fileName}")
public ResponseEntity<ByteArrayResource> downloadFile(#PathVariable String fileName) {
byte[] data = service.downloadImage(fileName);
ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity
.ok()
.contentLength(data.length)
.header("Content-type", "image/jpeg")
.header("Content-disposition", "attachment; filename=\"" + fileName + "\"")
.body(resource);
}
This will return an actual image whether the image was jpg or png it will not matter. You can actually view the image in postman and your frontend can pick it up directly.

"Warning database has been locked" warning with SQFlite and code stops. Why can I not query the table?

I'm having trouble querying a SQFlite db table in Flutter. I get the following warning several times:
I/chatty (32047): uid=10160(com.example.SQFLite_test) 1.ui identical 18498 lines 2
I/flutter (32047): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction
object for database operations during a transaction
I get the warning when calling the getClients() method to get all clients from the Client table. The main issue though is that it seems to freeze the code as well. Even when I only try to select the top 100, it still gives me the warning and it freezes and doesn't progress.
My db class helping me to init and manage the database:
class LogServiceTwo {
LogServiceTwo._();
static final LogServiceTwo logRepo = LogServiceTwo._();
static Database _database;
Future<Database> get database async {
if (_database != null) {
return _database;
}
var db = await openDb();
// if (db == null) {
// _database = await initDB();
// return _database;
// }
var hasClientTableB = await hasClientTable(db);
if (hasClientTableB) {
_database = db;
return _database;
}
// var backup = await restoreBackup(db);
// if (backup != null) {
// _database = backup;
// return _database;
// }
await createClientTable(db);
_database = db;
return _database;
}
Future createClientTable(Database db) async {
await db.execute("CREATE TABLE Client ("
"id INTEGER PRIMARY KEY,"
"first_name TEXT,"
"last_name TEXT,"
"blocked BIT"
")");
}
Future<bool> hasClientTable(Database db) async {
try {
var table = await db.query("Client");
return table != null;
} catch (e) {
return false;
}
}
Future<Database> openDb() async {
try {
var path = await getPersistentDbPath();
var db = await openDatabase(path, version: 1);
return db;
} catch (e) {
return null;
}
}
Future initDB() async {
var path = await getPersistentDbPath();
return await openDatabase(path, version: 1, onOpen: (db) {}, onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE Client ("
"id INTEGER PRIMARY KEY,"
"first_name TEXT,"
"last_name TEXT,"
"blocked BIT"
")");
});
}
Future newClient(Client newClient) async {
final db = await database;
var res = await db.insert("Client", newClient.toMap());
return res;
}
Future newClients(List<Client> clients) async {
var clientMaps = clients.map((client) => client.toMap()).toList();
final db = await database;
clientMaps.forEach((clientMap) async {
await db.insert("Client", clientMap);
});
}
Future<Client> getClient(int id) async {
final db = await database;
var res = await db.query("Client", where: "id = ?", whereArgs: [id]);
return res.isNotEmpty ? Client.fromMap(res.first) : Null;
}
Future<List<Client>> getAllClients() async {
final db = await database;
var res = await db.query("Client");
List<Client> list = res.isNotEmpty ? res.map((c) => Client.fromMap(c)).toList() : [];
return list;
}
Future<List<Client>> getBlockedClients() async {
final db = await logRepo.database;
var res = await db.rawQuery("SELECT * FROM Client WHERE blocked=1");
List<Client> list = res.isNotEmpty ? res.toList().map((c) => Client.fromMap(c)) : null;
return list;
}
Future<List<String>> getTables() async {
var db = await logRepo.database;
var tableNames = (await db.query('sqlite_master', where: 'type = ?', whereArgs: ['table'])).map((row) => row['name'] as String).toList(growable: false);
return tableNames;
}
Future<String> getPersistentDbPath() async {
return await createPersistentDbDirecotry();
}
Future createPersistentDbDirecotry() async {
var externalDirectoryPath = await ExtStorage.getExternalStorageDirectory();
var persistentDirectory = "$externalDirectoryPath/db_persistent";
await createDirectory(persistentDirectory);
return "$persistentDirectory/persistent.db";
}
Future createDirectory(String path) async {
await (new Directory(path).create());
}
Future<bool> askForWritePermission() async {
var status = await Permission.storage.status;
if (!status.isGranted) {
status = await Permission.storage.request();
return status.isGranted;
}
return status.isGranted;
}
Future mockData() async {
var clients = ClientMocker.createClients();
await newClients(clients);
}
Future deleteAll() async {
var db = await database;
await db.rawDelete("DELETE FROM Client");
}
// Get all clients, throws warnings and stops proceeding in the code.
Future getClients() async {
try {
var db = await database;
return await db.rawQuery("SELECT * FROM Client");
} catch (e) {
print(e);
}
}
}
My main class calling the database service for testing purposes:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
// This makes the visual density adapt to the platform that you run
// the app on. For desktop platforms, the controls will be smaller and
// closer together (more dense) than on mobile platforms.
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
// firstTest();
// secondTest();
thirdTest();
testText = "";
super.initState();
}
String testText;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Text(testText),
)
],
),
),
);
}
Future firstTest() async {
testText = "";
// var client = new Client(blocked: false, firstName: "Tobias", lastName: "Eliasson", id: null);
// await LogRepository.logRepo.newClient(client);
// await LogRepository.logRepo.newClient(client);
// await LogRepository.logRepo.newClient(client);
var clients = await LogRepository.logRepo.getAllClients();
clients.forEach((c) {
setState(() {
testText += "\n${c.toMap()}";
});
print(c.toMap());
});
setState(() {
testText += "Length of found clients: ${clients.length.toString()}";
});
var success = await LogRepository.logRepo.saveBackup();
print(success);
}
Future secondTest() async {
try {
await LogRepository.logRepo.deleteAll();
await LogRepository.logRepo.mockData();
var a = DateTime.now();
print("Saving backup $a");
var backupSuccess = await LogRepository.logRepo.saveBackup();
print("Backup success: $backupSuccess");
var b = DateTime.now();
print("Saved backup:${a.difference(b)}");
} catch (e) {
print("Error!!!");
print(e);
}
}
Future thirdTest() async {
await LogServiceTwo.logRepo.database;
await LogServiceTwo.logRepo.mockData();
var clients = await LogServiceTwo.logRepo.getClients();
print(clients.length);
}
}
As far as I can see, I await all db operations and only use on db object to access it so there shouldn't be any weird parallell access going on. Maybe you find an error somewhere I'm missing though. In case you wonder why I create the database in external memory, is that the database needs to be persistent and saved when uninstalling the app or updating it.
Thanks!
I found the problem and learnt a lesson. In short, follow conventions and best practices when accessing a database. The problem was that I inserted one client at the time inside the method:
Future newClients(List<Client> clients) async {
var clientMaps = clients.map((client) => client.toMap()).toList();
final db = await database;
clientMaps.forEach((clientMap) async {
await db.insert("Client", clientMap);
});
}
When inserting or doing many db operations at the same moment, use batch and commit instead like this:
Future newClients(List<Client> clients) async {
var clientMaps = clients.map((client) => client.toMap()).toList();
final db = await database;
var batch = db.batch();
clientMaps.forEach((clientMap) async {
batch.insert("Client", clientMap);
});
await batch.commit(noResult: true);
}
With the batch and commit solution I could insert 90 000 clients on first try with no errors and could query them all after.

FS access api & React: duplicate entries

I'm trying to build a movie dashboard (something like Plex); the user selects a folder and all the movies he has in the folder show up. The use of the new File System Access API allows me to create file handlers on the fly and be able to display movies using the browser video player.
The problem I'm facing is with duplicated entries, for instance "Ghostbusters" (can't really understand why, but that's the only one causing the issue)
This is the basic implementation of the file system:
try {
const folderHandle = await window.showDirectoryPicker();
const addedFilms = [];
history.push('/list');
// const entries = await folderHandle.values()
const entries = await folderHandle.values();
for await (const entry of entries) {
const movie = await readMoviesonDisk(folderHandle, entry);
console.log(addedFilms);
if (addedFilms.includes(entry.name)) continue;
addedFilms.push(entry.name);
setMovies((movies) => [...movies, movie]);
}
} catch (error) {
alert('Alert from reading files: ' + error);
}
setMovies just sets a Context with a movies array and readMoviesOnDisk is the following:
const readMoviesonDisk = async (folderHandle, entry) => {
if (entry.kind === 'file' && entry.name.endsWith('.mp4')) {
const path = await folderHandle.resolve(entry);
const handle = await folderHandle.getFileHandle(path);
const movie = await getMovie(entry.name);
if (movie) {
return { ...movie.data, file: handle, name: entry.name };
}
const movieData = await searchMovie(entry.name);
if (movieData) {
const actualData = await getMovieDetails(movieData.id);
if (actualData !== undefined) {
await insertMovie(entry.name, actualData, handle);
} else {
await insertMovie(entry.name, actualData, handle);
}
return { ...actualData, file: handle, name: entry.name };
}
return { name: entry.name, file: handle };
}
};
searchMovie and insertMovie only interact with IndexedDB to store movie info for offline use. getMovieDetails does API calls to TMDB to get movie info.
The key I use for displaying the movies is their TMDB id. Ghostbusters' id is "620".
Can anyone help me?
Without additional background it seems impossible to answer this properly. Can you iterate over all files in the folder and just log the names and kinds? This should work and show no duplicate entries.
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.kind, entry.name);
}

Resources