flutter_banuba_ar 0.1.0
flutter_banuba_ar: ^0.1.0 copied to clipboard
Flutter plugin for the Banuba augmented reality SDK. Provides camera capture, AR effect loading/switching, and a stream of AR-processed frames (RGBA/BGRA) for use in live streaming, recording, or preview.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_banuba_ar/flutter_banuba_ar.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Banuba AR Example',
theme: ThemeData(
colorSchemeSeed: Colors.teal,
useMaterial3: true,
brightness: Brightness.dark,
),
home: const BanubaExamplePage(),
);
}
}
class BanubaExamplePage extends StatefulWidget {
const BanubaExamplePage({super.key});
@override
State<BanubaExamplePage> createState() => _BanubaExamplePageState();
}
class _BanubaExamplePageState extends State<BanubaExamplePage> {
final BanubaARController _banuba = BanubaARController();
bool _isInitialized = false;
bool _isCapturing = false;
String _currentEffect = 'None';
int _frameCount = 0;
String _frameInfo = 'No frames yet';
// Replace with your Banuba client token
static const String _banubaToken = 'YOUR_BANUBA_CLIENT_TOKEN';
@override
void initState() {
super.initState();
_initialize();
}
Future<void> _initialize() async {
final success = await _banuba.initialize(token: _banubaToken);
setState(() => _isInitialized = success);
// Listen for frames
_banuba.frameStream.listen((frame) {
_frameCount++;
if (_frameCount == 1 || _frameCount % 100 == 0) {
setState(() {
_frameInfo =
'Frame #$_frameCount: ${frame.width}x${frame.height} (${frame.format})';
});
}
});
}
Future<void> _toggleCapture() async {
if (_isCapturing) {
await _banuba.stopCapture();
} else {
await _banuba.startCapture();
}
setState(() => _isCapturing = !_isCapturing);
}
Future<void> _loadEffect(String name) async {
if (name == 'None') {
await _banuba.clearEffect();
} else {
await _banuba.loadEffect(name);
}
setState(() => _currentEffect = name);
}
@override
void dispose() {
_banuba.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Banuba AR Example')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Status card
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Status',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
_statusRow('Initialized', _isInitialized),
_statusRow('Capturing', _isCapturing),
_statusRow('Effect', false, label: _currentEffect),
const SizedBox(height: 8),
Text(_frameInfo,
style: Theme.of(context).textTheme.bodySmall),
],
),
),
),
const SizedBox(height: 24),
// Controls
FilledButton.icon(
onPressed: _isInitialized ? _toggleCapture : null,
icon: Icon(_isCapturing ? Icons.stop : Icons.play_arrow),
label: Text(_isCapturing ? 'Stop Capture' : 'Start Capture'),
),
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: _isCapturing ? () => _banuba.switchCamera() : null,
icon: const Icon(Icons.cameraswitch),
label: const Text('Switch Camera'),
),
const SizedBox(height: 24),
// Effect buttons
Text('Effects', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: ['None', 'TrollGrandma', 'Beauty', 'Glasses']
.map(
(name) => ChoiceChip(
label: Text(name),
selected: _currentEffect == name,
onSelected: _isCapturing ? (_) => _loadEffect(name) : null,
),
)
.toList(),
),
],
),
),
);
}
Widget _statusRow(String label, bool active, {String? label2}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
children: [
Icon(
active ? Icons.check_circle : Icons.cancel,
size: 16,
color: active ? Colors.green : Colors.grey,
),
const SizedBox(width: 8),
Text(label2 ?? '$label: ${active ? "Yes" : "No"}'),
],
),
);
}
}