I want to use sqflite in my app. To do this, I'm trying to follow this tutorial: https://flutter.dev/docs/cookbook/persistence/sqlite. However, I don't know where to place the code inside my application. In the tutorial, the code seems to be placed at the main() function - but, if I do that, how can I call the insert, update and delete methods at other files?
Update:
As suggested by #Madhavam Shahi, I created a file databaseServices.dart. Now, at the other file, I'm importing databaseServices.dart and trying to use it as below:
import 'databaseServices.dart';
DataBaseServices db=DataBaseServices();
db.delete() //example
However, it is not working. I think the databaseServices.dart is not structured the right way, but I can't spot the error. I know I must be making a very newbie mistake. Here is the code for the databaseServices.dart:
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'counter.dart';
class DatabaseServices {
void whatever() async {
// Open the database and store the reference.
final Future<Database> database = openDatabase(
// Set the path to the database.
join(await getDatabasesPath(), 'counter_database.db'),
// When the database is first created, create a table to store counters;
onCreate: (db, version) {
// Run the CREATE TABLE statement on the database.
return db.execute(
"CREATE TABLE counters(id INTEGER PRIMARY KEY, name TEXT, value INTEGER)",
);
},
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
version: 1,
);
// Define a function that inserts counters into the database.
Future<void> insertCounter(Counter counter) async {
// Get a reference to the database.
final Database db = await database;
// Insert the Counter into the correct table. Here, if a counter is inserted twice,
// it replace any previous data.
await db.insert(
'counters',
counter.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// A method that retrieves all the counters from the counters table.
Future<List<Counter>> counters() async {
// Get a reference to the database.
final Database db = await database;
// Query the table for all the Counters.
final List<Map<String, dynamic>> maps = await db.query('counters');
// Counvert the List<Map<String, dynamic>> into a List<Counter>
return List.generate(maps.length, (i) {
return Counter(
id: maps[i]['id'],
name: maps[i]['name'],
value: maps[i]['value'],
);
});
}
// Method to update a Counter in the database
Future<void> updateCounter(Counter counter) async {
final db = await database;
await db.update(
'counters',
counter.toMap(),
where: "id = ?",
whereArgs: [counter.id],
);
}
//Delete a Counter from the database
Future<void> deleteCounter(int id) async {
final db = await database;
await db.delete(
'counters',
where: "id = ?",
whereArgs: [id],
);
}
}
}
No, it doesn't matter wherever you create the database.
You can create a file databaseServices.dart which manages the database service. It'll be easy for you to manage your code then.
In the cookbook, they are just showing an example, how you can use sqlflite.
But, however you should place this line WidgetsFlutterBinding.ensureInitialized(); in your main() method before anything, IF you are doing an asynchronous task in the main() method.
Update:
To perform CRUD in other files,
import your databaseServices.dart file that file, in which you want to perform the CRUD.
import 'databaseServices.dart';
DataBaseServices db=DataBaseServices();// create an object (DataBaseServices is the name of the class)
//Now, you can access all the methods,
db.delete()//example
Alternatively, if you don't want to create a class in the databaseServices.dart file, and want to keep every function a top level function, then you can do the following.
import 'databaseServices.dart' as db;
//Now, you can access all the top level functions or variables.
db.delete()//example.
Update 2:-
To make database accessible to every function,
move Future database outside the whatever () method, and place it just below the class name. (Making it global so that every function can access it), notice i removed the "final" keyword, because we are going to initialise it later, in the whatever method. Now, in the whatever method do what you were, but instead of final Future database = //yoir code, do this, database =//your code.. by doing this, you will be initialising the database variable, and as database variable is a global variable(declared outside any function, inside a class), any function can access it. But you have to keep in mind that, database has to be initialised before you call any other method which requires database, because if you will not call whatever () method before any other function, then the database won't be initialised, and hence your other functions won't work.
Example,
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'counter.dart';
class DatabaseServices {
Future<Database> database;//making database global so that every function inside the class can access it.
void whatever() async {
// Open the database and store the reference.
database = openDatabase(
// Set the path to the database.
join(await getDatabasesPath(), 'counter_database.db'),
// When the database is first created, create a table to store counters;
onCreate: (db, version) {
// Run the CREATE TABLE statement on the database.
return db.execute(
"CREATE TABLE counters(id INTEGER PRIMARY KEY, name TEXT, value INTEGER)",
);
},
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
version: 1,
);
}//Function whatever () ends here
// Define a function that inserts counters into the database.
Future<void> insertCounter(Counter counter) async {
// Get a reference to the database.
final Database db = await database;
// Insert the Counter into the correct table. Here, if a counter is inserted twice,
// it replace any previous data.
await db.insert(
'counters',
counter.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// A method that retrieves all the counters from the counters table.
Future<List<Counter>> counters() async {
// Get a reference to the database.
final Database db = await database;
// Query the table for all the Counters.
final List<Map<String, dynamic>> maps = await db.query('counters');
// Counvert the List<Map<String, dynamic>> into a List<Counter>
return List.generate(maps.length, (i) {
return Counter(
id: maps[i]['id'],
name: maps[i]['name'],
value: maps[i]['value'],
);
});
}
// Method to update a Counter in the database
Future<void> updateCounter(Counter counter) async {
final db = await database;
await db.update(
'counters',
counter.toMap(),
where: "id = ?",
whereArgs: [counter.id],
);
}
//Delete a Counter from the database
Future<void> deleteCounter(int id) async {
final db = await database;
await db.delete(
'counters',
where: "id = ?",
whereArgs: [id],
);
}
}
Now, since there are no nested functions, you can easily create an object of the class, and call the functions as you need easily :)
I subscribe to a stream with the listen method so I can process the data as it comes in. If I found what I am looking for, I want to cancel the stream and return the data from the stream subscription block.
I have a working implementation but I am using a Completer to block the function from returning immediate and it feels messy, so I want to know if there's a better way to achieve this. Here is my code snippet:
Future<String> _extractInfo(String url) async {
var data = <int>[];
var client = http.Client();
var request = http.Request('GET', Uri.parse(url));
var response = await client.send(request);
var process = Completer();
var interestingData = '';
StreamSubscription watch;
watch = response.stream.listen((value) async {
data.clear(); //clear the previous stale data from the list so searching can be faster
data.addAll(value);
if (
hasInterestingData(data) //search the bytelist for the data I'm looking for
){
interestingData = extractInterestingData(data) //extract the data if it's been found. This is what I want my function to return;
await watch.cancel(); //cancel the stream subscription if the data has been found. ie: Cancel the download
process.complete('done'); //complete the future, so the function can return interestingData.
}
});
watch.onDone(() {
//complete the future if the data was not found...so the function will return an empty string
if (!process.isCompleted) {
process.complete('done');
}
});
await process.future; //blocks the sync ops below so the function doesn't return immediately
client.close();
return interestingData;
}
You should use await for for things like this when you are in an async function.
Future<String> _extractInfo(String url) async {
var client = http.Client();
var request = http.Request('GET', Uri.parse(url));
var response = await client.send(request);
await for (var data in response.stream) {
if (hasInterestingData(data)) {
return extractInterestingData(data); // Exiting loop cancels the subscription.
}
}
return "done";
}
However, while this approach is equivalent to your code, it's probably equally flawed.
Unless you are looking for a single byte, you risk the thing you are looking for being split between consecutive data events. You like do need to retain some part of the previous data array, or some state summarizing what you have already seen, but how much depends on what you are actually looking for.
I'm using async / await methods in C#... my operations are to create databases on Azure Cosmos DB, the problem is when the code is runnnig, for any reason... the flow brakes without any issues apparently
can anyone help?
Task p= Run();
public async static Task Run()
{
.
.
.
await CreateDatabase(client)
}
private async static Task CreateDatabase(DocumentClient client)
{
var databaseDefinition = new Database { Id = "MyDB" };
var result = await client.CreateDatabaseAsync(databaseDefinition);
var database = result.Resource;
}
//In second line, the code brake when the debugging is operating
//==> var result = await client.CreateDatabaseAsync(databaseDefinition);
I want to use JSON as a local database for my firebase data ,I don't want offline data store.I am using path_provider to store my data on the phone
So I tried storing data with
file.writeAsStringSync(json.encode(snapshot.toString()))
And it worked the stored data file looked like a JSON file
"{-key:{name:name,age:age},-key:{name:name,age:age}}"
The " " where missing and I can't decode it.
So what can be done here ?
(Edit:Does Firestore provide anything that can help me with this ?)
You can use the shared_preferences package to store JSON,
or if you want to write to a custom file use the path_provider package to get paths to directories where you app has permissions to write to.
You might also need to use the async versions of the dart:io API to access files in Flutter (not sure about that tough)
"{-key:{name:name,age:age},-key:{name:name,age:age}}" is not valid JSON
Either use
import 'dart:convert';
...
var myJson = {'-key':{'name:name','age:age'},'-key':{'name:name','age:age'}}";
var myJsonString = jsonEncode(myJson);
await file.writeAsString(myJsonString);
or
var myJsonString = '{"-key":{"name:name","age:age"},"-key":{"name:name","age:age"}}';
await file.writeAsString(myJsonString);
Create a JSON File
Future<String> get _localPath async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
Future<File> get _dbFile async {
final path = await _localPath;
return new File("$path/demo.json");
}
Write data into a file
Future<File> write(DataSnapshot snapshot,String chatId) async {
final path = await _dbFile;
final String key = snapshot.key;
final String name = snapshot.value['senderUid'];
final int age= snapshot.value['receivedUid'];
String content = '{"$key":{"name":"$name","age":"$age"}}';
return file.writeAsStringSync(content);
}
Read Data
Future<Null> read() async {
try {
final file = await _dbFile;
String content = file.readAsStringSync();
Map<String, dynamic> chatMap=json.decode(content);
chatMap.keys.forEach((E){debugPrint(E);});
} catch (e) {
debugPrint('Error : '+e.toString());
}
}
My calls to a Google API from an API Controller in IIS express hang indefinitely when called sequentially using async await.
var id = CreateDocument("My Title").Result;
async Task<string> CreateDocument(string title)
{
var file = new GData.File { Title = title };
// Stepping over this line in the debugger never returns in IIS Express.
file = await Service.Files.Insert(file).ExecuteAsync();
return file.Id;
}
It does not hang calling the same method from a test console app.
The same logic also does not hang IIS Express when called using the corresponding synchronous method instead.
var id = CreateDocument("My Title");
string CreateDocument(string title)
{
var file = new GData.File { Title = title };
// This has no problem
file = Service.Files.Insert(file).Execute();
return file.Id;
}
Where should I look for the defect?
The defect is here:
var id = CreateDocument("My Title").Result;
As I explain on my blog, you should not block on async code.
Instead of Result, use await:
var id = await CreateDocument("My Title");