vibecallsdk 1.0.0 copy "vibecallsdk: ^1.0.0" to clipboard
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,
        ),
      ),
    );
  }
}