gyanmeet_flutter_sdk 1.4.0
gyanmeet_flutter_sdk: ^1.4.0 copied to clipboard
Gyanmeet meeting SDK for Flutter — a self-contained video meeting widget with host controls, chat, polls, and optional on-device AI proctoring.
example/lib/main.dart
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:gyanmeet_flutter_sdk/gyanmeet_flutter_sdk.dart';
import 'dev_join.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Gyanmeet Flutter SDK Example',
debugShowCheckedModeBanner: false,
home: JoinScreen(),
);
}
}
// Dev harness: enter API base + API key + room code, mint a token via the
// dev join flow, then embed GyanmeetMeeting with that token. In production the
// consumer's backend mints the token; the app never holds an API key.
class JoinScreen extends StatefulWidget {
const JoinScreen({super.key});
@override
State<JoinScreen> createState() => _JoinScreenState();
}
class _JoinScreenState extends State<JoinScreen> {
final _backend = TextEditingController(
text: 'https://pdot-dev-api.gyanmeet.com/api',
);
// Dev-only prefill for quick local testing — do NOT commit a real API key.
final _apiKey = TextEditingController(
text: '1e563b3247a77ded479b06ce767d3bbc63cd25401c3cbd4b0b66408c060a5365',
);
final _roomCode = TextEditingController(text: '3DG-W4W-VBK');
final _name = TextEditingController(text: 'Flutter Tester');
final _email = TextEditingController(text: 'dev@gyanbazzar.com');
String? _token;
bool _minting = false;
String? _error;
// Reference image for the identity gate / manual-enroll. Loaded from assets
// for testing; in production the consumer fetches it (backend photo, etc.).
Uint8List? _referenceImage;
@override
void initState() {
super.initState();
_loadReference();
}
Future<void> _loadReference() async {
try {
final data = await rootBundle.load('assets/reference/person_a.png');
if (mounted) {
setState(() => _referenceImage = data.buffer.asUint8List());
}
} catch (e) {
debugPrint('reference load failed: $e');
}
}
@override
void dispose() {
_backend.dispose();
_apiKey.dispose();
_roomCode.dispose();
_name.dispose();
_email.dispose();
super.dispose();
}
Future<void> _mintAndJoin() async {
setState(() {
_minting = true;
_error = null;
});
try {
final token = await devMintToken(
apiBase: _backend.text.trim(),
apiKey: _apiKey.text.trim(),
roomCode: _roomCode.text.trim(),
name: _name.text.trim(),
email: _email.text.trim(),
);
if (!mounted) return;
setState(() {
_token = token;
_minting = false;
});
} catch (e) {
if (!mounted) return;
setState(() {
_error = '$e';
_minting = false;
});
}
}
@override
Widget build(BuildContext context) {
if (_token != null) {
return ScaffoldMessenger(
child: Builder(
builder: (context) {
return GyanmeetMeeting(
backendUrl: _backend.text.trim(),
token: _token!,
config: const GyanmeetConfig(
videoMode: VideoMode.hostsOnly,
showPreJoin: true,
enableIdentityVerificationGate: true,
darkMode: true,
showParticipantsTab: false,
showPolls: false,
enableBreak: true,
),
identityReferenceImage: _referenceImage,
primaryColor: '#366D5A',
onJoined: (id, role) => debugPrint('joined $id as $role'),
onLeave: () => setState(() => _token = null),
onEnded: () => setState(() => _token = null),
onError: (e) => debugPrint('error: $e'),
onFacePresenceChanged: (isPresent) =>
debugPrint('Face present: $isPresent'),
onGazeViolation: (reason) =>
debugPrint('Gaze violation: $reason'),
onIdentityMismatch: (similarity) =>
debugPrint('Identity mismatch! similarity=$similarity'),
onProctoringViolation: (v) => debugPrint(
'PROCTORING VIOLATION: ${v.type} reason=${v.reason} sim=${v.similarity}',
),
);
},
),
);
}
return Scaffold(
appBar: AppBar(title: const Text('Gyanmeet SDK Example')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
children: [
_field(_backend, 'Backend URL'),
_field(_apiKey, 'API key (dev only)'),
_field(_roomCode, 'Room code'),
_field(_name, 'Display name'),
_field(_email, 'Email'),
const SizedBox(height: 20),
if (_error != null)
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Text(
_error!,
style: const TextStyle(color: Color(0xFFDC2626)),
),
),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _minting ? null : _mintAndJoin,
child: _minting
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Join meeting'),
),
),
],
),
),
);
}
Widget _field(TextEditingController c, String label) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TextField(
controller: c,
decoration: InputDecoration(labelText: label),
),
);
}