Skip to Content
Getting StartedFlutter SDK

Flutter SDK: Bridging Dart and Native

Tracelet is unique because it pushes almost all of the logic down to the native iOS and Android SDKs. Why? Because Dart isolates are heavy, and waking up a Flutter engine in the background every time the user takes a step destroys battery life.

This page explains how the Flutter layer interacts with the native engine in real-world scenarios.


Scenario 1: The UI Subscription

Concepts Explored: Event Streams, Headless Execution

The Problem

You are building a fitness app. When the user is looking at the screen, you want a little blue dot to move on a Google Map in real-time. When the user locks the screen, you still want to record the run to the database, but you don’t need to update a UI that nobody is looking at.

How Tracelet Solves It

Tracelet separates the Native Tracking Engine from the Dart UI Streams.

When you call Tracelet.start(), the native iOS/Android engine powers up. It handles motion detection, GPS filtering, SQLite saving, and HTTP syncing entirely in Swift/Kotlin.

In your Flutter UI, you simply subscribe to the stream:

import 'package:tracelet/tracelet.dart' as tl; void initializeTracking() async { // 1. Listen to events in the UI tl.Tracelet.onLocation((tl.Location location) { print('Update UI map marker: ${location.coords.latitude}, ${location.coords.longitude}'); }); tl.Tracelet.onMotionChange((tl.Location location) { print('User started/stopped moving: ${location.isMoving}'); }); // 2. Start the native engine await tl.Tracelet.ready(tl.Config.balanced()); await tl.Tracelet.start(); }

The Magic: If the user force-kills your app, the onLocation Dart stream dies. But the native tracking engine keeps running. It continues saving locations to SQLite. When the user re-opens the app later, you can query the SQLite database using your Database Adapter to render their route.


Scenario 2: The Onboarding Flow

Concepts Explored: Permission Handling, Rejections

The Problem

You are building an app for elderly patients to track wandering. You need “Always” background location permission. If you just throw the system permission dialog at them, they will get confused, tap “Deny”, and the app becomes useless.

How Tracelet Solves It

Tracelet never requests permissions automatically. It leaves the UI flow entirely up to your Flutter code, allowing you to show educational custom dialogs before triggering the scary OS prompts.

import 'package:tracelet/tracelet.dart' as tl; // 1. Check current status final status = await tl.Tracelet.requestLocationAuthorization(); // 2. The user permanently denied permission in the past if (status == tl.AuthorizationStatus.deniedForever) { // Show a beautifully designed Flutter dialog explaining WHY you need it. // Add a button that calls: await tl.Tracelet.openAppSettings(); return; } // 3. We haven't asked yet, or they selected "Ask Next Time" if (status == tl.AuthorizationStatus.notDetermined || status == tl.AuthorizationStatus.denied) { // Ask for foreground permission first final result = await tl.Tracelet.requestLocationAuthorization(); if (result == tl.AuthorizationStatus.whenInUse) { // They granted "When In Use" // Show a second Flutter dialog explaining why "Always" is needed. // Then upgrade the permission: await tl.Tracelet.requestLocationAuthorization(); } }

Tracelet.requestLocationAuthorization() returns an AuthorizationStatus enum:

  • notDetermined: You haven’t asked yet.
  • denied: User denied (can ask again on Android).
  • whenInUse: Granted foreground only.
  • always: Granted background access.
  • deniedForever: User checked “Don’t ask again” or iOS permanently denied. You MUST send them to settings.