appmint_flutter_client is a small package that handles the parts of talking to
AppEngine that are easy to get wrong: authenticating the app itself, keeping two
different tokens on the right headers, and the fact that signing somebody in has
three possible endings rather than two.
It is the same code the Appmint apps run on, extracted so you do not have to copy it.
Earlier guidance here was to copy the client pattern into your project rather than depend on a package. That advice cost us: four apps carried four hand-copied clients, and when the server began issuing verification challenges, two of them locked their users out with a sign-in loop. The code is short; the contract is not, and it changes. Depend on the package.
Getting the code
The package is not on pub.dev yet. Depend on it straight from the repository:
dependencies:
appmint_flutter_client:
git:
url: https://github.com/JacLight/appmint-client.git
path: appmint_flutter_clientPin it to a commit or tag once you are shipping:
git:
url: https://github.com/JacLight/appmint-client.git
path: appmint_flutter_client
ref: v0.1.0To read or change the source, clone it and use a path dependency instead:
git clone https://github.com/JacLight/appmint-client.gitdependencies:
appmint_flutter_client:
path: ../appmint-client/appmint_flutter_clientWorking examples live in a second repository — see Example apps.
Your first Appmint app builds a working app from an empty folder in about twenty minutes — sign-in, verification codes, a session and a real read. This page is the map; that one is the walk.
Connect once
final appmint = Appmint(const AppmintConfig(
baseUrl: 'https://appengine.appmint.io',
orgId: 'acme',
appId: 'acme-mobile',
appKey: '…',
appSecret: '…',
));That is the last time app authentication comes up. The client fetches the app
token on the first call, shares one fetch between concurrent callers, renews it
when it expires, retries only failures that never reached the server, and stamps
orgid and shared-org-id on everything.
Anyone can read them out of an APK or IPA. They identify your app; they do not protect it. What protects a request is the signed-in person's token. Do not build a permission model on the app key.
What it covers
| Area | What you get |
|---|---|
| App authentication | Token fetch, caching, renewal, retry — invisible |
| Sign-in | Staff and customer, password, magic code, passcode and NFC card |
| Verification | Challenges as a first-class result: verify, resend, switch method, cancel |
| Sessions | Restore at startup, refresh on 401, sign out, a change stream |
| Remembered accounts | One-tap sign-in per organization |
| Data | Reads and writes over any datatype, including your own |
| Anything else | appmint.http — same headers and tokens, any endpoint |
The shape of it
Your widgets → Appmint → auth ──┐
→ repository ──┤→ AppmintHttp → AppEngine
→ http ───────┘ │
SessionStoreAppmint is the one object to hold. It carries the session and the cached app
token, so construct it once and keep it.
No state-management package is imposed. appmint.auth.changes is a plain
Stream<AppmintUser?>; wrap it in whatever your app already uses.
Storage
Tokens go to the platform keystore, the rest to preferences. Swap it by
implementing SessionStore — MemorySessionStore ships for tests and kiosks
that should forget on close.
Signing out clears the tokens and the cached person and nothing else. It will not empty your preference store: an app keeps its own settings there, and wiping them on sign-out has in practice left a configured till unable to trade until somebody re-paired the hardware.
Offline
The client reads live; there is no cache and no queue of offline actions. What that means for an app that runs in basements and warehouses — and what to do about it — is in Flutter offline and caching.