SMKitUI Flutter Plugin (v1.5.2)
A Flutter plugin for integrating Sency's SMKitUI SDK, supporting both iOS and Android. Run fitness, body360, strength, cardio, and custom assessments and workouts with unified user data, summary options, skeleton visualization, and runtime modifications.
Requirements
- Flutter: 3.44.0+
- Dart: 3.12.0+
- iOS: 16.0+, SMKitUI 2.0.0, SMKit 2.0.0
- Android: minSdk 26, compileSdk 36, SMKitUI 1.6.8, SMKit 1.6.8, Gradle 8.14, AGP 8.13.2, Kotlin 2.2.20, Java 17
Getting Started
1. Configure
final _smkitUiFlutterPlugin = SmkitUiFlutterPlugin();
await _smkitUiFlutterPlugin.configure(
key: apiPublicKey,
poseModelChoice: SmKitPoseModelChoice.adaptiveChoice,
includesHighlights: false, // iOS only
);
Available Android pose choices:
adaptiveChoice, prime, pro, lite, ultraLite, basic.
On iOS this parameter is accepted for API parity and currently ignored.
2. Apply SDK Config (Optional)
await _smkitUiFlutterPlugin.setConfig(
config: SMKitConfig(
enableIntelligenceRest: true,
startTimerOnFirstActivity: true,
workoutContinuationTimerDuration: 10,
colorTheme: SMKitColorTheme.green,
playPhoneCalibrationAudio: true,
allowAudioMixing: false,
instructionVideoConfig: InstructionVideoConfig(
displayMode: InstructionVideoDisplayMode.mediumCycle,
mediumSizeCycles: 2,
),
skeletonConfig: SkeletonConfig(preset: SkeletonPreset.neonGlow),
pauseTypes: [SMKitPauseType.resume, SMKitPauseType.quit],
),
);
3. Set Preferences (Optional)
_smkitUiFlutterPlugin.setSessionLanguage(language: SMKitLanguage.english);
_smkitUiFlutterPlugin.setCounterPreferences(counterPreferences: SMKitCounterPreferences.perfectOnly);
_smkitUiFlutterPlugin.setEndExercisePreferences(endExercisePrefernces: SMKitEndExercisePreferences.targetBased);
4. Start an Assessment
_smkitUiFlutterPlugin.startAssessment(
type: AssessmentTypes.body360, // fitness, body360, strength, cardio, custom
userData: {
'gender': 'Male', // or 'Female', 'Idle'
'birthday': DateTime(1990, 1, 1).millisecondsSinceEpoch,
},
showSummary: true,
forceShowUserDataScreen: false,
modifications: {
'primaryColor': '#4CAF50',
'phoneCalibration': {'enabled': true},
},
onHandle: (status) {
if (status.operation == SMKitOperation.assessmentSummaryData) {
final summary = status.data as SMKitAssessmentSummaryData;
} else if (status.operation == SMKitOperation.error) {
final error = status.data as SMKitError;
}
},
);
5. Start a Customized Workout
_smkitUiFlutterPlugin.startCustomizedWorkout(
workout: SMKitWorkout(
id: '1',
name: 'My Workout',
exercises: [
SMKitExercise(
detector: 'SquatRegular',
prettyName: 'Squat',
totalSeconds: 30,
uiElements: [SMKitUIElement.repsCounter, SMKitUIElement.timer],
shortIntro: true,
adaptiveRomFeedbackEnabled: true,
),
],
continuation: SMKitWorkoutContinuation(
interactionUnlockSoundKey: 'ContinueWorkoutPrompt',
exercises: [
SMKitExercise(detector: 'Rest', prettyName: 'Rest', totalSeconds: 10),
],
),
),
modifications: {'primaryColor': '#2196F3'},
onHandle: (status) { ... },
);
6. Start a Customized Assessment
final targetReps = 5;
await _smkitUiFlutterPlugin.setEndExercisePreferences(
endExercisePrefernces: SMKitEndExercisePreferences.targetBased,
);
_smkitUiFlutterPlugin.startCustomizedAssessment(
assessment: SMKitWorkout(
id: 'assessment-1',
name: 'Target Reps Assessment',
exercises: [
SMKitExercise(
detector: 'SquatRegular',
prettyName: 'Squat',
totalSeconds: 30, // still required by the native model
uiElements: [SMKitUIElement.timer, SMKitUIElement.repsCounter],
scoringParams: ScoringParams.targetReps(
targetReps: targetReps,
scoreFactor: 0.5,
),
),
],
),
showSummary: true,
forceShowUserDataScreen: false,
modifications: {'primaryColor': '#FF9800'},
onHandle: (status) { ... },
);
For rep-scored dynamic assessments, keep SMKitUIElement.timer in
uiElements. Native SMKitUI displays completed/target reps in that slot and
ends the exercise when currentReps >= targetReps.
7. Start a Workout Program
_smkitUiFlutterPlugin.startWorkoutProgram(
config: WorkoutConfig(
programId: 'my-program-id',
week: 1,
bodyZone: BodyZone.fullBody,
difficultyLevel: DifficultyLevel.midDifficulty,
workoutDuration: WorkoutDuration.short,
language: SencySupportedLanguage.english,
shortIntro: true,
phonePosition: SMKitPhonePosition.floor, // iOS only
),
modifications: {'primaryColor': '#4CAF50'},
onHandle: (status) { ... },
);
SMKitConfig Reference
| Field | Type | Description |
|---|---|---|
enableIntelligenceRest |
bool? |
Enable AI-based rest detection |
enableHeartRateRest |
bool? |
Enable Apple Watch heart-rate rest suggestions on iOS |
heartRateRestThreshold |
int? |
iOS heart-rate threshold for rest suggestions |
allowAudioMixing |
bool? |
Allow app audio to mix with SDK audio |
showExternalAudioControl |
bool? |
Show external audio control button |
playPhoneCalibrationAudio |
bool? |
Play phone calibration audio |
playBodyCalibrationAudio |
bool? |
Play body calibration audio |
enableButtonTutorial |
bool? |
Enable hover-button tutorial |
buttonTutorialCompletionAudioPath |
String? |
Local or URL audio for tutorial completion |
enableWatchCompanion |
bool? |
Enable Apple Watch companion |
accuratePoseEstimation |
bool? |
Enable high-accuracy pose estimation |
showRowingPhoneCalibration |
bool? |
Show rowing phone calibration screen |
enablePhoneMovementCountPrevention |
bool? |
Block counting while phone movement is detected |
enableVariationMismatchFeedback |
bool? |
Play mismatch feedback for static/dynamic variation errors |
startTimerOnFirstActivity |
bool? |
Start timer only after first rep or in-position frame |
workoutContinuationTimerDuration |
int? |
Continuation prompt timer in seconds |
colorTheme |
SMKitColorTheme? |
Direct SDK theme selection |
skeletonConfig |
SkeletonConfig? |
Skeleton visualization settings |
pauseTypes |
List<SMKitPauseType>? |
Pause overlay buttons |
instructionVideoConfig |
InstructionVideoConfig? |
Instruction video display mode |
useDefaultGuidanceMode |
bool? |
Android guidance default mode |
guidanceDebugLogging |
bool? |
Native guidance debug logging |
androidConfigString |
String? |
Android native flat config string |
showDebugBoundingBox |
bool? |
iOS debug bounding box |
Runtime Controls
await _smkitUiFlutterPlugin.quitWorkout();
await _smkitUiFlutterPlugin.pauseSdk(); // iOS only
await _smkitUiFlutterPlugin.resumeSdk(); // iOS only
await _smkitUiFlutterPlugin.clearAdaptiveRomCache(); // Android only
await _smkitUiFlutterPlugin.setFeedbacksUiToExclude(
feedbacks: ['SquatHipCreaseDepth'],
);
await _smkitUiFlutterPlugin.setExcludedFeedbacks(
feedbacks: ['SquatHipCreaseDepth'], // iOS only
);
final movements = await _smkitUiFlutterPlugin.getSupportedMovements(); // iOS only
final type = await _smkitUiFlutterPlugin.getExerciseType(detector: 'SquatRegular'); // iOS only
SkeletonConfig
Customize skeleton overlay: preset, connectionStyle, jointShape, dotsOpacity, connectionsOpacity, dotsInnerColor, dotsOuterColor, dotsGlow, connectionsGlow, lineWidthScale, outlineScale, softness, animationDuration, hidden.
Modifications Map
| Key | Type | Description |
|---|---|---|
primaryColor |
String |
Hex color for UI theme (maps to green/blue/orange/purple/silver/gold/pink) |
phoneCalibration.enabled |
bool |
Show/hide phone calibration screen |
Platform Notes
forceShowUserDataScreenis iOS-only.pauseSdk,resumeSdk,setExcludedFeedbacks,getSupportedMovements,getExerciseType,includesHighlights,WorkoutConfig.phonePosition, andshowDebugBoundingBoxare iOS-only.clearAdaptiveRomCache,useDefaultGuidanceMode,guidanceDebugLogging, andandroidConfigStringare Android-only.SMKitPauseType.restandswitchExerciseare Android-only.WorkoutConfigenums use PascalCase on the wire (FullBody,MidDifficulty,Short,English).- Native SDK 2.0.0/1.6.8 support English and Hebrew. The Dart Spanish enum remains for compatibility but is not advertised as supported.
Changelog
See CHANGELOG.md for details.
License
MIT