samviva 1.0.0
samviva: ^1.0.0 copied to clipboard
A Flutter liveness detection library powered by Google ML Kit. Detect live users with configurable probability thresholds via active face challenges.
import 'package:flutter/material.dart';
import 'package:samviva/samviva.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Samviva Example',
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Samviva Demo')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const LivenessScreen()),
),
child: const Text('Start Liveness Check'),
),
),
);
}
}
class LivenessScreen extends StatelessWidget {
const LivenessScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SamvivaLivenessView(
challenges: const [
SamvivaLivenessChallenge.blink,
SamvivaLivenessChallenge.smile,
SamvivaLivenessChallenge.turnLeft,
],
// --- Detection thresholds ---
// Lower blinkEyeClosedThreshold = eyes must close more = stricter
// Higher smileThreshold = must smile more clearly = stricter
// Higher headTurnAngle = must turn head further = stricter
config: const SamvivaLivenessConfig(
blinkEyeClosedThreshold: 0.3,
smileThreshold: 0.75,
headTurnAngle: 20.0,
challengeTimeout: Duration(seconds: 5),
totalTimeout: Duration(seconds: 30),
),
onSuccess: (SamvivaLivenessResult result) {
// Show dialog while LivenessScreen is still active, then pop both
showDialog(
context: context,
barrierDismissible: false,
builder: (dialogContext) => AlertDialog(
title: const Text('Liveness Passed ✓'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Completed: ${result.completedChallenges.map((c) => c.name).join(', ')}',
),
Text('Time: ${result.timestamp}'),
if (result.capturedImage != null)
Padding(
padding: const EdgeInsets.only(top: 12),
child: Image.memory(result.capturedImage!, height: 160),
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(dialogContext);
Navigator.pop(context);
},
child: const Text('OK'),
),
],
),
);
},
onFailure: (SamvivaLivenessFailure failure) {
final messenger = ScaffoldMessenger.of(context);
Navigator.pop(context);
messenger.showSnackBar(
SnackBar(
content: Text('Failed: ${failure.message}'),
backgroundColor: Colors.red,
),
);
},
// Optional: monitor raw probabilities per frame
onChallengeUpdate: (SamvivaLivenessChallengeUpdate update) {
debugPrint(
'[samviva] ${update.challenge.name} '
'— prob: ${update.probability.toStringAsFixed(2)} '
'/ threshold: ${update.threshold} '
'— passed: ${update.isPassed}',
);
},
// Optional: override displayed text for localisation
labels: const SamvivaLivenessLabels(
blinkLabel: 'Blink',
smileLabel: 'Smile',
turnLeftLabel: 'Left',
turnRightLabel: 'Right',
blinkInstruction: 'Blink your eyes',
smileInstruction: 'Give us a smile',
turnLeftInstruction: 'Turn your head to the left',
turnRightInstruction: 'Turn your head to the right',
positionFaceText: 'Position your face in the oval',
noFaceText: 'No face detected — move closer',
holdStillText: 'Hold still…',
successText: 'Verification complete!',
failureText: 'Verification failed',
),
// Optional: override text styles
textStyles: const SamvivaLivenessTextStyles(
titleStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.white,
letterSpacing: 0.4,
),
instructionStyle: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.white,
),
stepLabelStyle: TextStyle(
fontSize: 10,
letterSpacing: 0.4,
),
countdownStyle: TextStyle(
fontSize: 96,
fontWeight: FontWeight.w900,
),
),
// Optional: customize colors to match your brand
theme: const SamvivaLivenessTheme(
activeColor: Color(0xFF4ADE80),
errorColor: Color(0xFFF87171),
overlayColor: Color(0xBB000000),
stepActiveColor: Colors.white,
stepPendingColor: Colors.white,
connectorIdleColor: Colors.white24,
),
),
);
}
}