Add `drift_sqflite` to replace `moor_flutter`
|
@ -0,0 +1 @@
|
|||
include: package:flutter_lints/flutter.yaml
|
|
@ -0,0 +1,215 @@
|
|||
/// Flutter implementation for the drift database packages.
|
||||
///
|
||||
/// The [SqfliteQueryExecutor] class can be used as a drift database
|
||||
/// implementation based on the `sqflite` package.
|
||||
library drift_sqflite;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/backends.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sqflite/sqflite.dart' as s;
|
||||
|
||||
/// Signature of a function that runs when a database doesn't exist on file.
|
||||
/// This can be useful to, for instance, load the database from an asset if it
|
||||
/// doesn't exist.
|
||||
typedef DatabaseCreator = FutureOr<void> Function(File file);
|
||||
|
||||
class _SqfliteDelegate extends DatabaseDelegate with _SqfliteExecutor {
|
||||
@override
|
||||
late s.Database db;
|
||||
bool _isOpen = false;
|
||||
|
||||
final bool inDbFolder;
|
||||
final String path;
|
||||
|
||||
bool singleInstance;
|
||||
final DatabaseCreator? creator;
|
||||
|
||||
_SqfliteDelegate(this.inDbFolder, this.path,
|
||||
{this.singleInstance = true, this.creator});
|
||||
|
||||
@override
|
||||
late final DbVersionDelegate versionDelegate = _SqfliteVersionDelegate(db);
|
||||
|
||||
@override
|
||||
TransactionDelegate get transactionDelegate =>
|
||||
_SqfliteTransactionDelegate(this);
|
||||
|
||||
@override
|
||||
bool get isOpen => _isOpen;
|
||||
|
||||
@override
|
||||
Future<void> open(QueryExecutorUser user) async {
|
||||
String resolvedPath;
|
||||
if (inDbFolder) {
|
||||
resolvedPath = join(await s.getDatabasesPath(), path);
|
||||
} else {
|
||||
resolvedPath = path;
|
||||
}
|
||||
|
||||
final file = File(resolvedPath);
|
||||
if (creator != null && !await file.exists()) {
|
||||
await creator!(file);
|
||||
}
|
||||
|
||||
// default value when no migration happened
|
||||
db = await s.openDatabase(
|
||||
resolvedPath,
|
||||
singleInstance: singleInstance,
|
||||
);
|
||||
_isOpen = true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
return db.close();
|
||||
}
|
||||
}
|
||||
|
||||
class _SqfliteVersionDelegate extends DynamicVersionDelegate {
|
||||
final s.Database _db;
|
||||
|
||||
_SqfliteVersionDelegate(this._db);
|
||||
|
||||
@override
|
||||
Future<int> get schemaVersion async {
|
||||
final result = await _db.rawQuery('PRAGMA user_version;');
|
||||
return result.single.values.first as int;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setSchemaVersion(int version) async {
|
||||
await _db.rawUpdate('PRAGMA user_version = $version;');
|
||||
}
|
||||
}
|
||||
|
||||
class _SqfliteTransactionDelegate extends SupportedTransactionDelegate {
|
||||
final _SqfliteDelegate delegate;
|
||||
|
||||
_SqfliteTransactionDelegate(this.delegate);
|
||||
|
||||
@override
|
||||
void startTransaction(Future<void> Function(QueryDelegate) run) {
|
||||
delegate.db.transaction((transaction) async {
|
||||
final executor = _SqfliteTransactionExecutor(transaction);
|
||||
await run(executor);
|
||||
}).catchError((_) {
|
||||
// Ignore the error! We send a fake exception to indicate a rollback.
|
||||
// sqflite will rollback, but the exception will bubble up. Here we stop
|
||||
// the exception.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _SqfliteTransactionExecutor extends QueryDelegate with _SqfliteExecutor {
|
||||
@override
|
||||
final s.DatabaseExecutor db;
|
||||
|
||||
_SqfliteTransactionExecutor(this.db);
|
||||
}
|
||||
|
||||
mixin _SqfliteExecutor on QueryDelegate {
|
||||
s.DatabaseExecutor get db;
|
||||
|
||||
@override
|
||||
Future<void> runBatched(BatchedStatements statements) async {
|
||||
final batch = db.batch();
|
||||
|
||||
for (final arg in statements.arguments) {
|
||||
batch.execute(statements.statements[arg.statementIndex], arg.arguments);
|
||||
}
|
||||
|
||||
await batch.commit(noResult: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> runCustom(String statement, List<Object?> args) {
|
||||
return db.execute(statement, args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runInsert(String statement, List<Object?> args) {
|
||||
return db.rawInsert(statement, args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<QueryResult> runSelect(String statement, List<Object?> args) async {
|
||||
final result = await db.rawQuery(statement, args);
|
||||
return QueryResult.fromRows(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runUpdate(String statement, List<Object?> args) {
|
||||
return db.rawUpdate(statement, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// A query executor that uses sqflite internally.
|
||||
class SqfliteQueryExecutor extends DelegatedDatabase {
|
||||
/// A query executor that will store the database in the file declared by
|
||||
/// [path]. If [logStatements] is true, statements sent to the database will
|
||||
/// be [print]ed, which can be handy for debugging. The [singleInstance]
|
||||
/// parameter sets the corresponding parameter on [s.openDatabase].
|
||||
/// The [creator] will be called when the database file doesn't exist. It can
|
||||
/// be used to, for instance, populate default data from an asset. Note that
|
||||
/// migrations might behave differently when populating the database this way.
|
||||
/// For instance, a database created by an [creator] will not receive the
|
||||
/// [MigrationStrategy.onCreate] callback because it hasn't been created by
|
||||
/// moor.
|
||||
SqfliteQueryExecutor(
|
||||
{required String path,
|
||||
bool? logStatements,
|
||||
bool singleInstance = true,
|
||||
DatabaseCreator? creator})
|
||||
: super(
|
||||
_SqfliteDelegate(false, path,
|
||||
singleInstance: singleInstance, creator: creator),
|
||||
logStatements: logStatements);
|
||||
|
||||
/// A query executor that will store the database in the file declared by
|
||||
/// [path], which will be resolved relative to [s.getDatabasesPath()].
|
||||
/// If [logStatements] is true, statements sent to the database will
|
||||
/// be [print]ed, which can be handy for debugging. The [singleInstance]
|
||||
/// parameter sets the corresponding parameter on [s.openDatabase].
|
||||
/// The [creator] will be called when the database file doesn't exist. It can
|
||||
/// be used to, for instance, populate default data from an asset. Note that
|
||||
/// migrations might behave differently when populating the database this way.
|
||||
/// For instance, a database created by an [creator] will not receive the
|
||||
/// [MigrationStrategy.onCreate] callback because it hasn't been created by
|
||||
/// moor.
|
||||
SqfliteQueryExecutor.inDatabaseFolder(
|
||||
{required String path,
|
||||
bool? logStatements,
|
||||
bool singleInstance = true,
|
||||
DatabaseCreator? creator})
|
||||
: super(
|
||||
_SqfliteDelegate(true, path,
|
||||
singleInstance: singleInstance, creator: creator),
|
||||
logStatements: logStatements);
|
||||
|
||||
/// The underlying sqflite [s.Database] object used by moor to send queries.
|
||||
///
|
||||
/// Using the sqflite database can cause unexpected behavior in moor. For
|
||||
/// instance, stream queries won't update for updates sent to the [s.Database]
|
||||
/// directly.
|
||||
/// For this reason, projects shouldn't use this getter unless they absolutely
|
||||
/// need to. The database is exposed to make migrating from sqflite to moor
|
||||
/// easier.
|
||||
///
|
||||
/// Note that this returns null until the moor database has been opened.
|
||||
/// A moor database is opened lazily when the first query runs.
|
||||
s.Database? get sqfliteDb {
|
||||
final sqfliteDelegate = delegate as _SqfliteDelegate;
|
||||
return sqfliteDelegate.isOpen ? sqfliteDelegate.db : null;
|
||||
}
|
||||
|
||||
@override
|
||||
// We're not really required to be sequential since sqflite has an internal
|
||||
// lock to bring statements into a sequential order.
|
||||
// Setting isSequential here helps with moor cancellations in stream queries
|
||||
// though.
|
||||
bool get isSequential => true;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
name: drift_sqflite
|
||||
description: A Flutter-only implementation of a drift database, based on the `sqflite` package.
|
||||
version: 1.0.0
|
||||
repository: https://github.com/simolus3/drift
|
||||
homepage: https://drift.simonbinder.eu/
|
||||
issue_tracker: https://github.com/simolus3/drift/issues
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
drift: ^1.0.0
|
||||
sqflite: ^2.0.0+3
|
||||
path: ^1.8.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.4
|
||||
flutter_test:
|
||||
sdk: flutter
|
|
@ -9,3 +9,5 @@ GeneratedPluginRegistrant.java
|
|||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
|
@ -26,21 +26,26 @@ apply plugin: 'kotlin-android'
|
|||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.moor_flutter"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
applicationId "com.example.sqflite"
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.moor_flutter">
|
||||
package="com.example.sqflite">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
|
@ -1,16 +1,12 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.moor_flutter">
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="moor_flutter"
|
||||
package="com.example.sqflite">
|
||||
<application
|
||||
android:label="sqflite"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
|
@ -24,15 +20,6 @@
|
|||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
|
@ -1,4 +1,4 @@
|
|||
package com.example.moor_flutter
|
||||
package com.example.sqflite
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting -->
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
|
@ -10,9 +10,9 @@
|
|||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">@android:color/white</item>
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -1,5 +1,5 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.moor_flutter">
|
||||
package="com.example.sqflite">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
|
@ -1,12 +1,12 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
ext.kotlin_version = '1.6.10'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
|
@ -1,7 +1,3 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
include ':app'
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 564 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
|
@ -1,8 +1,8 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:drift_sqflite/drift_sqflite.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart' show WidgetsFlutterBinding;
|
||||
import 'package:moor_flutter/moor_flutter.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sqflite/sqflite.dart' show getDatabasesPath;
|
||||
import 'package:test/test.dart';
|
||||
|
@ -12,7 +12,7 @@ class SqfliteExecutor extends TestExecutor {
|
|||
@override
|
||||
DatabaseConnection createConnection() {
|
||||
return DatabaseConnection.fromExecutor(
|
||||
FlutterQueryExecutor.inDatabaseFolder(
|
||||
SqfliteQueryExecutor.inDatabaseFolder(
|
||||
path: 'app.db',
|
||||
singleInstance: false,
|
||||
),
|
||||
|
@ -44,7 +44,7 @@ Future<void> main() async {
|
|||
}
|
||||
|
||||
var didCallCreator = false;
|
||||
final executor = FlutterQueryExecutor(
|
||||
final executor = SqfliteQueryExecutor(
|
||||
path: dbFile.path,
|
||||
singleInstance: true,
|
||||
creator: (file) async {
|
|
@ -1,4 +1,4 @@
|
|||
name: moor_flutter_integration_test
|
||||
name: drift_sqflite_integration_tests
|
||||
description: A new Flutter project.
|
||||
publish_to: 'none'
|
||||
version: 1.0.0+1
|
||||
|
@ -9,7 +9,8 @@ environment:
|
|||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
moor_flutter:
|
||||
drift_sqflite:
|
||||
path: ../../../drift_sqflite
|
||||
tests:
|
||||
path: ../tests
|
||||
encrypted_moor:
|
||||
|
@ -28,4 +29,4 @@ dependency_overrides:
|
|||
|
||||
flutter:
|
||||
assets:
|
||||
- test_asset.db
|
||||
- test_asset.db
|