gyanmeet_flutter_sdk 0.0.2
gyanmeet_flutter_sdk: ^0.0.2 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 'package:flutter/material.dart';
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://api.gyanmeet.com/api');
// Dev-only prefill for quick local testing — do NOT commit a real API key.
final _apiKey = TextEditingController(
text: 'bbc64569648d2ed797c95f6cb7d192d3345759d1d81924a0027ec74e9c8aa173');
final _roomCode = TextEditingController(text: 'N49-YT2-PFW');
final _name = TextEditingController(text: 'Flutter Tester');
final _email = TextEditingController(text: 'flutter@example.com');
String? _token;
bool _minting = false;
String? _error;
@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(showPreJoin: true),
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');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(isPresent ? 'Face Detected' : 'Face Lost'),
duration: const Duration(seconds: 1),
),
);
},
onGazeViolation: (reason) {
debugPrint('Gaze violation: $reason');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Gaze Violation: $reason'),
duration: const Duration(seconds: 1),
backgroundColor: Colors.orange,
),
);
},
);
}),
);
}
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),
),
);
}