vibecallsdk 1.0.0
vibecallsdk: ^1.0.0 copied to clipboard
A robust, modular video communication SDK with virtual backgrounds, face filters, and more.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:vibecallsdk/vibecallsdk.dart';
import 'package:vibecallsdk/src/core/logging/log_level.dart';
import 'core/utils/assets_downloader.dart';
import 'core/utils/mediapipe_downloader.dart';
import 'core/utils/demo_logger.dart';
import 'core/utils/first_launch_setup.dart';
import 'core/theme/theme.dart';
import 'core/utils/routes.dart';
import 'core/navigation/page_transitions.dart';
import 'widgets/navigation/bottom_nav_bar.dart';
import 'widgets/common/loading_indicator.dart';
import 'screens/documentation/documentation_home.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize logger
await DemoLogger.instance.configure(
enabled: true,
saveToFile: true,
);
// Show splash screen while loading assets
runApp(const SplashApp());
// Run first launch setup
await FirstLaunchSetup.instance.runSetup();
// Download all assets and models
await AssetsDownloader.downloadAllAssets();
await _downloadMLModels();
// Initialize SDK
await _initializeSDK();
// Start the main app
runApp(const MyApp());
}
/// Initialize the SDK
Future<void> _initializeSDK() async {
try {
await VibeCallSDK.instance.initialize(
config: SDKConfig(
logLevel: LogLevel.debug,
enableCloudReporting: false,
),
);
} catch (e) {
// SDK might already be initialized
DemoLogger.instance.error('SDK initialization issue: $e');
}
}
/// Splash screen app shown while loading assets
class SplashApp extends StatelessWidget {
const SplashApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: AppTheme.getLightTheme(),
home: Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
'assets/icons/camera.svg',
width: 100,
height: 100,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.primary,
BlendMode.srcIn,
),
placeholderBuilder: (context) => Icon(
Icons.camera_alt,
size: 100,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 30),
Text(
'VibeCallSDK Demo',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 40),
LoadingIndicator(
message: 'Loading assets and ML models...',
size: 40,
color: Theme.of(context).colorScheme.primary,
),
],
),
),
),
);
}
}
/// Download all required ML models
Future<void> _downloadMLModels() async {
try {
// Download models in parallel
await Future.wait([
MediaPipeDownloader.downloadFaceDetectionModel(),
MediaPipeDownloader.downloadFaceMeshModel(),
MediaPipeDownloader.downloadSegmentationModel(),
MediaPipeDownloader.downloadPoseDetectionModel(),
]);
} catch (e) {
DemoLogger.instance.error('Error downloading ML models: $e');
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'VibeCallSDK Demo',
debugShowCheckedModeBanner: false,
theme: AppTheme.getLightTheme(),
darkTheme: AppTheme.getDarkTheme(),
themeMode: ThemeMode.system,
routes: AppRoutes.routes,
onGenerateRoute: (settings) {
// Use custom transitions for all routes
if (settings.name != null &&
AppRoutes.routes.containsKey(settings.name)) {
return PageTransition<dynamic>(
child: AppRoutes.routes[settings.name]!(context),
type: PageTransitionType.fadeAndScale,
settings: settings,
);
}
return null;
},
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _fadeAnimation;
final List<Map<String, dynamic>> _categories = AppRoutes.categories
.map((category) => {
'title': category.title,
'icon': category.icon,
'routes': category.routes,
})
.toList();
@override
void initState() {
super.initState();
// Set up animations
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1200),
);
_fadeAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeOut,
);
_animationController.forward();
// Check if assets need to be pre-downloaded
_checkForAssetDownload();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
Future<void> _checkForAssetDownload() async {
// Wait a bit to ensure the UI is fully rendered
await Future.delayed(const Duration(milliseconds: 500));
if (!mounted) return;
// Check if user wants to pre-download assets
final shouldDownload =
await FirstLaunchSetup.instance.showPreDownloadDialog(context);
if (shouldDownload && mounted) {
await FirstLaunchSetup.instance.preDownloadAssets(context);
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Scaffold(
appBar: AppBar(
title: const Text('VibeCallSDK Demo'),
centerTitle: true,
elevation: 0,
actions: [
IconButton(
icon: const Icon(Icons.help_outline_rounded),
tooltip: 'Documentation',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DocumentationHomePage(),
),
);
},
),
],
),
body: SafeArea(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Welcome to VibeCallSDK',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 8),
Text(
'Explore our features with these interactive demos',
style: TextStyle(
fontSize: 16,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 16),
// Documentation banner
Card(
margin: const EdgeInsets.only(bottom: 16),
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DocumentationHomePage(),
),
);
},
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
backgroundColor: colorScheme.tertiaryContainer,
radius: 24,
child: Icon(
Icons.menu_book_rounded,
color: colorScheme.tertiary,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Documentation',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 4),
Text(
'Learn how to integrate VibeCallSDK into your Flutter apps',
style: TextStyle(
fontSize: 14,
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
size: 16,
color: colorScheme.onSurfaceVariant,
),
],
),
),
),
),
const SizedBox(height: 8),
Expanded(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.0,
),
itemCount: _categories.length,
itemBuilder: (context, index) {
final category = _categories[index];
return _buildCategoryCard(
context,
category['title'],
category['icon'],
category['routes'],
index,
);
},
),
),
],
),
),
),
),
);
}
Widget _buildCategoryCard(
BuildContext context,
String title,
IconData icon,
List<String> routes,
int index,
) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
// Generate gradient colors based on index
final List<List<Color>> gradients = [
[colorScheme.primary, colorScheme.primaryContainer],
[colorScheme.secondary, colorScheme.secondaryContainer],
[colorScheme.tertiary, colorScheme.tertiaryContainer],
[
colorScheme.primary.withAlpha(179),
colorScheme.primaryContainer.withAlpha(179)
],
[
colorScheme.secondary.withAlpha(179),
colorScheme.secondaryContainer.withAlpha(179)
],
];
final gradientColors = gradients[index % gradients.length];
return InkWell(
onTap: () {
_navigateToFirstRouteInCategory(routes, index);
},
borderRadius: BorderRadius.circular(16),
child: AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
// Staggered animation
final delay = index * 0.2;
final startTime = delay;
final endTime = startTime + 0.6;
final progress = _animationController.value;
final opacity = progress < startTime
? 0.0
: progress > endTime
? 1.0
: (progress - startTime) / (endTime - startTime);
final translateY = (1 - opacity) * 20;
return Opacity(
opacity: opacity,
child: Transform.translate(
offset: Offset(0, translateY),
child: child,
),
);
},
child: Card(
elevation: 4,
margin: EdgeInsets.zero,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: gradientColors,
),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 48,
color: Colors.white,
),
const SizedBox(height: 16),
Text(
title,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 4),
Text(
'${routes.length} demos',
style: TextStyle(
color: Colors.white.withAlpha(204),
fontSize: 12,
),
),
],
),
),
),
),
),
);
}
void _navigateToFirstRouteInCategory(List<String> routes, int categoryIndex) {
if (routes.isEmpty) return;
final routeName = routes.first;
final routeWidget = AppRoutes.routes[routeName]!(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BottomNavScaffold(
child: routeWidget,
selectedCategoryIndex: categoryIndex,
),
),
);
}
}