scrcpy_video_view 0.0.1 copy "scrcpy_video_view: ^0.0.1" to clipboard
scrcpy_video_view: ^0.0.1 copied to clipboard

PlatformmacOS

Low-latency Android screen streaming into Flutter textures using scrcpy and VideoToolbox on macOS.

example/lib/main.dart

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

void main() => runApp(const ExampleApp());

class ExampleApp extends StatefulWidget {
  const ExampleApp({super.key, this.discoverOnStart = true});

  final bool discoverOnStart;

  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  final controller = ScrcpyVideoController();
  List<ScrcpyDevice> devices = const [];
  String? serial;
  Object? discoveryError;
  bool discovering = false;

  @override
  void initState() {
    super.initState();
    controller.addListener(_changed);
    if (widget.discoverOnStart) _discover();
  }

  void _changed() {
    if (mounted) setState(() {});
  }

  Future<void> _discover() async {
    if (discovering) return;
    setState(() {
      discovering = true;
      discoveryError = null;
    });
    try {
      devices = await controller.discoverDevices();
      if (!devices.any((device) => device.serial == serial)) {
        serial = devices.length == 1 ? devices.single.serial : null;
      }
    } catch (error) {
      discoveryError = error;
    }
    if (mounted) setState(() => discovering = false);
  }

  Future<void> _start() async {
    try {
      await controller.start(serial!);
    } catch (_) {
      // The controller exposes the failure and notifies the UI.
    }
  }

  String _messageFor(Object error) {
    if (error is! ScrcpyVideoException) return '$error';
    return switch (error.code) {
      ScrcpyVideoErrorCode.adbNotFound =>
        'adb is not installed. Install Android platform tools or configure '
            'adbPath.',
      ScrcpyVideoErrorCode.scrcpyNotFound =>
        'scrcpy is not installed. Install it or configure scrcpyPath.',
      ScrcpyVideoErrorCode.noAuthorizedDevices =>
        'Connect an Android device, enable USB debugging, and authorize this '
            'Mac.',
      ScrcpyVideoErrorCode.unsupportedScrcpyProtocol =>
        'This scrcpy version uses an incompatible video protocol. Please '
            'report the version and logs on GitHub.',
      _ => error.message,
    };
  }

  @override
  void dispose() {
    controller.removeListener(_changed);
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final canStop = controller.state == ScrcpyVideoState.starting ||
        controller.state == ScrcpyVideoState.streaming;
    final transitioning = controller.state == ScrcpyVideoState.stopping;
    return MaterialApp(
      title: 'example',
      darkTheme: ThemeData.dark(useMaterial3: true),
      themeMode: ThemeMode.dark,
      home: Scaffold(
        appBar: AppBar(title: const Text('example')),
        body: Row(
          children: [
            SizedBox(
              width: 300,
              child: Padding(
                padding: const EdgeInsets.all(20),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    Row(
                      children: [
                        Expanded(
                          child: DropdownButtonFormField<String>(
                            initialValue: serial,
                            isExpanded: true,
                            hint: const Text('Select an adb device'),
                            items: devices
                                .map(
                                  (device) => DropdownMenuItem(
                                    value: device.serial,
                                    child: Text(device.displayName),
                                  ),
                                )
                                .toList(),
                            onChanged: canStop || transitioning
                                ? null
                                : (value) => setState(() => serial = value),
                          ),
                        ),
                        IconButton(
                          tooltip: 'Refresh devices',
                          onPressed: canStop || transitioning || discovering
                              ? null
                              : _discover,
                          icon: discovering
                              ? const SizedBox.square(
                                  dimension: 20,
                                  child: CircularProgressIndicator(
                                    strokeWidth: 2,
                                  ),
                                )
                              : const Icon(Icons.refresh),
                        ),
                      ],
                    ),
                    const SizedBox(height: 12),
                    FilledButton(
                      onPressed: transitioning || (!canStop && serial == null)
                          ? null
                          : canStop
                              ? controller.stop
                              : _start,
                      child: Text(canStop ? 'Stop' : 'Start'),
                    ),
                    if (discoveryError != null || controller.error != null) ...[
                      const SizedBox(height: 12),
                      Text(_messageFor(controller.error ?? discoveryError!)),
                    ],
                    const SizedBox(height: 16),
                    Expanded(
                      child: SingleChildScrollView(
                        child: Text(controller.logs.join('\n')),
                      ),
                    ),
                  ],
                ),
              ),
            ),
            Expanded(
              child: ScrcpyVideo(
                controller: controller,
                placeholder: const Center(child: Text('No video')),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
150
points
90
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Low-latency Android screen streaming into Flutter textures using scrcpy and VideoToolbox on macOS.

Repository (GitHub)
View/report issues
Contributing

Topics

#scrcpy #android #video #streaming #macos

License

BSD-3-Clause (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on scrcpy_video_view

Packages that implement scrcpy_video_view