mirror of
https://github.com/immich-app/drift.git
synced 2026-09-30 13:22:57 +08:00
Move more content over
This commit is contained in:
@@ -1,184 +0,0 @@
|
||||
---
|
||||
|
||||
title: FAQ
|
||||
|
||||
---
|
||||
|
||||
## Using the database
|
||||
If you've created a `MyDatabase` class by following the [getting started guide](setup.md), you
|
||||
still need to somehow obtain an instance of it. It's recommended to only have one (singleton) instance of your database,
|
||||
so you could store that instance in a global variable:
|
||||
|
||||
### Vanilla flutter
|
||||
```dart
|
||||
late MyDatabase database;
|
||||
|
||||
void main() {
|
||||
database = MyDatabase();
|
||||
runApp(MyFlutterApp());
|
||||
}
|
||||
```
|
||||
It would be cleaner to use `InheritedWidgets` for that, and the `provider` package helps here:
|
||||
|
||||
### Provider
|
||||
If you're using the [provider](https://pub.dev/packages/provider) package, you can wrap your top-level widget in a
|
||||
provider that manages the database instance:
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
Provider<MyDatabase>(
|
||||
create: (context) => MyDatabase(),
|
||||
child: MyFlutterApp(),
|
||||
dispose: (context, db) => db.close(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
Your widgets would then have access to the database using `Provider.of<MyDatabase>(context)`.
|
||||
### GetX
|
||||
If you're using the [GetX](https://pub.dev/packages/get) package, you can add it as a service that manages the database instance:.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
Get.put(MyDatabase());
|
||||
runApp(MyFlutterApp());
|
||||
}
|
||||
```
|
||||
Your widgets would then have access to the database using `Get.find<MyDatabase>().your_method`.
|
||||
|
||||
### A more complex architecture
|
||||
If you're strict on keeping your business logic out of the widget layer, you probably use some dependency injection
|
||||
framework like `kiwi` or `get_it` to instantiate services and view models. Creating a singleton instance of `MyDatabase`
|
||||
in your favorite dependency injection framework for flutter hence solves this problem for you.
|
||||
|
||||
## Why am I getting no such table errors?
|
||||
|
||||
If you add another table after your app has already been installed, you need to write a [migration](Migrations/index.md)
|
||||
that covers creating that table. If you're in the process of developing your app and want to use un- and reinstall your app
|
||||
instead of writing migrations, that's fine too. Please note that your apps data might be backed up on Android, so
|
||||
manually deleting your app's data instead of a reinstall is necessary on some devices.
|
||||
|
||||
## How do I fix lints in generated files?
|
||||
|
||||
Based on your linter settings, you might see some warnings in the `.g.dart` files generated by drift. Since lints are mainly used for
|
||||
human-written code, we recommend to disable static analysis on generated files. For that, generate a top-level file called
|
||||
`analysis_options.yaml` in your project and add this content:
|
||||
```yaml
|
||||
analyzer:
|
||||
exclude:
|
||||
- "**/*.g.dart"
|
||||
```
|
||||
|
||||
You might have to restart your IDE for the changes to apply.
|
||||
|
||||
## How can I inspect generated SQL?
|
||||
|
||||
All database implementations (`NativeDatabase`, `FlutterQueryExecutor`, ...) have a `logStatements` parameter that
|
||||
you can set to `true`. When enabled, drift will print the statements it runs.
|
||||
|
||||
## How do I insert data on the first app start?
|
||||
|
||||
You can populate the database on the first start of your app with a custom [migration strategy](Migrations/index.md).
|
||||
To insert data when the database is created (which usually happens when the app is first run), you can use this:
|
||||
|
||||
```dart
|
||||
MigrationStrategy(
|
||||
onCreate: (m) async {
|
||||
await m.createAll(); // create all tables
|
||||
await into(myTable).insert(...); // insert on first run.
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
You can even use transactions or batches in the `onCreate` callback.
|
||||
|
||||
Another approach is to include a pre-populated database in your app's asset and use that one:
|
||||
|
||||
```dart
|
||||
QueryExecutor databaseWithDefaultAsset(File file, String asset) {
|
||||
// A LazyDatabase lets us do async initialization work.
|
||||
return LazyDatabase(() async {
|
||||
if (!await file.exists()) {
|
||||
// Database does not exist yet, use default from asset
|
||||
final content = await rootBundle.load(asset);
|
||||
|
||||
await file.parent.create(recursive: true);
|
||||
await file.writeAsBytes(content.buffer.asUint8List(0));
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## My generated code is using another class with the same name
|
||||
|
||||
When you have an imported a class with the same name as the should-be generated
|
||||
ones on your `database.dart` file and your run `build_runner` there is a known
|
||||
problem about it using the imported class instead of the generated ones.
|
||||
|
||||
To solve that, if you can, you can enable
|
||||
[modular code generation](./generation_options/modular.md).
|
||||
It slightly changes how drift is used, but you probably will only have to update
|
||||
a few files to change parts to imports. The big difference is that it allows drift
|
||||
to emit a standalone library file instead of a part - that can have its own imports,
|
||||
so this is much easier and we'll use the correct imports even when the same name
|
||||
classes are imported in your `database.dart` file.
|
||||
|
||||
|
||||
## How does drift compare to X?
|
||||
There are a variety of good persistence libraries for Dart and Flutter.
|
||||
|
||||
That said, here's an incomplete (and obviously biased) list of great libraries and how drift compares to them.
|
||||
If you have experience with any of these (or other) libraries and want to share how they compare to drift, please
|
||||
feel invited to contribute to this page.
|
||||
|
||||
### sqflite, sqlite3
|
||||
|
||||
Sqflite is a Flutter package that provides bindings to the sqlite api for both iOS and Android. It's well maintained
|
||||
and has stable api. In fact, the `moor_flutter` or `drift_sqflite` variants are built on top of sqflite. But even though sqflite
|
||||
has an api to construct some simple queries in Dart, drift goes a bit further by
|
||||
|
||||
* Generating type-safe mapping code for your queries
|
||||
* Providing auto-updating streams for queries
|
||||
* Managing `CREATE TABLE` statements and most schema migrations
|
||||
* A more fluent api to compose queries
|
||||
|
||||
Still, for most apps that don't need these features, sqflite can be a very fitting persistence library.
|
||||
|
||||
The same thing applies to the `sqlite3` package - `package:drift/native.dart` uses that library, but provides
|
||||
additional services on top.
|
||||
|
||||
### sqlcool
|
||||
Sqlcool is a lightweight library around sqflite that makes writing queries and schema management easier, it also has
|
||||
auto-updating streams. It can be a decent alternative to drift if you don't want/need generated code to parse the
|
||||
result of your queries.
|
||||
|
||||
### floor
|
||||
Floor also has a lot of convenience features like auto-updating queries and schema migrations. Similar to drift, you
|
||||
define the structure of your database in Dart. Then, you have write queries in sql - the mapping code if generated
|
||||
by floor. Drift has a [similar feature](sql_api/custom_queries.md), but it can also verify that your queries are valid at compile time. Drift
|
||||
additionally has an api that lets you write some queries in Dart instead of sql.
|
||||
|
||||
A difference between these two is that Floor lets you write your own classes and generates mapping code around that.
|
||||
By default, drift generates most classes for you, which can make it easier to use, but makes the api less flexible in some
|
||||
instances.
|
||||
Drift can also be used with [custom row classes](dart_api/rows.md#custom-dataclass) though.
|
||||
|
||||
### firebase
|
||||
Both the Realtime Database and Cloud Datastore are easy to use persistence libraries that can sync across devices while
|
||||
still working offline. Both of them feature auto-updating streams and a simple query api. However, neither of them is
|
||||
a relational database, so they don't support useful sql features like aggregate functions, joins, or complex filters.
|
||||
|
||||
Firebase is a very good option when
|
||||
|
||||
- your data model can be expressed as documents instead of relations
|
||||
- you don't have your own backend, but still need to synchronize data
|
||||
|
||||
## Can I view a drift database?
|
||||
|
||||
Yes! Drift stores its data in a sqlite3 database file that can be extracted from the device and inspected locally.
|
||||
|
||||
To inspect a drift database directly in your app, you can use the [`drift_db_viewer`](https://pub.dev/packages/drift_db_viewer)
|
||||
package by Koen Van Looveren.
|
||||
|
||||
There is also an under-development DevTools Extension that comes when you add `drift` to your app. Open DevTools and try it out! Contributions and feedback are definitely welcome!
|
||||
@@ -1,219 +0,0 @@
|
||||
---
|
||||
|
||||
title: Setup
|
||||
description: All you need to know about adding drift to your project.
|
||||
|
||||
---
|
||||
|
||||
Drift is a powerful database library for Dart and Flutter applications. To
|
||||
support its advanced capabilities like type-safe SQL queries, verification of
|
||||
your database and migrations, it uses a builder and command-line tooling that
|
||||
runs at compile-time.
|
||||
|
||||
This means that the setup involves a little more than just adding a single
|
||||
dependency to your pubspec. This page explains how to add drift to your project
|
||||
and gives pointers to the next steps.
|
||||
If you're stuck adding drift, or have questions or feedback about the project,
|
||||
please share that with the community by [starting a discussion on GitHub](https://github.com/simolus3/drift/discussions).
|
||||
If you want to look at an example app for inspiration, a cross-platform Flutter app using drift is available
|
||||
[as part of the drift repository](https://github.com/simolus3/drift/tree/develop/examples/app).
|
||||
|
||||
## The dependencies
|
||||
|
||||
First, let's add drift to your project's `pubspec.yaml`.
|
||||
In addition to the core drift dependencies (`drift` and `drift_dev` to generate code), we're also
|
||||
adding a package to open database on the respective platform.
|
||||
|
||||
=== "Flutter (sqlite3)"
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
drift: ^{{ versions.drift }}
|
||||
drift_flutter: ^{{ versions.drift_flutter }}
|
||||
path_provider: ^{{ versions.path_provider }}
|
||||
|
||||
dev_dependencies:
|
||||
drift_dev: ^{{ versions.drift_dev }}
|
||||
build_runner: ^{{ versions.build_runner }}
|
||||
```
|
||||
|
||||
Alternatively, you can achieve the same result using the following command:
|
||||
|
||||
```
|
||||
dart pub add drift drift_flutter path_provider dev:drift_dev dev:build_runner
|
||||
```
|
||||
|
||||
=== "Dart (sqlite3)"
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
drift: ^{{ versions.drift }}
|
||||
sqlite3: ^{{ versions.sqlite3 }}
|
||||
|
||||
dev_dependencies:
|
||||
drift_dev: ^{{ versions.drift_dev }}
|
||||
build_runner: ^{{ versions.build_runner }}
|
||||
```
|
||||
|
||||
Alternatively, you can achieve the same result using the following command:
|
||||
|
||||
```
|
||||
dart pub add drift sqlite3 dev:drift_dev dev:build_runner
|
||||
```
|
||||
|
||||
=== "Dart (Postgres)"
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
drift: ^{{ versions.drift }}
|
||||
postgres: ^{{ versions.postgres }}
|
||||
drift_postgres: ^{{ versions.drift_postgres }}
|
||||
|
||||
dev_dependencies:
|
||||
drift_dev: ^{{ versions.drift_dev }}
|
||||
build_runner: ^{{ versions.build_runner }}
|
||||
```
|
||||
|
||||
Alternatively, you can achieve the same result using the following command:
|
||||
|
||||
```
|
||||
dart pub add drift postgres drift_postgres dev:drift_dev dev:build_runner
|
||||
```
|
||||
|
||||
Drift only generates code for sqlite3 by default. So, also create a `build.yaml`
|
||||
to [configure](generation_options/index.md) `drift_dev`:
|
||||
|
||||
```yaml
|
||||
targets:
|
||||
$default:
|
||||
builders:
|
||||
drift_dev:
|
||||
options:
|
||||
sql:
|
||||
dialects:
|
||||
- postgres
|
||||
# Uncomment if you need to support both
|
||||
# - sqlite
|
||||
```
|
||||
|
||||
|
||||
## Database class
|
||||
|
||||
Every project using drift needs at least one class to access a database. This class references all the
|
||||
tables you want to use and is the central entry point for drift's code generator.
|
||||
In this example, we'll assume that this database class is defined in a file called `database.dart` and
|
||||
somewhere under `lib/`. Of course, you can put this class in any Dart file you like.
|
||||
|
||||
To make the database useful, we'll also add a simple table to it. This table, `TodoItems`, can be used
|
||||
to store todo items for a todo list app.
|
||||
Everything there is to know about defining tables in Dart is described on the [Dart tables](dart_api/tables.md) page.
|
||||
If you prefer using SQL to define your tables, drift supports that too! You can read all about that [here](sql_api/index.md).
|
||||
|
||||
For now, populate the contents of `database.dart` with these tables which could form the persistence
|
||||
layer of a simple todolist application:
|
||||
|
||||
{{ load_snippet('before_generation','lib/snippets/setup/database.dart.excerpt.json') }}
|
||||
|
||||
You will get an analyzer warning on the `part` statement and on `extends _$AppDatabase`. This is
|
||||
expected because drift's generator did not run yet.
|
||||
You can do that by invoking [build_runner](https://pub.dev/packages/build_runner):
|
||||
|
||||
- `dart run build_runner build` generates all the required code once.
|
||||
- `dart run build_runner watch` watches for changes in your sources and generates code with
|
||||
incremental rebuilds. This is suitable for development sessions.
|
||||
|
||||
After running either command, the `database.g.dart` file containing the generated `_$AppDatabase`
|
||||
class will have been generated.
|
||||
You will now see errors related to missing overrides and a missing constructor. The constructor
|
||||
is responsible for telling drift how to open the database. The `schemaVersion` getter is relevant
|
||||
for migrations after changing the database, we can leave it at `1` for now. Update `database.dart`
|
||||
so it now looks like this:
|
||||
|
||||
<a name="open"></a>
|
||||
|
||||
=== "Flutter (sqlite3)"
|
||||
|
||||
{{ load_snippet('flutter','lib/snippets/setup/database.dart.excerpt.json',indent=4) }}
|
||||
|
||||
If you need to customize how databases are opened, you can also set the connection
|
||||
up manually:
|
||||
|
||||
??? note "Manual database setup"
|
||||
|
||||
```dart
|
||||
import 'dart:io';
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:sqlite3/sqlite3.dart';
|
||||
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
|
||||
|
||||
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(p.join(dbFolder.path, 'db.sqlite'));
|
||||
|
||||
// Also work around limitations on old Android versions
|
||||
if (Platform.isAndroid) {
|
||||
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
|
||||
}
|
||||
|
||||
// Make sqlite3 pick a more suitable location for temporary files - the
|
||||
// one from the system may be inaccessible due to sandboxing.
|
||||
final cachebase = (await getTemporaryDirectory()).path;
|
||||
// We can't access /tmp on Android, which sqlite3 would try by default.
|
||||
// Explicitly tell it about the correct temporary directory.
|
||||
sqlite3.tempDirectory = cachebase;
|
||||
|
||||
return NativeDatabase.createInBackground(file);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The Android-specific workarounds are necessary because sqlite3 attempts to use `/tmp` to store
|
||||
private data on unix-like systems, which is forbidden on Android. We also use this opportunity
|
||||
to work around a problem some older Android devices have with loading custom libraries through
|
||||
`dart:ffi`.
|
||||
|
||||
|
||||
=== "Dart (sqlite3)"
|
||||
|
||||
{{ load_snippet('sqlite3','lib/snippets/setup/database.dart.excerpt.json',indent=4) }}
|
||||
|
||||
|
||||
=== "Dart (Postgres)"
|
||||
|
||||
{{ load_snippet('postgres','lib/snippets/setup/database.dart.excerpt.json',indent=4) }}
|
||||
|
||||
|
||||
|
||||
|
||||
## Next steps
|
||||
|
||||
Congratulations! With this setup complete, your project is ready to use drift.
|
||||
This short snippet shows how the database can be opened and how to run inserts and selects:
|
||||
|
||||
{{ load_snippet('use','lib/snippets/setup/database.dart.excerpt.json') }}
|
||||
|
||||
But drift can do so much more! These pages provide more information useful when getting
|
||||
started with drift:
|
||||
|
||||
- [Dart tables](dart_api/tables.md): This page describes how to define your own tables in Dart.
|
||||
For an overview of the classes drift generates for tables, check out [row classes](dart_api/rows.md).
|
||||
- For new drift users or users not familiar with SQL, the [manager](dart_api/manager.md) APIs
|
||||
for tables allows writing most queries with a syntax you're likely familiar with from ORMs or other
|
||||
packages.
|
||||
- Writing queries: Drift-generated classes support writing the most common SQL statements, like
|
||||
[selects](dart_api/select.md) or [inserts, updates and deletes](dart_api/writes.md).
|
||||
- Something to keep in mind for later: When changing the database, for instance by adding new columns
|
||||
or tables, you need to write a migration so that existing databases are transformed to the new
|
||||
format. Drift's extensive [migration tools](Migrations/index.md) help with that.
|
||||
- Take a look at our [FAQ](./faq.md)! It will help you with the most common questions about drift projects.
|
||||
|
||||
Once you're familiar with the basics, the [overview here](index.md) shows what
|
||||
more drift has to offer.
|
||||
This includes transactions, automated tooling to help with migrations, multi-platform support
|
||||
and more.
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Reference in New Issue
Block a user