xcamera 0.1.0 copy "xcamera: ^0.1.0" to clipboard
xcamera: ^0.1.0 copied to clipboard

PlatformLinux

A lightweight Flutter camera plugin for Linux supporting high-performance V4L2 streaming and synchronized H.264/AAC video recording.

example/lib/main.dart

// example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:xcamera/xcamera.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'XCamera Basic Preview',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(useMaterial3: true).copyWith(
        colorScheme: const ColorScheme.dark(
          primary: Color(0xFF7C9E8A),
          secondary: Color(0xFFA8C4B8),
          surface: Color(0xFF1E1E1E),
          surfaceContainer: Color(0xFF2A2A2A),
        ),
        scaffoldBackgroundColor: const Color(0xFF121212),
        appBarTheme: const AppBarTheme(
          backgroundColor: Color(0xFF1A1A1A),
          elevation: 0.0,
          centerTitle: false,
        ),
        cardTheme: CardThemeData(
          color: const Color(0xFF252525),
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
          elevation: 1.0,
        ),
        textTheme: const TextTheme(
          bodyMedium: TextStyle(color: Color(0xFFE0E0E0), fontSize: 13.0),
          labelSmall: TextStyle(color: Color(0xFF9E9E9E), fontSize: 11.0),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
            textStyle: const TextStyle(fontWeight: FontWeight.w600, fontSize: 13.0),
            padding: const EdgeInsets.symmetric(
              horizontal: 16.0,
              vertical: 0.0,
            ),
            minimumSize: const Size(0.0, 40.0),
          ),
        ),
      ),
      home: const BasicPreview(),
    );
  }
}

class BasicPreview extends StatefulWidget {
  const BasicPreview({super.key});

  @override
  State<BasicPreview> createState() => _BasicPreviewState();
}

class _BasicPreviewState extends State<BasicPreview> {
  CameraController? _controller;

  List<CameraDescription>? _cameras;
  CameraDescription? _selectedCamera;

  bool _isInitialized = false;
  bool _isSwitching = false;

  String _error = '';

  @override
  void initState() {
    super.initState();

    // Initialize cameras when the widget is first created.
    _initCameras();
  }

  Future<void> _initCameras() async {
    try {
      // Retrieve all available camera devices.
      final cameras = await availableCameras();
      if (cameras.isEmpty) {
        setState(() => _error = 'No cameras found');
        return;
      }

      // Update state with the camera list and select the first camera.
      setState(() {
        _cameras = cameras;
        _selectedCamera = cameras[0];
        _error = '';
      });

      // Initialize the camera controller with the selected camera.
      await _initController(cameras[0]);
    } catch (e) {
      setState(() => _error = 'Error: $e');
    }
  }

  Future<void> _initController(CameraDescription camera) async {
    // Prevent multiple simultaneous initialization attempts.
    if (_isSwitching) return;

    // Update state to reflect the switching/initialization process.
    setState(() {
      _isSwitching = true;
      _isInitialized = false;
      _selectedCamera = camera;
    });

    try {
      // Dispose the existing controller if any.
      await _controller?.dispose();

      // Create and initialize the new controller with maximum resolution.
      final controller = CameraController(camera, ResolutionPreset.max);
      await controller.initialize();

      // Update state to reflect successful initialization.
      setState(() {
        _controller = controller;
        _isInitialized = true;
        _isSwitching = false;
        _error = '';
      });
    } catch (e) {
      // Handle initialization errors.
      setState(() {
        _error = 'Failed to initialize camera: $e';
        _isSwitching = false;
        _isInitialized = false;
        _controller = null;
      });
    }
  }

  @override
  void dispose() {
    // Release camera resources when the widget is disposed.
    _controller?.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_error.isNotEmpty) {
      return Scaffold(
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              _error,
              style: const TextStyle(
                color: Colors.redAccent,
              ),
            ),
          ),
        ),
      );
    } else if (_cameras == null || _cameras!.isEmpty) {
      return const Scaffold(
        body: Center(
          child: CircularProgressIndicator(),
        ),
      );
    } else if (_isSwitching || !_isInitialized || _controller == null) {
      return const Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            spacing: 16.0,
            children: [
              CircularProgressIndicator(
                valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF7C9E8A)),
              ),
              Text(
                'Initializing camera...',
                style: TextStyle(
                  color: Color(0xFF7C9E8A),
                  fontSize: 16.0,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
      );
    } else {
      return Scaffold(
        body: Column(
          children: [
            Expanded(
              child: Container(
                color: Colors.black,
                child: Center(
                  child: AspectRatio(
                    aspectRatio: (() {
                      if (_controller!.value.previewSize != null) {
                        return _controller!.value.previewSize!.width / _controller!.value.previewSize!.height;
                      } else {
                        return 16 / 9;
                      }
                    })(),
                    child: CameraPreview(_controller!),
                  ),
                ),
              ),
            ),
            Container(
              padding: const EdgeInsets.all(16.0),
              decoration: BoxDecoration(
                color: const Color(0xFF1A1A1A),
                border: Border(
                  top: BorderSide(
                    color: Colors.black54,
                    width: 1.0,
                  ),
                ),
              ),
              child: Row(
                spacing: 16.0,
                children: [
                  Expanded(
                    child: DropdownButton<CameraDescription>(
                      value: _selectedCamera,
                      isExpanded: true,
                      dropdownColor: const Color(0xFF252525),
                      style: const TextStyle(
                        color: Colors.white,
                        fontSize: 14.0,
                      ),
                      underline: Container(
                        height: 2.0,
                        color: const Color(0xFF7C9E8A),
                      ),
                      items: _cameras!.map((camera) {
                        return DropdownMenuItem(
                          value: camera,
                          child: Text(
                            camera.name,
                            style: const TextStyle(
                              color: Colors.white,
                              fontWeight: FontWeight.w500,
                            ),
                          ),
                        );
                      }).toList(),
                      onChanged: (camera) async {
                        if (camera != null && camera != _selectedCamera) {
                          await _initController(camera);
                        }
                      },
                    ),
                  ),
                  Container(
                    decoration: BoxDecoration(
                      color: _isInitialized ? const Color(0xFF7C9E8A) : Colors.grey[800],
                      borderRadius: BorderRadius.circular(6.0),
                    ),
                    padding: const EdgeInsets.symmetric(
                      horizontal: 12.0,
                      vertical: 6.0,
                    ),
                    child: Text(
                      _isInitialized ? 'LIVE' : 'OFF',
                      style: const TextStyle(
                        fontSize: 11.0,
                        fontWeight: FontWeight.bold,
                        color: Colors.black,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      );
    }
  }
}
1
likes
160
points
168
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight Flutter camera plugin for Linux supporting high-performance V4L2 streaming and synchronized H.264/AAC video recording.

Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

cross_file, equatable, flutter, get_it, plugin_platform_interface

More

Packages that depend on xcamera

Packages that implement xcamera