sayspec_flutter_recorder 0.9.0
sayspec_flutter_recorder: ^0.9.0 copied to clipboard
SaySpec Flutter recording SDK. Emits widget-level interaction evidence that SaySpec turns into Patrol tests.
SaySpec Flutter Recorder #
Instrument a Flutter app so the SaySpec desktop app can turn a narrated Android walkthrough into reusable Patrol tests.
Platform status: Android is supported. iOS recording has not yet been qualified. This package records only when a SaySpec capture session supplies a local endpoint and session credentials.
Features #
- Records taps, drags, route changes, lifecycle events, and explicit markers.
- Resolves widget keys, semantics, visible text, and bounds into ranked Patrol finder candidates.
- Captures bounded screenshots from an app-provided
RepaintBoundary. - Uses monotonic event timestamps and measured host clock calibration.
- Keeps failed event batches queued instead of silently losing evidence.
- Sends evidence only to the authenticated capture endpoint provided by the SaySpec desktop app.
Install #
flutter pub add sayspec_flutter_recorder
The SaySpec desktop app can preview and apply this integration to a selected Flutter project. To integrate it manually, initialize the recorder before runApp, expose a screenshot boundary, and register the recorder as a navigator observer:
import 'package:flutter/material.dart';
import 'package:sayspec_flutter_recorder/sayspec_flutter_recorder.dart';
final recordingSurfaceKey = GlobalKey();
const recordingUrl = String.fromEnvironment('SAYSPEC_RECORDING_URL');
const recordingSessionId = String.fromEnvironment('SAYSPEC_RECORDING_SESSION');
const recordingSessionToken = String.fromEnvironment('SAYSPEC_RECORDING_TOKEN');
const recordingProjectId = String.fromEnvironment('SAYSPEC_RECORDING_PROJECT');
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (recordingUrl.isNotEmpty) {
await SaySpecRecorder.instance.initialize(
SaySpecRecorderOptions(
baseUrl: recordingUrl,
projectId: recordingProjectId,
sessionId: recordingSessionId,
sessionToken:
recordingSessionToken.isEmpty ? null : recordingSessionToken,
appId: 'com.example.my_app',
screenshotBoundaryKey: recordingSurfaceKey,
autoStart: true,
),
);
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: <NavigatorObserver>[SaySpecRecorder.instance],
builder: (context, child) => RepaintBoundary(
key: recordingSurfaceKey,
child: child ?? const SizedBox.shrink(),
),
home: const Scaffold(body: Center(child: Text('Ready'))),
);
}
}
When SaySpec launches the instrumented app, it supplies the four SAYSPEC_RECORDING_* values as Dart defines and forwards the device loopback port to its local capture server. Without SAYSPEC_RECORDING_URL, the example leaves the recorder disabled and the app runs normally.
Add stable keys and semantic labels to important controls so generated finders are resilient:
FilledButton(
key: const ValueKey<String>('checkout_submit'),
onPressed: submitOrder,
child: const Text('Place order'),
)
Use markers when an application event should appear alongside user interactions:
await SaySpecRecorder.instance.mark('cart updated');
Evidence and privacy #
The recorder posts evidence to the capture URL supplied at launch. SaySpec uses an ephemeral bearer token and an Android port forward to a loopback-only desktop listener. The SDK does not contain a cloud endpoint and does not upload evidence by itself.
Screenshots are optional. Set captureScreenshots: false, or omit screenshotBoundaryKey, to record interaction identity and timing without image evidence.
Limitations #
- The app must be built with this package; it cannot instrument an arbitrary production binary.
- Only Android recording has been qualified for this release.
- A generated finder is evidence, not a final assertion. Review generated Patrol tests before saving or running them.
- Screenshot capture is bounded by
maxScreenshotsandmaxScreenshotBytes.
For the SaySpec product and desktop recorder, visit sayspec.app.