How to delete local database while uninstalling the flutter windows application? - flutter

I am using the DRIFT package (formerly known as MOOR) for local database and I want to delete the database file, when uninstalling the windows application, which is saved in local machine (Windows) as db.sqlite. How can I achieve this?
Drift package for database. Drift documentation. Drift code :
// These imports are only needed to open the database
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:summa_app/database/program_location/program_location_dao.dart';
import 'package:summa_app/database/program_location/program_location_table.dart';
import 'package:summa_app/database/programs/program_dao.dart';
import 'package:summa_app/database/programs/program_table.dart';
part 'summa_database.g.dart';
#DriftDatabase(tables: [ProgramLocation, DbPrograms], daos: [ProgramLocationDao, ProgramsDao])
class SummaDatabase extends _$SummaDatabase {
// we tell the database where to store the data with this constructor
SummaDatabase() : super(_openConnection());
// you should bump this number whenever you change or add a table definition.
// Migrations are covered later in the documentation.
#override
int get schemaVersion => 1;
#override
Future<void> close() async{
await _openConnection().close();
return super.close();
}
}
LazyDatabase _openConnection() {
// the LazyDatabase util lets us find the right location for the file async.
return LazyDatabase(() async {
// put the database file, called db.sqlite here, into the documents folder
// for your app.
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(path.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase(file);
});
}

There are two options here:
You could close the database, delete the file from the device, and then re-open it.
You can delete everything from all tables, without closing the database:
Future<void> deleteEverything() {
return transaction(() async {
// you only need this if you've manually enabled foreign keys
// await customStatement('PRAGMA foreign_keys = OFF');
for (final table in allTables) {
await delete(table).go();`enter code here`
}
});
}

Related

I want to add export and import to my application. My database is hive flutter. How?

I want to add export and import database to my application. My database is hive flutter. I tried several methods but it didn't work
ElevatedButton(onPressed: () async{
final String? pathHive=Hive.box<Cart>(cartBoxName).path;
Directory dir=Directory('/storage/emulated/0/Download');
await File(pathHive!).copy('$dir/backup.hive');
}
it didn't work
you can use those methods in order to import/export a backup file for your Hive box in your flutter app:
import 'dart:io';
import 'package:hive/hive.dart';
Future<void> backupHiveBox<T>(String boxName, String backupPath) async {
final box = await Hive.openBox<T>(boxName);
final boxPath = box.path;
await box.close();
try {
File(boxPath).copy(backupPath);
} finally {
await Hive.openBox<T>(boxName);
}
}
Future<void> restoreHiveBox<T>(String boxName, String backupPath) async {
final box = await Hive.openBox<T>(boxName);
final boxPath = box.path;
await box.close();
try {
File(backupPath).copy(boxPath);
} finally {
await Hive.openBox<T>(boxName);
}
}
The concept over it, is that open the Hive box first (if it's already open then you can just get its instance ), Then, using dart:io we can export/import a File with the path of the Hive Box that we got with box.path.
And make sure that the box should be closed when you make the copying operation into/from a file, after that you can open it again.

FlutterDriver.Connect Requires VM_SERVICE_URL or String?

My app is the basic counter, with a FlutterDriver for UI Automation. My conundrum is when I attempt to run my test, it tells me that I need to specify a connection or set the VM_SERVICE_URL
ERROR:
DriverError: Could not determine URL to connect to application. Either
the VM_SERVICE_URL environment variable should be set, or an explicit
URL should be provided to the FlutterDriver.connect() method.
I've tried a few things.
Using FlutterDriver.connect();
Setting the VM_SERVICE_URL in Terminal (MacOS)
Setting the Dart Command Line to include VM_SERVICE_URL with a value
The most success I've had is with the code below. By adding enableFlutterDriverExtension to the lib/main.dart, then executing lib/main.dart, I can copy/paste the ws://127.0.0.1 connection into the test/my_test.dart. This allows me to successfully run my tests, but this isn't an ideal process.
Is there a way to pull in the connection string automatically?
Why does Platform.environment['VM_SERVICE_URL'] always return null despite my having set it?
lib/main.dart
void main() {
enableFlutterDriverExtension();
runApp(const MyApp());
}
test/main_app.dart
void main() {
// enableFlutterDriverExtension();
MainApp.main();
MyTest.main();
}
test/my_test.dart
void main() {
FlutterDriver? driver;
dynamic DartVmServiceUrl;
DartVmServiceUrl ??= Platform.environment['VM_SERVICE_URL'];
print('VM_SERVICE_URL:\t${DartVmServiceUrl}');
String vmServURL = 'ws://127.0.0.1:59488/WK8KTNVXXOo=/ws';
setUpAll( () async {
driver = await FlutterDriver.connect(dartVmServiceUrl: vmServURL);
});
tearDownAll( () {
driver?.close();
});
test('Push Button',() async {
var pushMeButton = find.byValueKey('IncrementButton');
await driver!.tap(pushMeButton);
} );
}
you have to move the files in the specific folders you see below, then try to run from terminal with
flutter drive \
--driver=test/my_test.dart \
--target=test_driver/test_driver.dart
in your lib/main.dart you don't need enableFlutterDriverExtension(); because it is already linked to your main() in the test_driver.dart
also your main in test_driver/test_driver.dart should look like this:
import 'package:{here}/main.dart' as app; // insert here your main app
import 'package:flutter_driver/driver_extension.dart';
void main() {
enableFlutterDriverExtension();
app.main();
}
your my_test.dart should look like this:
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
late FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() {
driver.close();
});
test('check flutter driver health', () async {
Health health = await driver.checkHealth();
print(health.status);
});
}
give attention to use the correct packages to avoid this error.
Error: Not found: 'dart:ui'
import 'dart:ui';

How setup dart objectbox with a local database pre-populated?

I want to set up an ObjectBox database with Flutter.
I would like to pre-populate the database file with values. When installing the application, the database file will be copied and is used by the application. I want to be able to continue to provide schema migrations. Is it possible?
How to set up this type of architecture?
Have you any example?
SOLUTION :
With #vaind response, I have implemented a database Manager like :
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import '../objectbox.g.dart';
/// Database manager manage database initialisation and configuration.
class DatabaseManager {
static DatabaseManager? _instance;
DatabaseManager._internal();
/// Singleton Factory constructor
factory DatabaseManager() {
if (_instance == null) {
_instance = DatabaseManager._internal();
}
return _instance!;
}
/// Initialize configuration of database:
/// - Init database file by copying.
/// - Open instance of database and close it.
Future<void> init() async {
await copyDatabaseFileFromAssets();
await testInstanceAndCloseIt();
}
/// Copy packaged database from the assets folder to the app directory.
Future<void> copyDatabaseFileFromAssets() async {
// Get a pre-populated DB file.
ByteData data = await rootBundle.load("assets/databases/data.mdb");
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
// Search and create DB file destination folder if not exist
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = documentsDirectory.path + "/objectbox/data.mdb";
if (!await File(path).exists()) {
var objectBoxDirectory = Directory(documentsDirectory.path + "/objectbox/");
if ((!await objectBoxDirectory.exists())) {
objectBoxDirectory.create();
}
// Copying source data into destination file.
await new File(path).writeAsBytes(bytes);
}
}
/// Open an instance of the database and close it.
Future<void> testInstanceAndCloseIt() async {
await openStore().then((Store store) {
store.close();
});
}
}
Yes, even if you want to avoid populating the database in-app (e.g. on the first run), you can bundle an existing database. Just create the database file (data.mdb) locally, even on your machine (they're compatible across platforms) and than add it to the app as a resource.
On the first app run you can then just move the data.mdb file to the application documents directory (into subdirectory objectbox) - that's where ObjectBox stores the by default if you've used the generated openStore() method. So the path to the database file should be (using package path_provider):
(await getApplicationDocumentsDirectory()).path + '/objectbox/data.mdb'. Make sure you open the store after you write the file, of course.
Update after the question has been updated. I think your copyDatabaseFileFromAssets should look more like the following. Create the database file only if it doesn't exist.
Future<void> copyDatabaseFileFromAssets() async {
// Search and create db file destination folder if not exist
final documentsDirectory = await getApplicationDocumentsDirectory();
final objectBoxDirectory = Directory(documentsDirectory.path + '/objectbox/');
if (!objectBoxDirectory.existsSync()) {
await objectBoxDirectory.create(recursive: true);
}
final dbFile = File(objectBoxDirectory.path + '/data.mdb');
if (!dbFile.existsSync()) {
// Get pre-populated db file.
ByteData data = await rootBundle.load("assets/databases/data.mdb");
// Copying source data into destination file.
await dbFile.writeAsBytes(data.buffer.asUint8List());
}
}

Unit Testing hive abstraction layer

So I created a simpler level of abstraction to use Hive into my Flutter app. This should be the central point, where all hive boxes are administrated and accessed. Since e.g. getApplicationDocumentsDirectory is not available during testing, how can I still manage to test this whole file?
import '../services/workout.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart' as path_rovider;
import 'workout.dart';
class HiveService {
static final HiveService _singleton = HiveService._internal();
static const String _workoutBox = "workoutBox";
factory HiveService() {
return _singleton;
}
HiveService._internal();
static Future<void> init() async {
final appDocumentDirectory =
await path_rovider.getApplicationDocumentsDirectory();
Hive.init(appDocumentDirectory.path);
Hive.registerAdapter(WorkoutAdapter());
}
static Future openWorkouts() {
return Hive.openBox<Workout>(_workoutBox);
}
static Future close() {
return Hive.close();
}
}
First thing first, you need to initial Hive at the top of the main method in your test file, and then you can proceed with the rest of the tests.
You can use it in this way:
void initHive() {
var path = Directory.current.path;
Hive.init(path + '/test/hive_testing_path');
}
main() {
initHive();
//The rest of your test code.
}

Saving State After App Is Closed with Provider Package FLUTTER

What's the best way to save an activity page state with Flutter provider package? Currently, using the provider package and doing a restart or closing the app the page gets rebuilt and all the new widgets are removed. The state is saved on hot reload not hot restart. For example, the post created by a user on the news feed page is removed when I close the app or restart it.
So following the comment and reading the flutter persistence cookbook, I see three common possibilites:
Persist data with SQLite
Read and write files
Store key-value data on disk
Each has different usecases, but I think the common case is to store a few objects as JSON. SQLite seems to me in simple cases an overkill and key-values do not offer enough flexibility. So I will focus on reading and writing files.
Here comes an example. I use JSON code generation libraries as described here.
The data object I read and write to disk is looks the following way:
import 'package:json_annotation/json_annotation.dart';
// json methods generated using code geneartion
// see https://flutter.dev/docs/development/data-and-backend/json#serializing-json-using-code-generation-libraries
part 'easy_data.g.dart';
#JsonSerializable()
class EasyData {
int counter;
String data;
EasyData(this.counter, this.data);
// run the following command to generate json:
// flutter pub run build_runner build
factory EasyData.fromJson(Map<String, dynamic> json) => _$EasyDataFromJson(json);
Map<String, dynamic> toJson() => _$EasyDataToJson(this);
}
The Service to load the json from disk and save looks the following way:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import '../model/easy_data.dart';
class FileStorage with ChangeNotifier {
EasyData loadedData;
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/app_data.json');
}
loadData() async {
try {
final file = await _localFile;
// Read the file
String contents = await file.readAsString();
if (contents != null) {
// parse json if file was saved before
loadedData = EasyData.fromJson(json.decode(contents));
notifyListeners();
}
} on FileSystemException catch (_) {
// the file did not exist before
} catch (e) {
// error handling
log(e);
}
}
Future<File> writeData(EasyData data) async {
final file = await _localFile;
// Write the file
return file.writeAsString(jsonEncode(data.toJson()));
}
}
I used a provider for the service, because I want other services to be notified when the data is loaded from disk. The other Providers need to be declared with ChangeNotifierProxyProvider in main dart to use the information from the FileStorage Provider.
I hope it this was helpful.