vnc_viewer 0.0.6 copy "vnc_viewer: ^0.0.6" to clipboard
vnc_viewer: ^0.0.6 copied to clipboard

PlatformAndroid

Flutter plugin for embedding an Android VNC client that renders remote desktops and sends keyboard and pointer input to VNC/RFB servers.

vnc_viewer #

vnc_viewer is a Flutter plugin for embedding an Android VNC client inside your app.

It connects to a VNC/RFB server, renders the remote desktop through a Flutter Texture, and exposes APIs for sending keyboard and pointer events from Dart.

Features #

  • Connect to a VNC server with hostName, port, and password
  • Configure a connection timeout from Dart, with a built-in default timeout
  • Render the remote framebuffer inside Flutter with VncViewerWidget
  • Receive connection lifecycle callbacks such as onStart, onClose, and onError
  • React to framebuffer size changes with onImageResize
  • Send keyboard and pointer events with VncViewerHandel
  • Includes an example app for quick local verification

Platform Support #

Platform Status
Android Supported
iOS Not supported
macOS Not supported
Windows Not supported
Linux Not supported
Web Not supported

Requirements #

  • Flutter >=3.3.0
  • Dart SDK >=3.3.4 <4.0.0
  • Android minSdkVersion 19
  • Android app must have network access permission

The plugin currently builds native libraries for:

  • armeabi-v7a
  • arm64-v8a

Installation #

Add the dependency to your pubspec.yaml:

dependencies:
  vnc_viewer: ^0.0.6

Then run:

flutter pub get

Android Configuration #

Your host app must declare Internet permission in android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:label="your_app_name">
        ...
    </application>
</manifest>

If you plan to test a release build, add the permission to the main manifest, not only the debug manifest.

Quick Start #

Use VncViewerWidget when you want a ready-to-embed viewer widget:

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:vnc_viewer/vnc_viewer_handel.dart';
import 'package:vnc_viewer/vnc_viewer_widget.dart';

class VncPage extends StatefulWidget {
  const VncPage({
    super.key,
    required this.hostName,
    required this.port,
    required this.password,
  });

  final String hostName;
  final int port;
  final String password;

  @override
  State<VncPage> createState() => _VncPageState();
}

