Flutter SDK reference
The public classes you use day to day, what each is for, and the one thing about it that surprises people.
The public API you use day to day. If you find yourself importing from src/, tell us what you needed and we will export it properly.
Full generated dartdoc lands on pub.dev with the first published release. This page is the map.
You rarely write these calls yourself: each task opens with the prompt that has your coding agent write them. This page is what they rely on.
Rendel
The facade. One init per app run.
| Member | Signature | Notes |
|---|---|---|
init | Future<void> init({required String appKey, RendelTheme? theme, List<RendelAction> actions, RendelConfig config}) | Awaited. A key that does not start with rd_pk_ asserts in debug and reports through onLog in release, then disables the copilot for the run — a typo used to cost a silent afternoon. |
open | Future<void> open(BuildContext context) | Presents the copilot as a full-height sheet. While it is already up, or shrunk to the bubble after a screen it opened, it returns or restores that one rather than stacking a second (0.38). Returns without presenting when the copilot is disabled. |
close | void close(BuildContext context) | — |
reset | void reset() | Call on logout. Clears the conversation and the user, so the next turn is anonymous. Safe to call while the sheet is up. |
registerAction | void registerAction(RendelAction action) | Re-syncs the manifest. |
registerRoute | void registerRoute(String name, {required String title, required String description, Map<String, RendelRouteParam> params, required RendelRouteOpener open}) | A screen of your app the copilot may offer on a screens card. name is snake_case. title (1-60 characters) is your app's own name for the screen, drawn under the model's title. description (1-300) tells the model when it is the right screen. Up to 8 params, 50 routes per app. open runs your own navigation once the sheet is out of the way, with the copilot waiting in a bubble. Re-syncs the manifest. Safe before init. |
addContextProvider | void addContextProvider(String key, Future<Map<String, dynamic>> Function() provider) | Called per turn, with a timeout. A slow provider costs its own context, never the turn. |
identify | Future<void> identify({required String userId, Map<String, dynamic>? traits, String? userHmac}) | See Context. |
pair | Future<RendelPairResult> pair(String code) | Lets the console watch this device's conversation, read only, for two hours. Test keys only; a code works once, for ten minutes. Never throws: the result carries a sentence to show. Safe before init. See Pair a test app. |
refreshConfig | Future<void> refreshConfig() | Re-reads the published appearance. The SDK does not hook the app lifecycle itself; call this on foreground if you want a publish to land without a relaunch. |
events | Stream<RendelEvent> | Broadcast. |
theme / config | RendelTheme / RendelConfig | The effective values, after any published config is layered over yours. |
strings | RendelStrings | The words the SDK is drawing in: yours, or the shipped set for the answer language. |
configRevision | ValueNotifier<int> | Ticks when a published config lands and changes what is on screen. |
isEnabled | bool | False when you turned it off in code or the console published a kill switch. |
isInitialized | bool | — |
deviceId | String? | The anonymous per-install id this run is sending. Null before init completes. It is what Support asks for. |
RendelConfig
Everything that is your choice rather than the protocol's. Every field is also the floor for anything published from the console.
| Field | Default | Notes |
|---|---|---|
apiBaseUrl | https://api.rendel.ai | — |
userToken | null | Future<String?> Function(): the signed-in person's current token from your own sign-in. Asked for before every question and never stored. See Signed-in user. |
requestTimeout | 30 s | Non-streaming calls. |
streamIdleTimeout | 45 s | How long a turn may go without a single server event. Server pings every 15 s, and the SDK counts them. |
sdkVersion | flutter/<package version> (flutter/0.46.3 today) | Sent as client.sdk. |
languages | [] | The languages this app is willing to be answered in, in preference order — ['tr', 'en', 'ru']. The device picks from among them; one speaking none of them gets the first, so the order is a decision. Empty hands the choice to the list published from the console, and absent that the copilot answers in whatever it is addressed in. Different from locale, which is one device's setting. |
locale | device's own | BCP-47 (tr, pt-BR). The language the copilot answers in, and the one confirmTemplates is matched on. A message written in another language is still answered in that one. See Locales. |
assistantName | Copilot | The name at the top of the copilot's sheet. |
launcherLabel | Ask | The word on the launcher. |
suggestions | [] | Opening prompts. Name things this app can do, in the user's words. |
emptyStateTitle | null | The headline shown before the first question, in your own words. Null keeps the SDK's, translated into the language the copilot answers in. Publishable from Appearance. |
emptyStateBody | null | The line under that headline, shown before the first question. |
composerHint | null | The words in the empty message box. Null uses RendelStrings.composerHint, which is translated. Publishable. |
attachIcon / voiceIcon / sendIcon | plus / microphone / arrowUp | RendelAttachIcon (paperclip, image, plus, off), RendelVoiceIcon (microphone, waveform, off), RendelSendIcon (arrowUp, paperPlane, arrowRight). off removes that control. Publishable. |
enabled | true | The kill switch in code. Setting it false wins over anything published. Not a security control — the API enforces suspension server-side. |
remoteConfig | true | False pins the copilot to exactly what this object says. |
configStore | null | Where the last good published config is kept between launches. |
deviceIdStore | null | Where the device id is kept between launches. Use a different key from configStore — both sides read and write one string. |
onPickAttachment | built in | Returns an RendelAttachment (bytes, name, media type) from your own picker, replacing the SDK's Photos, Camera and Files; the "+" then opens yours directly. Null uses the built-in. attachIcon: RendelAttachIcon.off removes the control. |
onStopDictating | null | Ends a dictation when the user presses the microphone again. Without it the control cannot be pressed mid-session and the recogniser ends the session itself. |
onDictate | built in | Returns recognised speech as text from your own recogniser, replacing the SDK's speech_to_text; it is handed an onPartial callback to feed the composer while somebody is still speaking. Return null when nobody spoke and throw when the recogniser failed: the SDK words the two differently and logs the error. Null uses the built-in. voiceIcon: RendelVoiceIcon.off removes the control. |
onOpenUrl | null | Null opens a card's link in an in-app browser, so the user comes back to the conversation when they close it. Set it to route links yourself, for example to your own product screen. |
bubbleOffset | 104 | Logical pixels above the bottom edge for the bubble while a screen the copilot opened is showing, so it clears your bottom bar. Never less than the safe area. |
strings | by locale | Every word the SDK puts on screen that is not your app's or the model's. Null takes them from locale — Türkçe, Deutsch, Français, Español, العربية, English elsewhere. Set it and you own every word. RendelStrings.forLocale('tr').copyWith(...) changes only the words you name and keeps the rest of that language (before 0.44.1 it returned most words in English). |
onLog | null | Point it at your crash reporter. |
httpClient | null | Test seam: pass a MockClient from package:http/testing.dart to run manifest sync, turns and action dispatch offline. Leave it null in production. |
RendelAction
What the copilot is allowed to do.
| Field | Type | Notes |
|---|---|---|
name | String | snake_case, asserted. One camelCase name used to reject the entire manifest. |
description | String | The model reads this to decide when to call it. Write it for a colleague, not for a compiler. |
params | RendelSchema | Validated server-side before an invocation exists. |
risk | RendelRisk | read runs silently; write and destructive get a server-built confirmation card. |
handler | RendelHandler? | Future<RendelResult> Function(Map<String, dynamic> params) |
onInvoke | RendelInvocationHandler? | Future<RendelResult> Function(RendelInvocation) — the same thing plus the invocation's id and risk, for idempotency on your side. |
confirmTemplate | String? | Overrides the server's sentence on the confirm card. |
confirmTemplates | Map<String, String>? | The same sentence per language, keyed by BCP-47 tag: {'tr': '{order_id} numaralı siparişi iptal edelim mi?'}. Matched on the locale the SDK sends: the exact tag, then the language, then confirmTemplate. Sixteen at most. |
timeout | Duration | Default 15 s, capped at 120 s. |
sandboxResult | Map<String, dynamic>? | Returned instead of running the handler on a test key. |
RendelSchema
Parameter shapes, without hand-writing JSON Schema.
RendelSchema.object({
'order_id': RendelSchema.string(description: 'The order number'),
'reason': RendelSchema.enumOf(['damaged', 'wrong_item', 'other']).optional,
'quantity': RendelSchema.integer().optional,
'tags': RendelSchema.list(RendelSchema.string()),
})string, number, integer, boolean, enumOf, list, object, and raw for anything the helpers do not cover. .optional (or .orAbsent) on any of them.
RendelResult
RendelResult.ok(Map<String, dynamic> data) or RendelResult.fail(String error). The failure string reaches the model, so write it as something the model can act on: "no order with that number" beats "404".
RendelRisk
read, write, destructive. See Actions and risk levels.
RendelInvocation
id, name, params, risk. The id is stable across a retry of the same turn, so it is the natural idempotency key for whatever your handler does.
Take it with onInvoke when running an action twice would be worse than not running it: an app killed between running the handler and reporting the outcome leaves the server without a result, and the model may propose the same thing again.
Rendel.registerAction(RendelAction(
name: 'refund_order',
description: 'Refund an order to the original payment method',
params: RendelSchema.object({'order_id': RendelSchema.string()}),
risk: RendelRisk.destructive,
confirmTemplate: 'Refund order {order_id}? This cannot be undone.',
onInvoke: (invocation) async {
// The same id comes back if the turn is resumed after a dropped stream.
if (await refunds.alreadyProcessed(invocation.id)) {
return RendelResult.ok({'status': 'already_refunded'});
}
return RendelResult.ok(await refunds.create(
orderId: invocation.params['order_id'] as String,
idempotencyKey: invocation.id,
));
},
));RendelRouteParam
RendelRouteParam.string(), .number(), .boolean(), each taking description, required and fromActions. With fromActions: true the value must be one an action returned in this conversation, so a guessed order id never opens the screen.
Rendel.registerRoute(
'order_detail',
title: 'My orders › Order',
description: 'One order: its items, delivery and payment.',
params: {'order_id': RendelRouteParam.string(required: true, fromActions: true)},
open: (p) => navigatorKey.currentState!.pushNamed('/orders/${p['order_id']}'),
);open is a RendelRouteOpener, FutureOr<void> Function(Map<String, Object?> params), and gets the params the server checked.
RendelLauncher
RendelLauncher({String? label}). Draws nothing when the copilot is uninitialised or disabled, so you can leave it in the tree unconditionally.
RendelTheme
See Theming. Built with RendelTheme.fromTokens(...), which derives readable text and border roles from your four colours rather than trusting them — a themeable SDK that does not do this ships unreadable badges.
RendelTheme.paper() and RendelTheme.deep() are the two built-in themes; fromTokens(...) builds yours. RendelThemeScope.of(context) reads the theme inside a custom component. contrastRatio and auditContrast check your colours.
RendelConfigStore
abstract class RendelConfigStore {
Future<String?> read();
Future<void> write(String value);
}One interface, two uses: RendelConfig.configStore and RendelConfig.deviceIdStore. By default both are kept in the app's preferences (shared_preferences). RendelMemoryConfigStore is the in-memory option, and the default under flutter test.
RendelLogRecord, RendelLogLevel, RendelLog
Where the SDK reports what it could not tell you any other way.
code | When |
|---|---|
invalid_key | appKey does not start with rd_pk_ (or nc_pk_, a key made before the rename). |
handler_failed | An action handler threw. |
handler_timeout | A handler passed its timeout. |
context_provider_timeout | A context provider missed its 300 ms budget. |
context_provider_failed | A context provider threw. |
manifest_sync_failed | The server rejected the manifest. The message carries the field and the reason. |
transport_failed | A network call failed. |
config_unreadable | A published config could not be parsed, or was not legible on this device. |
config_cache_write_failed | The config store threw on write. |
device_id_ephemeral | No deviceIdStore is set, so the device id is new each launch. Debug level, once. |
device_id_store_failed | The store threw or hung. |
usage_description_missing | Info.plist lacks NSMicrophoneUsageDescription or NSSpeechRecognitionUsageDescription, so the built-in microphone is not drawn, or NSCameraUsageDescription, so the "+" offers no Camera. iOS would close the app the first time either was used. |
user_store_failed | The signed-in user could not be stored or read back, so a relaunch starts signed out until the next identify. |
call_failed / mail_failed / copy_failed / calendar_failed | The dialler, mail app, clipboard or calendar editor could not be opened for a call, email, copy or add_to_calendar tap. |
open_url_failed / open_external_failed | A link could not be opened in the in-app browser, or could not be handed to the system. |
route_open_failed | A registered route's open threw. |
dictation_failed | The recogniser failed: your onDictate threw, or the built-in one could not start. |
user_token_failed | userToken threw; the turn went without a token. |
surface_stale | Debug: a block arrived for a surface_id with a revision no newer than the one on screen, and was dropped. |
paired | Debug: pair was accepted; the console watches until the time in the message. |
pair_test_only / pair_not_found / pair_rate_limited / pair_failed | Why pair did not pair: a live key; a wrong, used or expired code; too many tries in a minute; anything else. The web SDK uses the same codes. |
code is stable and safe to switch on; message is for humans and may be reworded.
RendelEvent
opened, closed, messageSent, actionExecuted. For your own analytics.
Rendering your own component
ComponentRegistry.register(String type, ComponentBuilder builder) puts a widget of yours in the catalog's place. ComponentBuilder is Widget Function(BuildContext context, WireBlock block, RendelDispatcher dispatch). RendelActionButton, RendelChip, RendelControlShell, RendelOverline, RendelPressable, RendelPulse and RendelSwap are exported so your component presses, encloses and breathes like a built-in rather than like Material.
RendelDispatcher
How your component talks back. The SDK hands one to your builder; you never construct it. A member that is null means this build cannot do that, so draw that part as content.
| Member | Signature | Notes |
|---|---|---|
onAction | Future<bool> Function(UIAction) | Fires a tap; resolves false when the tap was dropped. |
canHandle | bool Function(UIAction) | Ask before drawing a tap surface; false means draw it as content. |
onSubmitForm | Future<RendelSubmitOutcome> Function(String formId, Map<String, Object> values) | Values are String, num, bool or List<String>. |
onSelect | Future<bool> Function(WireBlock, String id, String shown)? | A variant_picker or datetime_slots pick, sent as a selection. Null where nothing can be picked. |
onChoose | Future<bool> Function(WireBlock, List<String> ids, String shown, {String? otherText, Duration lit})? | A choice answer: ids, or otherText, or nothing for skip. |
onRate | Future<bool> Function(WireBlock, int score, {String? comment})? | A rating score and optional comment, posted to /v1/ratings with no turn. |
onPickFile | Future<RendelAttachment?> Function(RendelAttachSource)? | A form upload field's picker. Resolves null when the user backs out. |
fileSources | List<RendelAttachSource> | Where onPickFile can pick from. |
onUpload | Future<String> Function(RendelAttachment)? | The upload that resolves an upload id. Throws an exception whose toString() is the server's sentence. |
routeTitle | String? Function(String route)? | The app's title for a registered route, for screens. |
UIAction
A sealed union: ReplyAction, OpenUrlAction, NavigateAction, CallAction, EmailAction, CopyAction, AddToCalendarAction, ConfirmAction, CancelAction, AppActionRef. New kinds arrive in minor releases, so give any switch over it a wildcard (_ =>) case.
Names from before 0.32.0
Every NC*, NeonCopilot and Copilot* name is a deprecated typedef of its Rendel* replacement, and each goes at 1.0.