SQLite adapters
This feature is part of FD Components Pro, which is documented as a preview and is not publicly available yet. See Editions and Availability.
FDC Pro provides two SQLite adapter models:
FdcSqliteTableAdapterfor writable table-backed CRUD datasets;FdcSqliteQueryAdapterfor read-only SQL result sets.
Table adapter
Use the table adapter when dataset fields map directly to SQLite columns.
final customers = FdcDataSet(
fields: const [
FdcIntegerField(name: 'customer_id', isKey: true),
FdcStringField(name: 'company', size: 120),
FdcStringField(name: 'city', size: 80),
FdcStringField(name: 'state', size: 2),
],
adapter: FdcSqliteTableAdapter(
databasePath: databasePath,
table: 'customers',
sorts: const [
FdcDataAdapterSort(
fieldName: 'company',
sortType: FdcSortType.ascending,
),
],
),
);
await customers.open();
Field names must match SQLite column names exactly. When the dataset does not provide explicit fields, the table adapter can infer schema information from PRAGMA table_info during open.
The path-backed constructor performs SQLite work through the adapter's worker-backed execution path.
Query adapter
Use the query adapter for read-only SQL results:
final sales = FdcDataSet(
fields: const [
FdcStringField(name: 'state', size: 2),
FdcDecimalField(name: 'total_sales', precision: 18, scale: 2),
],
adapter: FdcSqliteQueryAdapter(
databasePath: databasePath,
sql: '''
SELECT state, SUM(amount) AS total_sales
FROM orders
GROUP BY state
''',
),
);
The default query adapter runs the supplied SQL as a raw read-only source. It does not claim adapter-side filtering, sorting, or paging for arbitrary SQL.
Wrappable queries
For a simple SELECT that can safely be wrapped as a subquery, use FdcSqliteQueryAdapter.wrappable:
final adapter = FdcSqliteQueryAdapter.wrappable(
databasePath: databasePath,
sql: '''
SELECT
order_id,
customer_id,
order_date,
amount
FROM orders
''',
);
Wrappable mode can apply outer filtering, search, sorting, paging, and aggregates. Every projected column, including expressions, must expose a stable alias matching the FDC field name exactly.
Debugging and profiling
SQLite diagnostics are opt-in and exposed through a separate development entrypoint:
import 'package:flutter_data_components_pro/fdc_pro_debug.dart';
Normal application code should continue importing fdc_pro.dart. Import the debug entrypoint only in development or diagnostics code that needs low-level SQLite tracing.
SQL debug logging
Enable logSql to inspect the exact statements generated and executed by the SQLite adapters:
FdcSqliteDebug.logSql = true;
FdcSqliteDebug.logSqlArguments = true; // default
await customers.open();
SQL diagnostics use the [FDC-SQLITE-SQL] prefix. Depending on the operation, the trace can include generated SELECT, COUNT, PRAGMA, INSERT, UPDATE, DELETE, and transaction statements.
Bound arguments are logged separately when logSqlArguments is enabled. This is useful when a generated statement looks correct but a filter, search term, key value, or paging argument does not produce the expected result.
FdcSqliteDebug.logSql = true;
FdcSqliteDebug.logSqlArguments = false;
Disable argument logging when values are sensitive or when the SQL shape alone is sufficient for diagnosis.
For difficult call-path problems, stack traces can be enabled as well:
FdcSqliteDebug.logSql = true;
FdcSqliteDebug.logSqlStack = true;
logSqlStack is intentionally noisy and is best used for short, targeted debugging sessions.
Apply diagnostics
logApply is independent from SQL logging. It emits one summary line before each adapter apply call and reports insert, update, and delete counts together with the changed field names:
FdcSqliteDebug.logApply = true;
await customers.applyUpdates();
Apply diagnostics use the [FDC-SQLITE-APPLY] prefix. This mode is useful when investigating update lifecycle problems without printing every generated SQL statement.
For example, use it to answer questions such as:
- Did the dataset send an apply call at all?
- Was the change classified as an insert, update, or delete?
- Which fields were marked as changed?
- Was the submitted changeset empty?
SQL profiler
Enable logSqlTiming to profile SQLite operation timings and result sizes:
FdcSqliteDebug.logSqlTiming = true;
await customers.open();
Timing entries include elapsed milliseconds and, when available, row counts or affected-row counts. This makes the profiler useful for comparing query shapes, indexes, filters, paging strategies, and count-query cost.
You can profile timings without printing every SQL statement:
FdcSqliteDebug.logSql = false;
FdcSqliteDebug.logSqlTiming = true;
Or enable SQL and timing together when you need to correlate a slow operation with the generated statement:
FdcSqliteDebug.logSql = true;
FdcSqliteDebug.logSqlTiming = true;
FdcSqliteDebug.logSqlArguments = true;
A practical profiling workflow is:
- enable
logSqlTimingalone to identify slow operations; - enable
logSqltemporarily to inspect the SQL for a slow operation; - inspect bound arguments when filters or paging behavior are suspicious;
- enable
logSqlStackonly when the caller itself is unclear; - disable diagnostics after the investigation.
Diagnostic switches
| Option | Purpose | Default |
|---|---|---|
logSql | Logs generated and executed SQL statements | false |
logSqlArguments | Includes bound argument values with SQL logs | true |
logSqlStack | Prints a stack trace after SQL/apply diagnostics | false |
logSqlTiming | Logs elapsed operation time and result counts | false |
logApply | Logs one summary before each adapter apply call | false |
When both logSql and logSqlTiming are disabled, FDC does not allocate or start timing stopwatches on the normal SQLite hot path. Keep diagnostics disabled in normal builds and enable only the level of tracing needed for the current investigation.
Resource lifecycle
Datasets dispose their adapters when the dataset is disposed. When application code owns an adapter directly and needs explicit completion of SQLite shutdown, the table adapter also exposes an asynchronous close() operation.