class _VncPageState extends State<VncPage> {
  final VncViewerHandel _handle = VncViewerHandel();
  int? _clientId;
  String _status = 'Connecting...';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('${widget.hostName}:${widget.port}'),
        actions: [
          IconButton(
            onPressed: _clientId == null ? null : _sendEnter,
            icon: const Icon(Icons.keyboard_return),
          ),
        ],
      ),
      body: Column(
        children: [
          Expanded(
            child: VncViewerWidget(
              hostName: widget.hostName,
              port: widget.port,
              password: widget.password,
              onStart: (clientId) {
                setState(() {
                  _clientId = clientId;
                  _status = 'Connected';
                });
              },
              onClose: (_) {
                setState(() {
                  _clientId = null;
                  _status = 'Closed';
                });
              },
              onImageResize: () {
                setState(() {
                  _status = 'Rendering';
                });
              },
              onError: (message) {
                setState(() {
                  _status = 'Error: $message';
                });
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text(message)),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              children: [
                Expanded(child: Text(_status)),
                FilledButton(
                  onPressed: _clientId == null ? null : _sendEnter,
                  child: const Text('Send Enter'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Future<void> _sendEnter() async {
    final clientId = _clientId;
    if (clientId == null) {
      return;
    }

    _handle.sendKey(clientId, 0xff0d, true);
    await Future.delayed(const Duration(milliseconds: 80));
    _handle.sendKey(clientId, 0xff0d, false);
  }
}

API Overview #

VncViewerWidget #

The widget manages VNC client initialization, event subscription, rendering, and cleanup for you.

Constructor:

const VncViewerWidget({
  Key? key,
  required String hostName,
  required String password,
  int port = 5900,
  Duration connectTimeout = VncViewerWidget.defaultConnectTimeout,
  void Function(int clientId)? onStart,
  void Function(int clientId)? onClose,
  VoidCallback? onImageResize,
  void Function(String msg)? onError,
})

Parameters:

  • hostName: Hostname or IP address of the VNC server
  • port: TCP port of the VNC server, default is 5900
  • password: VNC password, pass an empty string if the server does not require one
  • connectTimeout: Timeout used while establishing the native VNC connection. Defaults to Duration(seconds: 10)
  • onStart: Called after the native VNC connection is established successfully
  • onClose: Called when the VNC session is closed
  • onImageResize: Called when the remote framebuffer size changes
  • onError: Called when initialization or runtime errors occur

Behavior:

  • Shows a loading state while connecting
  • Shows an error or closed state instead of staying on the loading indicator when the session fails or ends
  • Scales the remote frame to fit the available viewport
  • Wraps the frame with InteractiveViewer for zooming and panning
  • Closes the native client automatically on widget disposal

Logging #

Android-side logs use the tag libvncviewer_flutter.

The plugin now logs key lifecycle steps including:

  • Init request and validation
  • Event stream registration
  • Native start, connect success, framebuffer resize, and close
  • Error and cleanup paths

VncViewerHandel #

VncViewerHandel exposes low-level methods for manual control:

final handle = VncViewerHandel();

Available methods:

  • getPlatformVersion()
  • initVncClient(String hostName, int port, String password, {Duration? connectTimeout})
  • startVncClient(int clientId)
  • closeVncClient(int clientId)
  • sendPointer(int clientId, int x, int y, int mask)
  • sendKey(int clientId, int key, bool down)

In most cases, you should let VncViewerWidget manage initialization and lifecycle, and only use VncViewerHandel for sending input events after onStart gives you a clientId.

Timeout example:

final clientId = await handle.initVncClient(
  '192.168.1.20',
  5900,
  'secret',
  connectTimeout: const Duration(seconds: 5),
);

Sending Input Events #

Keyboard events #

sendKey expects VNC/X11 keysym values.

Example: send Enter

handle.sendKey(clientId, 0xff0d, true);
handle.sendKey(clientId, 0xff0d, false);

Pointer events #

sendPointer expects remote coordinates and a VNC button mask.

Common button mask values:

  • 0: release all buttons
  • 1: left button
  • 2: middle button
  • 4: right button

Example:

handle.sendPointer(clientId, 120, 80, 1);
handle.sendPointer(clientId, 120, 80, 0);

Note that VncViewerWidget currently focuses on rendering. If you want full remote mouse or touch control, map your own Flutter gestures to sendPointer.

Example App #

The bundled example supports --dart-define so you can launch a server directly:

cd example
flutter run \
  --dart-define=VNC_HOST=192.168.1.100 \
  --dart-define=VNC_PORT=5900 \
  --dart-define=VNC_PASSWORD=secret \
  --dart-define=VNC_AUTO_CONNECT=true

If VNC_AUTO_CONNECT is omitted or set to false, the example app shows a form where you can enter the connection details manually.

Error Handling #

Recommended practice:

  • Always provide onError
  • Validate host and port before opening the viewer
  • Treat clientId == null or 0 as initialization failure
  • Clean up any custom input controllers when the page is disposed

Typical failure reasons:

  • Wrong host, port, or password
  • The VNC server is unreachable
  • The server requires an authentication flow not covered by the current API
  • Missing INTERNET permission in the Android app

Limitations #

  • Android only at the moment
  • Current Dart API only accepts hostName, port, and password
  • No built-in username field for auth flows that require a non-empty username
  • No clipboard, file transfer, or audio API exposed in Dart
  • No built-in desktop control overlay; custom gesture mapping is up to the app

Testing #

The package includes Dart-side unit tests for:

  • VncViewerHandel delegation
  • MethodChannelVncViewer method channel calls

Run tests with:

flutter test

License #

This package is distributed under the GNU General Public License, version 2 or later (GPL-2.0-or-later). See LICENSE.

0
likes
140
points
128
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter plugin for embedding an Android VNC client that renders remote desktops and sends keyboard and pointer input to VNC/RFB servers.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on vnc_viewer

Packages that implement vnc_viewer