Supabase Postgres 适配器
tracelet_supabase 包提供了一个嵌入式适配器,可通过 Postgres RPC 函数 或 Supabase Edge Function 将后台位置直接同步到 Supabase Postgres。
安装
将适配器添加到您的 pubspec.yaml:
flutter pub add tracelet_supabase该适配器使用 supabase_flutter。使用 Supabase 客户端之前必须对其进行初始化。
它是如何运作的
适配器插入 Tracelet 的电池高效 本机 HTTP 同步引擎:buildHttpConfig 生成指向您的 Supabase REST 端点的 HttpConfig — /rest/v1/rpc/<function> (RPC) 或 /functions/v1/<function>(边缘函数) — 并为您设置 apikey 和 Authorization: Bearer <token> 标头。
由于同步在本机引擎中运行,因此即使在应用程序强制退出时也可以通过指数退避和离线排队进行批量上传。在 401 上,configureTokenRefresh 刷新 Supabase 会话(在前台和无头后台隔离中)并重试。您提供恰好一个目标:rpcFunction 或 edgeFunction。
设置和配置
创建一个表和一个摄取函数
创建一个表来接收位置,启用 RLS,并添加插入传入批次的 Postgres 函数。 Tracelet 发布了一批位置对象,因此该函数接受 jsonb 数组:
-- Table
CREATE TABLE public.locations (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
user_id uuid REFERENCES auth.users NOT NULL DEFAULT auth.uid(),
latitude double precision NOT NULL,
longitude double precision NOT NULL,
accuracy double precision,
speed double precision,
timestamp timestamptz NOT NULL,
is_moving boolean,
created_at timestamptz DEFAULT now()
);
ALTER TABLE public.locations ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users insert their own locations" ON public.locations
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- RPC function: receives the batch as a jsonb array and inserts each row.
-- (The exact field names come from Tracelet's sync payload — see the
-- Tracelet Sync docs for the payload schema.)
CREATE OR REPLACE FUNCTION public.insert_tracelet_locations(payload jsonb)
RETURNS void
LANGUAGE sql
SECURITY INVOKER
AS $$
INSERT INTO public.locations (latitude, longitude, accuracy, speed, timestamp, is_moving)
SELECT
(e->'coords'->>'latitude')::double precision,
(e->'coords'->>'longitude')::double precision,
(e->'coords'->>'accuracy')::double precision,
(e->'coords'->>'speed')::double precision,
(e->>'timestamp')::timestamptz,
(e->>'is_moving')::boolean
FROM jsonb_array_elements(payload) AS e;
$$;更喜欢更多自定义处理(验证、扇出、丰富)?相反,部署 Supabase Edge Function 并将其名称传递为 edgeFunction — 适配器会将批次发布到 /functions/v1/<name>。
初始化Supabase
在 main.dart 中初始化 Supabase 客户端:
import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
url: 'https://xyzcompany.supabase.co',
anonKey: 'public-anon-key',
);
runApp(const MyApp());
}配置令牌刷新并构建 HTTP 配置
调用 configureTokenRefresh() 一次,构建针对您的 RPC(或 Edge Function)的 HttpConfig,并将其传递给 Tracelet.ready:
import 'package:tracelet_supabase/tracelet_supabase.dart';
import 'package:tracelet/tracelet.dart';
const supabaseUrl = 'https://xyzcompany.supabase.co';
const supabaseAnonKey = 'public-anon-key';
// 1. Keep the Supabase session token fresh in the background (resolves 401s).
await TraceletSupabase.configureTokenRefresh(anonKey: supabaseAnonKey);
// 2. Build the native HTTP config. Provide EITHER rpcFunction OR edgeFunction.
final httpConfig = TraceletSupabase.buildHttpConfig(
supabaseUrl: supabaseUrl,
anonKey: supabaseAnonKey,
rpcFunction: 'insert_tracelet_locations', // ...or: edgeFunction: 'tracelet-ingest'
// autoSync: true, batchSync: true, maxBatchSize: 250 are the defaults
);
// 3. Start Tracelet with the adapter's HTTP config.
await Tracelet.ready(Config(
geo: const GeoConfig(distanceFilter: 50),
http: httpConfig,
));
await Tracelet.start();附加自定义元数据
要使用业务数据标记每个位置(例如 driver_id 或 trip_id),请使用 setRouteContext() — 它通过 SQLite 队列与每个位置一起传输,并包含在您的函数接收的有效负载中:
await Tracelet.setRouteContext(RouteContext(
taskId: 'order_123',
custom: {'trip_id': 'trip_456'},
));表现
同步通过 Tracelet 的批处理引擎运行,因此离线点的积压会批量刷新(默认 maxBatchSize: 250),而不是每个点一个请求。与 RPC 内的 Postgres 批量插入相结合,这可以在设备电池和数据库往返过程中保持高频跟踪(例如每 10 m 一个点)的效率。