Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ at the time; the last full run on the Simulator is under
| [`sensors_plus_watchos`](packages/sensors_plus_watchos) [![pub](https://img.shields.io/pub/v/sensors_plus_watchos.svg)](https://pub.dev/packages/sensors_plus_watchos) | [`sensors_plus`](https://pub.dev/packages/sensors_plus) | CoreMotion (`CMMotionManager`) |
| [`local_auth_watchos`](packages/local_auth_watchos) [![pub](https://img.shields.io/pub/v/local_auth_watchos.svg)](https://pub.dev/packages/local_auth_watchos) | [`local_auth`](https://pub.dev/packages/local_auth) | LocalAuthentication (passcode) |
| [`geolocator_watchos`](packages/geolocator_watchos) [![pub](https://img.shields.io/pub/v/geolocator_watchos.svg)](https://pub.dev/packages/geolocator_watchos) | [`geolocator`](https://pub.dev/packages/geolocator) | CoreLocation (`CLLocationManager`) |
| [`sqflite_watchos`](packages/sqflite_watchos) | [`sqflite`](https://pub.dev/packages/sqflite) | The system SQLite (`libsqlite3`), its C API called from Dart |
| [`video_player_watchos`](packages/video_player_watchos) [![pub](https://img.shields.io/pub/v/video_player_watchos.svg)](https://pub.dev/packages/video_player_watchos) | [`video_player`](https://pub.dev/packages/video_player) | AVFoundation + AVKit platform view |
| [`audioplayers_watchos`](packages/audioplayers_watchos) [![pub](https://img.shields.io/pub/v/audioplayers_watchos.svg)](https://pub.dev/packages/audioplayers_watchos) | [`audioplayers`](https://pub.dev/packages/audioplayers) | AVFoundation (`AVPlayer`) |
| [`in_app_purchase_watchos`](packages/in_app_purchase_watchos) [![pub](https://img.shields.io/pub/v/in_app_purchase_watchos.svg)](https://pub.dev/packages/in_app_purchase_watchos) | [`in_app_purchase`](https://pub.dev/packages/in_app_purchase) | StoreKit (`SKProductsRequest`, `SKPaymentQueue`) |
Expand Down Expand Up @@ -116,11 +117,11 @@ watch** — plugins built on those are portable, not excluded.

These have a watchOS-viable native backend and are good future additions;
they are simply out of scope for now (large surface or partial support):
`sqflite` (SQLite), `flutter_tts` (AVFoundation) and `cloud_firestore`
(the Firebase Apple SDK ships Firestore's core as a prebuilt binary with no
watchOS slice, so a port needs Firebase's from-source Firestore build; the
rest of the network-viable Firebase family — core, auth, storage,
messaging — is ported above).
`flutter_tts` (AVFoundation) and `cloud_firestore` (the Firebase Apple SDK
ships Firestore's core as a prebuilt binary with no watchOS slice, so a
port needs Firebase's from-source Firestore build; the rest of the
network-viable Firebase family — core, auth, storage, messaging — is
ported above).

## Usage

Expand Down
23 changes: 23 additions & 0 deletions packages/sqflite_watchos/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Dart / Flutter
.dart_tool/
build/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub/

# Xcode / SwiftPM (per-user, generated when watchos/Package.swift is opened)
**/.swiftpm/
**/.build/
**/xcuserdata/

# flutter-watchos app wiring (generated in an app's watchos/, never a plugin's)
watchos/Flutter/

# IDE
.idea/
.vscode/
*.iml

# macOS
.DS_Store
27 changes: 27 additions & 0 deletions packages/sqflite_watchos/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## 0.0.1

First version, staged: checked on the watchOS 27.0 Simulator, not yet on a
physical watch.

* sqflite on the watch, on the system SQLite: `watchos/Package.swift` links
`libsqlite3`, and Dart calls its C API over dart:ffi.
* sqflite's method protocol answered in Dart: open, close, execute, insert,
update, query with paged cursors, batch with `noResult` and
`continueOnError`, transactions with transaction ids, database files and
bytes. `lib/src/handler.dart` is adapted from sqflite_common_ffi's
`sqflite_ffi_impl.dart` and carries its BSD-2-Clause notice.
* Arguments bind as on iOS: a `bool` as 1 or 0, a `Uint8List` or plain
`List<int>` as a blob.
* A connection waits up to 2 seconds for another connection's lock, FMDB's
default on iOS.
* An in-memory database is never a single instance.
* `SqfliteWatchos` extends `SqflitePlatform`; `registerWith` makes its
`databaseFactoryWatchos` sqflite's default on watchOS.
* `sqflite_common: ">=2.5.14 <2.6.0"`: 2.5.14 fixes a hang when a cursor is
closed inside a transaction, and the `src/` imports stop below the next
minor.
* Two FFI exports: `sqflite_watchos_databases_path` and
`sqflite_watchos_sqlite_version`.
* The example's `lib/` is a small watch demo, because sqflite's own example
depends on the unpublished `sqflite_example_common`; its official
integration test is upstream's, unchanged.
25 changes: 25 additions & 0 deletions packages/sqflite_watchos/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BSD 2-Clause License

Copyright (c) 2019, Alexandre Roux Tekartik
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
90 changes: 90 additions & 0 deletions packages/sqflite_watchos/PORTING_REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# sqflite_watchos — porting report

## Verification status

| Aspect | Result |
|---|---|
| Implementation | ✅ Working (system SQLite over FFI) |
| watchOS capability | Full — every method of sqflite's protocol. The deprecated `Sqflite.*` debug helpers call the method channel directly and throw (see the README). Not yet checked on a physical watch |
| Host unit tests (`flutter test`) | ✅ pass on macOS — real SQL against the host's system SQLite (`/usr/lib/libsqlite3.dylib`; `libsqlite3.so.0` on CI's Linux runner), through sqflite_common's `Database` API and the protocol below it |
| Upstream integration test | ✅ passes verbatim on the watchOS 27.0 Simulator (14 tests), against the watch's own `libsqlite3`. Run it with `flutter-watchos test`: it uses `package:test`'s `test`, which `flutter-watchos drive` does not collect, so `drive` reports nothing and waits |
| Internal unified demo | not in the internal test app yet |

Marking: ✅ full / passes · ◐ partial — reason given · ○ not applicable (no upstream test) · ✗ unsupported on watchOS.

> This scaffold has been **finished by hand** into a working FFI package; see
> the status above. The generated notes below are kept for provenance.

Generated by `flutter-watchos plugin port` on 2026-10-19.

Source: `sqflite_darwin` 2.4.4+1
Protocol handler adapted from: `sqflite_common_ffi` 2.4.4, `lib/src/sqflite_ffi_impl.dart` (BSD-2-Clause, Alexandre Roux Tekartik)
Base platform: ios (Objective-C)
Output: `./sqflite_watchos`

## How this differs from sqflite_darwin

sqflite_darwin is a method-channel plugin around FMDB. Method channels do
not run on watchOS, and FMDB is a wrapper the watch does not need, so this
package keeps none of that native code:

- `lib/src/sqlite3.dart` binds the part of SQLite's C API sqflite needs
(`sqlite3_open_v2`, `_prepare_v2`, `_step`, the `_bind_*` and `_column_*`
functions, `_changes`, `_last_insert_rowid`, `_get_autocommit`,
`_busy_timeout`, the error calls). Arguments bind as sqflite_darwin binds
them: a `bool` as 1 or 0, a `Uint8List` or plain `List<int>` as a blob.
Each connection waits up to 2 seconds for another connection's lock,
FMDB's default on iOS. On the watch the functions resolve from the
process, because `watchos/Package.swift` links the system `libsqlite3`;
host tests open the host's system library instead, so the same code runs
real SQL there.
- `lib/src/handler.dart` answers sqflite's method protocol: the same
methods, arguments and results the method channel carries on iOS. It is
adapted from sqflite_common_ffi's `lib/src/sqflite_ffi_impl.dart`, the
protocol's reference implementation outside a method channel, and carries
its copyright notice. That covers transaction ids (a call from outside
the open transaction waits for it), `transactionClosed` when SQLite rolls a
transaction back, paged cursors, batches with `continueOnError`, and
single-instance databases, never for `:memory:`, as in sqflite_darwin.
- `watchos/Classes/sqflite_watchos_ffi.m` has two helpers: the Documents
directory, and `sqlite3_libversion()`, which also keeps `libsqlite3` linked.
- sqflite federates through sqflite_common's default factory. `SqfliteWatchos`
extends `SqflitePlatform`, as `SqfliteDarwin` does, and its `registerWith`
sets `databaseFactoryOrNull` to `databaseFactoryWatchos`, a factory from
sqflite_common's `buildDatabaseFactory`, as sqflite_darwin sets its
method-channel factory. `buildDatabaseFactory` and
`SqfliteDatabaseException` come from sqflite_common's `src/` libraries,
which sqflite_common_ffi imports the same way; `src/` is not public API, so
`sqflite_common: ">=2.5.14 <2.6.0"` stops below the next minor.

The one API the port tool flagged, `UIDevice` (the platform version), is
replaced by `Platform.operatingSystemVersion`.

## Example

sqflite's own example app is built on `sqflite_example_common`, which is
not published, so `example/lib/main.dart` is a small watch demo instead.
`example/integration_test/sqflite_test.dart` is upstream's test, unchanged but for
the formatting this repo's CI checks.

## Protocol coverage

| Method | watchOS |
|---|---|
| `openDatabase` | ✅ read-write (creating parent directories) or read-only; single instance by path, never for `:memory:` |
| `closeDatabase` | ✅ cancels operations queued behind a transaction; closing twice is fine |
| `execute` | ✅ several statements without arguments, one with; begins and ends transactions |
| `insert` | ✅ the new row id, or null when nothing was inserted (sqflite_common returns 0) |
| `update` | ✅ the changed row count |
| arguments, every method | ✅ null, int, `BigInt` within 64 bits, double, bool (1 or 0), String, `Uint8List` and `List<int>` (a blob) |
| `query` / `queryCursorNext` | ✅ all rows, or pages for a cursor |
| `batch` | ✅ `noResult`, `continueOnError` |
| `getDatabasesPath` | ✅ the app's Documents directory |
| `databaseExists` / `deleteDatabase` | ✅ |
| `readDatabaseBytes` / `writeDatabaseBytes` | ✅ |
| `options` / `debugMode` | ✅ kept; nothing is logged |
| `getPlatformVersion` | ✅ `watchOS <version>` |

---

See the finished plugins in the `flutterwatch/plugins` repo (e.g. `path_provider_watchos`, `battery_plus_watchos`) for worked examples of this exact shape.
77 changes: 77 additions & 0 deletions packages/sqflite_watchos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# sqflite_watchos

The watchOS implementation of [`sqflite`](https://pub.dev/packages/sqflite).

Databases are the **system SQLite**: watchOS ships `libsqlite3`, the
flutter-watchos CLI links it, and Dart calls its C API over dart:ffi.
sqflite's method protocol (open, execute, insert, update, query with cursors,
batch, transactions) is answered in Dart, the way `sqflite_common_ffi`
answers it on desktop, so the `Database` API behaves as it does on iOS and
Android.

> Scaffolded by [`flutter-watchos plugin port`](https://github.com/flutterwatch/flutter-watchos)
> from `sqflite_darwin`, then implemented and verified by hand.

## Usage

This is a federated plugin implementation. Apps that already depend on
`sqflite` and target watchOS only need to add this package alongside it:

```yaml
dependencies:
sqflite: ^2.4.4
sqflite_watchos: ^0.0.1
```

The plugin registers automatically via Flutter's federated registry — no
explicit imports required from app code. Use sqflite as usual:

```dart
final Database db = await openDatabase(
'laps.db',
version: 1,
onCreate: (Database db, int version) =>
db.execute('CREATE TABLE lap (id INTEGER PRIMARY KEY, at INTEGER)'),
);
await db.insert('lap', <String, Object?>{'at': DateTime.now().millisecondsSinceEpoch});
```

## Behaviour on watchOS

| sqflite | watchOS |
|---|---|
| `openDatabase` / `openReadOnlyDatabase` | supported; relative paths live in the app's Documents directory, as on iOS, and missing parent directories are created. A connection waits up to 2 seconds for another connection's lock, as on iOS |
| `execute` / `insert` / `update` / `delete` / `rawQuery` and friends | supported; integers are 64-bit, a `bool` argument is stored as 1 or 0, text is UTF-8, and a blob (a `Uint8List` or a `List<int>` argument) comes back as `Uint8List`. sqflite_common still warns once per type, in debug, about `bool` and `List<int>` arguments, as on every platform |
| `queryCursor` | supported, page by page |
| `batch` | supported, with `noResult` and `continueOnError` |
| `transaction` | supported; calls from outside an open transaction wait until it ends |
| `databaseExists` / `deleteDatabase` / `getDatabasesPath` | supported; deleting an open single-instance database closes it first, and the `-wal`, `-shm` and `-journal` files beside it go too, as on iOS |
| `readDatabaseBytes` / `writeDatabaseBytes` | supported |
| `DatabaseException` helpers | `isUniqueConstraintError`, `isNoSuchTableError`, `isSyntaxError`, `isOpenFailedError`, `isReadOnlyError`, `isDatabaseClosedError`; `getResultCode()` is SQLite's extended result code |

SQL runs on the isolate that calls it, synchronously inside each `Future`.
On iOS sqflite runs it on a native background thread; on the watch a long
query holds up the frame it runs in, so keep heavy work small or move it to
an isolate of its own.

`SqfliteWatchos.sqliteVersion` reports the system SQLite's version.

## Not supported on watchOS

Every method of sqflite's protocol is answered; `PORTING_REPORT.md` lists
each under "Protocol coverage". What is left:

| sqflite | On watchOS | Why |
|---|---|---|
| `Sqflite.setDebugModeOn`, `Sqflite.devSetDebugModeOn`, `Sqflite.devSetOptions`, `Sqflite.devInvokeMethod` (all deprecated) | throw `MissingPluginException` | they call sqflite's method channel directly, not the database factory; `databaseFactory.setLogLevel` reaches this package |
| `androidSetLocale` | fails | Android only, as on iOS |

`Sqflite.firstIntValue`, `Sqflite.hex` and `Sqflite.setLockWarningInfo`
run in Dart and work. The sqflite-specific `PRAGMA sqflite_…` statements
are accepted and ignored.

## License

BSD-2-Clause, as upstream sqflite's. `lib/src/handler.dart` is adapted from
sqflite_common_ffi and carries its copyright notice. See `LICENSE` for the
full text.
19 changes: 19 additions & 0 deletions packages/sqflite_watchos/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
include: package:flutter_lints/flutter.yaml

analyzer:
exclude:
- build/**
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**
language:
strict-casts: true
strict-inference: true
strict-raw-types: true

linter:
rules:
public_member_api_docs: true
14 changes: 14 additions & 0 deletions packages/sqflite_watchos/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.dart_tool/
.packages
build/
.flutter-plugins
.flutter-plugins-dependencies
*.iml
.idea/
.DS_Store

# watchOS / CocoaPods
watchos/Pods/
watchos/Podfile.lock
watchos/.symlinks/
watchos/Flutter/Flutter.framework
16 changes: 16 additions & 0 deletions packages/sqflite_watchos/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# sqflite_example

A watchOS-only example: laps saved to a SQLite database on the watch. Run it
on an Apple Watch simulator with:

```sh
flutter-watchos run
```

`integration_test/sqflite_test.dart` is sqflite's official integration test,
unchanged. It uses `package:test`'s `test`, which `flutter-watchos drive`
does not collect, so run it with `test`:

```sh
flutter-watchos test integration_test/sqflite_test.dart -d <watch-sim>
```
4 changes: 4 additions & 0 deletions packages/sqflite_watchos/example/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
analyzer:
exclude:
- build/**
include: package:flutter_lints/flutter.yaml
Loading
Loading