Dart Types Reference
Every public type in neuctra_authix_dart_package. All of them come from the single package import — there are no sub-imports.
import 'package:neuctra_authix_dart_package/neuctra_authix_dart_package.dart';How null works here
Parameter classes serialise with toJson(), and null fields are omitted from the request body — the same way JSON.stringify drops undefined in the JavaScript SDK. A null therefore means "leave unchanged", not "clear this field".
Client
NeuctraAuthix
The client. One positional config plus two optional named arguments.
1final authix = NeuctraAuthix(
2 config, {
3 http.Client? client, // bring your own — BrowserClient, a mock, a retrying wrapper
4 SessionStore? session, // share one cookie jar between clients
5});
6
7authix.appId; // String
8authix.baseUrl; // String — trailing slashes stripped
9authix.keyType; // ApiKeyType.publishable | ApiKeyType.secret
10authix.keyPrefix; // String — 'pk_live_a1b2c3d4'. Safe to log.
11authix.appName; // String?
12authix.timeout; // Duration
13authix.session; // SessionStore
14authix.close(); // releases the client, if the SDK created it
15
16authix.toString(); // masks the key: 'sk_live_e5f6a7b8…cccc'NeuctraAuthixConfig
Immutable configuration. Also exposes copyWith(); toString() masks the key.
| Field | Type | Notes |
|---|---|---|
| appId | String | Required. Every route is scoped to one app. |
| publishableKey | String? | pk_live_… — safe to ship in an app. Supply this or secretKey, not both. |
| secretKey | String? | sk_live_… — full account authority. Server-side only. |
| baseUrl | String | Defaults to the production API. A trailing slash is stripped. |
| appName | String? | Optional label. Cosmetic. |
| timeout | Duration | Per-request timeout. Defaults to 30 seconds. |
| apiKey | String? | Removed in v2 — kept only so an upgrade throws with an instruction instead of silently sending an unclassifiable key. |
ApiKeyType · ParsedApiKey · parseApiKey · maskApiKey
Key parsing and misuse guards. These run before any network call, so the wrong kind of key fails immediately rather than working with more privilege than intended.
| Field | Type | Notes |
|---|---|---|
| ApiKeyType | enum | publishable | secret. Each carries .code — 'pk' or 'sk'. |
| ParsedApiKey.type | ApiKeyType | Which authority the key carries. |
| ParsedApiKey.env | String | 'live' or 'test'. |
| ParsedApiKey.prefix | String | The lookup handle, e.g. 'pk_live_a1b2c3d4'. Not secret — safe to log. |
| parseApiKey(raw) | ParsedApiKey? | null for a missing or malformed key. |
| maskApiKey(raw) | String | Prefix plus the last four characters. |
1// Parse a key locally, without contacting the API.
2final parsed = parseApiKey('pk_live_a1b2c3d4_…');
3
4parsed?.type; // ApiKeyType.publishable
5parsed?.env; // 'live'
6parsed?.prefix; // 'pk_live_a1b2c3d4' — the lookup handle, not a secret
7
8// null for a missing or malformed key, so you can reject it early.
9parseApiKey('nonsense'); // null
10
11// Render a key safely for logs.
12maskApiKey(key); // 'pk_live_a1b2c3d4…9999'Pagination
Page<T>
What every list and search returns. It extends Iterable<T>, so a page is its records.
| Field | Type | Notes |
|---|---|---|
| data | List<T> | The records — raw maps unless itemParser is supplied. |
| hasMore | bool | Whether the server holds further records. |
| nextCursor | String? | Pass back as cursor for the next page. null on the last page. |
| total | int | Records in this page — not how many exist. Reads totalFetched or totalItems, whichever the endpoint sent. |
| success | bool | The API's success flag. |
| message | String? | The API's message, when it sent one. |
| raw | Map<String, dynamic> | The undecoded response body, for anything not surfaced above. |
| Page.empty() | Page<T> | An empty page, useful as a seed value in UI state. |
1final page = await authix.getUserData(
2 GetUserDataParams(userId: userId, limit: 50),
3);
4
5// A Page IS an Iterable of its records.
6for (final record in page) {
7 print(record['title']);
8}
9
10page.length; // records in this page
11page.isEmpty; // from Iterable
12page.first; // from Iterable
13page[0]; // indexed access
14page.hasMore; // bool
15page.nextCursor; // String? — pass back as cursor
16page.total; // int — records in this page, as the API reported it
17page.raw; // Map<String, dynamic> — the undecoded body
18
19Page<Map<String, dynamic>>.empty(); // a seed value for UI statemaxPageLimit · defaultPageLimit · normalizeLimit
The server's bounds, exposed so your UI can respect them.
| Field | Type | Notes |
|---|---|---|
| maxPageLimit | const int | 100 — the ceiling on a single response. |
| defaultPageLimit | const int | 20 — what the API uses when limit is omitted. |
| normalizeLimit(limit) | int? | Clamps a requested page size. A value above 100 is clamped, not rejected; 0 or negative falls back to 20; null is left out so the server applies its own default. |
iteratePages · iterateItems · pages
Cursor traversal as a Stream. Every iterate* method on the client is built on these, and they guard against a page that claims hasMore but returns no cursor — without that check the traversal would refetch page one forever.
1// Every iterate* method returns a Stream, so rows render as they arrive.
2await for (final record in authix.iterateUserData(
3 GetUserDataParams(userId: userId),
4 maxPages: 10,
5)) {
6 print(record['title']);
7}
8
9// Walk any cursor-taking method yourself.
10await for (final page in authix.pages(
11 (cursor) => authix.getUserData(
12 GetUserDataParams(userId: userId, cursor: cursor),
13 ),
14)) {
15 print('${page.length} records');
16}ItemParser<T>
The factory signature the paginated methods accept to build your own models. Without one, T must be Map<String, dynamic>.
1typedef ItemParser<T> = T Function(Map<String, dynamic> json);
2
3// Your own model
4final notes = await authix.getUserData<Note>(
5 GetUserDataParams(userId: userId),
6 itemParser: Note.fromJson,
7);
8
9// Or the built-in record shape, which types version for you
10final items = await authix.getUserData<DataItem>(
11 GetUserDataParams(userId: userId),
12 itemParser: DataItem.fromJson,
13);Auth parameters
SignupParams
Passed to signupUser.
| Field | Type | Notes |
|---|---|---|
| name | String | Required. |
| String | Required. | |
| password | String | Required. |
| username | String? | Optional handle. |
| phone | String? | Optional. |
| address | String? | Optional. |
| avatarUrl | String? | Optional. |
| isActive | bool? | Optional. |
| role | String? | Optional. |
| extra | Map<String, dynamic> | Any further fields your app stores on the user record. Defaults to empty. |
LoginParams
Passed to loginUser.
| Field | Type | Notes |
|---|---|---|
| String | Required. | |
| password | String | Required. |
UpdateUserParams
Passed to updateUser. userId travels in the path. appId is no longer a field — the client supplies it, so it can never disagree with the app the client is scoped to.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
| name · username · email · phone · address · avatarUrl | String? | Optional profile fields. |
| password | String? | Optional — bypasses the currentPassword check; prefer changePassword. |
| isActive | bool? | Optional. |
| role | String? | Optional. |
| settings · packageInfo · notifications · extraInfo | dynamic | Optional structured fields. |
| extra | Map<String, dynamic> | Any further fields to write. |
ChangePasswordParams
Passed to changePassword. userId travels in the path, so only the passwords are serialised.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
| currentPassword | String | Required — verified server-side. |
| newPassword | String | Required. |
DeleteUserParams
Passed to deleteUser.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
User record parameters
AddUserDataParams
Passed to addUserData.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
| dataCategory | String | Required. Lower-cased by the server. |
| data | Map<String, dynamic> | Required — flattened into the request body. |
| parentId | String? | Optional parent record, for order → line-item shapes. |
GetUserDataParams
Passed to getUserData and iterateUserData. Renamed from GetUserAllDataParams in v2; the old name remains as a deprecated alias.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
| category | String? | Restrict to one category. |
| parentId | String? | Restrict to the children of one record. |
| limit | int? | Page size. Defaults to 20 server-side, capped at 100. |
| cursor | String? | nextCursor from the previous page. Ignored by iterateUserData. |
GetSingleUserDataParams
Passed to getSingleUserData.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
| dataId | String | Required. |
SearchUserDataParams
Passed to searchInUserData.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
| q | String? | Substring match across the whole record. |
| category | String? | Restrict to one category. |
| keys | Map<String, dynamic>? | Exact field match, e.g. {'status': 'paid'}. |
| limit · cursor | int? · String? | Pagination. |
UpdateUserDataParams
Passed to updateUserData.
| Field | Type | Notes |
|---|---|---|
| userId | String | Required. |
| dataId | String | Required. |
| data | Map<String, dynamic> | Required — fields to write. Omitted fields are left alone. |
| version | int? | The version you last read. Omit it and the write always wins; supply it and a competing change raises VersionConflictError. |
DeleteUserDataParams · DeleteManyUserDataParams
| Field | Type | Notes |
|---|---|---|
| DeleteUserDataParams.userId · .dataId | String | Both required. |
| DeleteManyUserDataParams.userId | String | Required. |
| DeleteManyUserDataParams.dataIds | List<String> | Required — up to 100 ids, removed in one transaction. |
BatchParams · BatchOperation
Passed to batch(). Use the named constructors rather than raw maps — the shape each kind needs differs, and getting it wrong would otherwise be a runtime 400.
| Field | Type | Notes |
|---|---|---|
| BatchParams.userId | String | Required. |
| BatchParams.operations | List<BatchOperation> | Required — up to 50. |
| BatchOperation.create | {dataCategory, data, parentId?} | Insert a new record. |
| BatchOperation.update | {id, data, version?} | Update an existing record, optionally version-guarded. |
| BatchOperation.delete | {id} | Remove a record. |
App data parameters
AddAppDataParams
Passed to addAppData.
| Field | Type | Notes |
|---|---|---|
| dataCategory | String | Required — travels in the URL path. |
| data | Map<String, dynamic> | Required — sent as the body verbatim. No envelope, and no appId is merged in. |
GetAppDataParams · SearchAppDataParams
| Field | Type | Notes |
|---|---|---|
| GetAppDataParams | category?, limit?, cursor? | All optional. |
| SearchAppDataParams | q?, category?, keys?, limit?, cursor? | All optional. |
UpdateAppDataParams
Passed to updateAppData.
| Field | Type | Notes |
|---|---|---|
| dataId | String | Required. |
| data | Map<String, dynamic> | Required — sent as the body. |
| version | int? | Optional optimistic concurrency guard. |
DeleteAppDataParams
Passed to deleteAppData.
| Field | Type | Notes |
|---|---|---|
| dataId | String | Required. |
GetAllAppUsersParams · SearchAllAppUsersParams · GetAllAppUsersDataParams · SearchAllAppUsersDataParams
The across-all-users parameter objects. All fields optional.
| Field | Type | Notes |
|---|---|---|
| GetAllAppUsersParams | limit?, cursor? | Passed to getAllUsersFromApp and iterateAllUsers. |
| SearchAllAppUsersParams | q?, keys?, limit?, cursor? | keys is restricted to id, username, name, email, phone, address, role, isVerified, isActive. |
| GetAllAppUsersDataParams | category?, limit?, cursor? | Passed to getAllUsersDataFromApp. |
| SearchAllAppUsersDataParams | q?, category?, keys?, limit?, cursor? | Passed to searchInAllAppUsersData. |
Responses
CheckUserResponse
Returned by checkIfUserExists.
| Field | Type | Notes |
|---|---|---|
| success | bool | Backend success flag. |
| exists | bool | Whether the user is registered for this app. |
CheckSessionResponse
Returned by checkUserSession.
| Field | Type | Notes |
|---|---|---|
| authenticated | bool | Whether an end user is signed in on this client. |
| user | Map<String, dynamic>? | The signed-in user, when authenticated. |
| userId | String? | Shorthand for user?['id']. |
DataItem
One stored record, user or app-wide — they share a shape. Replaces AppDataItem, which remains as a deprecated alias.
| Field | Type | Notes |
|---|---|---|
| id | String | Record ID. |
| dataCategory | String | The category it was filed under. |
| version | int | Bumped on every write. Pass it back on update. |
| createdAt · updatedAt | DateTime? | Parsed timestamps, when the API sent them. |
| parentId | String? | Parent record, when created with one. |
| operator [] | dynamic | Read a payload field: item['title']. |
| payload | Map<String, dynamic> | The payload with the identity fields stripped out. |
| fields | Map<String, dynamic> | The full decoded record. |
| DataItem.fromJson | ItemParser<DataItem> | Pass it as itemParser to get typed records from a Page. |
DataItemResponse
Returned by every write that stores or changes a single record.
| Field | Type | Notes |
|---|---|---|
| success | bool | Backend success flag. |
| message | String? | The API's message, when it sent one. |
| data | DataItem? | The stored record. |
| id | String? | Shorthand for data?.id. |
| version | int? | Shorthand for data?.version. Keep it to update the record next. |
| raw | Map<String, dynamic> | The undecoded response body. |
Errors
AuthixException
The base type for every failure. NeuctraAuthixException remains as an alias, so v1 code that wrote `on NeuctraAuthixException` still compiles.
| Field | Type | Notes |
|---|---|---|
| message | String | Backend message, transport error, or a generic fallback. |
| status | int | HTTP status; 0 when no response was received. |
| code | String? | Machine-readable code from the API, e.g. 'VERSION_CONFLICT'. |
| payload | Map<String, dynamic> | The decoded response body. |
| isNetworkError · isAuthError | bool | Convenience predicates. |
The hierarchy
Failures are mapped onto the most specific subclass the status code and error code allow.
| Type | status | When |
|---|---|---|
| ConfigurationError | — | Bad client setup or a missing required argument. Thrown before any request. |
| NetworkError | — | Connection failure, timeout, or DNS error. |
| ValidationError | 400 | Rejected as invalid, including a filter on a non-searchable field. |
| AuthenticationError | 401 | Missing, malformed, expired or revoked credential. |
| NoUserSessionError | 401 | Subclass of AuthenticationError — the endpoint acts on the signed-in user, but nobody is signed in. |
| InsufficientScopeError | 403 | Publishable key on a secret-key endpoint. Carries .hint. |
| PermissionDeniedError | 403 | Authenticated but not permitted — unverified account, plan limit reached. |
| NotFoundError | 404 | The app, user, or record does not exist under this account. |
| VersionConflictError | 409 | The record changed since you read it. Carries .currentVersion and .expectedVersion; nothing was written. |
| RateLimitError | 429 | Rate limited or quota exhausted. Carries .resetDate. |
| ServerError | 5xx | The API failed to process the request. |
1try {
2 await authix.updateUserData(
3 UpdateUserDataParams(
4 userId: userId,
5 dataId: dataId,
6 data: {'status': 'shipped'},
7 version: 3,
8 ),
9 );
10} on VersionConflictError catch (e) {
11 e.currentVersion; // int? — where the record is now
12 e.expectedVersion; // int? — what you sent
13} on InsufficientScopeError catch (e) {
14 e.hint; // String? — which credential the endpoint needs
15} on RateLimitError catch (e) {
16 e.resetDate; // String? — when the quota resets
17} on AuthixApiError catch (e) {
18 e.status; // int
19 e.code; // String? — 'VERSION_CONFLICT', 'INSUFFICIENT_SCOPE', …
20 e.payload; // Map<String, dynamic> — the decoded body
21} on AuthixException catch (e) {
22 // The base type — catches configuration and network failures too.
23 e.message;
24}Session
SessionStore
The cookie jar that stands in for the browser's automatic cookie handling. It lives in memory — persist sessionCookie yourself.
1authix.session.hasSession; // bool
2authix.session.sessionCookie; // String? — persist this
3authix.session.cookies; // Map<String, String>, unmodifiable
4authix.session.cookieHeader; // String? — what gets sent
5
6authix.session.setSessionCookie(v); // restore a persisted session
7authix.session.setCookie(name, v); // seed any cookie
8authix.session.clearSession(); // drop the Neuctra Authix session cookie only
9authix.session.clear(); // drop everything
10
11SessionStore.sessionCookieName; // 'authix_user_session'On Flutter Web this stays empty
Pass a BrowserClient with withCredentials = true and the browser holds the HTTP-only cookie itself. session.hasSession will read false even while the user is signed in — trust checkUserSession() instead.
Escape hatch
SDKRequestOptions · rawRequest
Reach an endpoint the SDK does not wrap, with the same headers, cookie handling and error mapping.
| Field | Type | Notes |
|---|---|---|
| method | String | Required. 'GET', 'POST', 'PATCH', … |
| path | String | Required. Appended to baseUrl. |
| data | Map<String, dynamic>? | JSON body. Sent on every method except GET. |
| query | Map<String, dynamic>? | Query parameters. null values are dropped. |
| extraHeaders | Map<String, String> | Extra headers. Defaults to empty. |
| injectAppId | bool | Merge the configured appId into the body. Defaults to true — turn it off for routes that carry the app id in their path, or it lands in the stored payload. |
1final result = await authix.rawRequest(
2 const SDKRequestOptions(
3 method: 'POST',
4 path: '/custom/endpoint',
5 data: {'foo': 'bar'},
6 query: {'verbose': true},
7 extraHeaders: {'x-trace-id': 'abc123'},
8 injectAppId: true,
9 ),
10);appId is overridable
On routes that inject it, request bodies are built as {appId, ...data}, so a field literally named appId inside your data wins over the configured one. Avoid appId, dataCategory, parentId and version as field names in records you store.
Differences from the JavaScript SDK
- Errors are a typed class hierarchy, not plain thrown objects — catch the case you handle instead of switching on a status code.
- Cursor traversal is exposed as a
Stream, so rows can render as they arrive. logoutUserdoes not reload the page — it calls youronLoggedOutcallback, and clears the local session even if the server call fails.- A malformed or mismatched key throws at construction rather than on the first request.
- Nothing is ever written to the console.
Related