12

I have searched for a while for a good solution to this but there doesn't seem to be many others with the same setup as I have.

My setup consists of a low spec laptop (Surface pro 3) and a server machine (much higher spec) running ProxMox (which is where my Ubuntu 22.04 development VM resides).

What I am trying to achieve is to use VS Code remote development to develop the Flutter app on the server using remote development while viewing the output of the app on my Surface. My surface alone isn't powerful enough to run an Android emulator very well so it would be fantastic if the server could handle the grunt work and just forward the visual output to my Surface. Does anyone know how this would be possible?

The only success I have currently had is by running the Flutter app as a web server and then accessing the instance via Chrome with the Dart debugging extension. This isn't really ideal though. A part of me thinks that I may have to use a physical Android device and forward ADB to it or something but I'm unsure if this is the best option.

I'll be grateful for any help, thank you.

1 Answer 1

18

VS Code can indeed be used for remote development. I was able to achieve the described workflow using code-server and a bunch of SSH forwarding.

Here I describe how to set up code-server for Android development on Flutter with a remote machine used for build and debug tasks, and with a local machine used to access the editor GUI and to connect a physical device.

Verified on Ubuntu 22.04 (server), Fedora 36 (local), and Flutter 3.0.5.

Install software and SDKs on remote machine

1. Install code-server from the official repo

Important: After installing code-server tells you to make systemd start your server automatically by running a systemctl command. Avoid doing that because that way ADB under VS Code won't detect devices. I haven't come up with any workaround for that yet to make it work under a systemd-managed instance.

2. Install Flutter SDK and update your PATH

Your system might also need additional dependencies for the Flutter SDK to run. I recommend learning about it from the official manual. Prefer manual ways of installation described there.

After installation is done, update PATH variable in ~/.bashrc file to include /bin folder of the Flutter SDK, for example, add a line like this:

export PATH="$PATH:$HOME/path/to/flutter/bin" 

after which, apply the changes:

source ~/.bashrc 

3. Install Android toolchain

I assume your server does not have any Desktop Environment, so we will install Android toolchain without Android Studio (since Studio requires a DE to run).

Install Java

As of September 2023, Android Command line tools require minimal class file version 61.0 (Java 17).

You can check what version of Java you have installed by running java --version, you can also check where it is installed by running which java. If you already have Java installed, make sure it is compatible with cmdline-tools and your projects. You can install multiple Java versions and control your preferred version with sudo update-alternatives --config java command or JAVA_HOME environment variable.

Here we'll be installing OpenJDK from Ubuntu's repositories for simplicity. If you prefer so, you can install commercial Oracle Java from the official website.

To install OpenJDK 17, run:

sudo apt install openjdk-17-jdk 

You can also install latest available OpenJDK version, to list available versions for install run:

sudo apt list openjdk-*-jdk 

If you are trying to build an existing project
Older Flutter projects that did not target Android 14 might require older JDK version to build out of the box, like openjdk-11-jdk. Newer cmdline-tools won't work with openjdk-11, so you will either have to use older cmdline-tools and JDK, or migrate the project to newer version. Google doesn't provide links to older releases of cmdline-tools, but you can find them on the Internet Archive, for example, this archived copy of v9.

Download cmdline-tools

Go to Android Studio website and download "Command line tools only".

To download the archive directly onto your server, click the link on the Android Studio website, scroll down and agree to the terms, but instead of clicking the "Download Android Command Line Tools for Linux" button, right click it and copy the URL. Then download it from your server, for example, using wget:

wget https://dl.google.com/android/repository/commandlinetools-linux-XXX_latest.zip 

Unpack them with unzip commandlinetools-linux-XXX_latest.zip command in a desired location. Calling unzip will create a cmdline-tools folder in your current location. I recommend creating this folder structure when unpacking the archive:

~/path/to/android-sdk/cmdline-tools 

This way, when sdkmanager downloads its packages, new folders will be created inside the android-sdk folder.

As of September 2023, sdkmanager inside Android command line tools requires a special folder hierarchy, which can be achieved by putting content of the cmdline-tools folder inside a latest folder under it with this command:

mv ./cmdline-tools/ ./latest && mkdir cmdline-tools && mv ./latest/ ./cmdline-tools/latest/ 

Add the tools to your PATH in ~/.bashrc file and specify ANDROID_SDK_ROOT by adding new lines:

export ANDROID_SDK_ROOT="$HOME/path/to/android-sdk" export PATH="$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools" 

Don't forget to run source ~/.bashrc

Install SDK

Flutter SDK requires three packages to be installed: build-tools, platforms and platform-tools.

sdkmanager "build-tools;34.0.0" "platforms;android-34" "platform-tools" 

34.0.0 is the latest version of build-tools, and android-34 is the latest version of platforms, as of September 2023, targeting Android 14.

Learn what the latest version of build-tools is available by running:

sdkmanager --list | grep build-tools 

And for platforms:

sdkmanager --list | grep "platforms;android" 

For existing projects, Flutter will automatically download required SDKs to build them.

Accept licenses

Run sdkmanager --licenses and accept all licenses by pressing the y key

VS Code setup

After installing code-server, you can now access the editor from your browser.

It is also recommended to install it as a PWA, so you will have more screen space, more options for keyboard shortcuts and ability to launch the editor from the system launcher.

  1. Install the Flutter extension
  2. Add these settings to VS Code user settings:
{ "dart.flutterRunAdditionalArgs": [ // Dart Developer Service port (debugger) "--dds-port=10388", // Dart VM Service instance port (device) "--host-vmservice-port=10389" ], "dart.devToolsPort": 9100, } 

By default, Dart chooses random ports for connection between the debugger and the device. By setting these settings we make the ports static, so we can forward them easily.

You might want to add dart.devToolsLocation: external to your config if your browser or adblocker prevents "local network intrusion", aka preventing websites from accessing localhost ports. Though, I'd recommend adding your code-server instance into exceptions instead.

Port forwarding with SSH on local machine

For debugging an Android app with Flutter, we'll have to forward 4 ports. The table lists the port numbers according to the VS Code settings above. You can use your own port numbers, but then you must change the configs accordingly.

Port Description Forwarding
5037 ADB To remote
10388 Dart Developer Service To local
10389 VM Service on device To remote
9100 Dart DevTools To local

Commands

SSH can be used to forward ports.

To forward a port from localhost to remote host, run on the local machine:

ssh -R XXXX:localhost:XXXX user@host 

To forward a port from remote host to localhost, run on the local machine:

ssh -L XXXX:localhost:XXXX user@host 

You can chain options to ssh command, for example:

ssh -R 5037:localhost:5037 -L 10388:localhost:10388 -R 10389:localhost:10389 -L 9100:localhost:9100 user@host 

Port forwarding will be active until you close the SSH connection.

Make sure your firewall is set up to allow port forwarding.

Automation script

I made a script that automates possible quirks around the process:

  1. Kills local ADB and restarts it to release used ports
  2. Sets up port forwarding for specified ports to a remote host. The script expects you to use keys for SSH authentication.
  3. Kills all running instances of code-server, node, and adb. You will have to customize this if this doesn't work for you.
  4. Sets VSCODE_PROXY_URI to be empty to turn off port forwarding URL overrides, which break Dart DevTools. Starts ADB and launches code-server.

It is intended to be run on the local machine.

#!/bin/bash # Remote machine CE_MACHINE="user@host" # Local machine, no need to change CE_LOCALHOST="localhost" # Default port for ADB daemon is 5037 CE_ADB_PORT="5037" # You might need to specify exact path to adb on your # remote system since .bashrc is not sourced for # non-interactive sessions (see https://stackoverflow.com/a/6212684) CE_ADB_EXECUTABLE="~/dev/tools/android-sdk/platform-tools/adb" # "Dart Developer Service port CE_DDS_PORT="10388" # Dart VM Service instance port CE_HOST_VMSERVICE_PORT="10389" # Flutter DevTools port CE_DEVTOOLS_PORT="9100" #### VSCode Settings #### # "dart.flutterRunAdditionalArgs": [ # "--dds-port=10388", # "--host-vmservice-port=10389", # ], # "dart.devToolsPort": 9100 #### VSCode Settings #### # Reset ADB on local machine # so it releases used ports killall adb adb devices ## INSERT ADDITIONAL LINES HERE # When `adb devices` is called, ADB checks the daemon port. # If it detects there's no response on the port, it # launches a new daemon. # # After killing ADB and forwarding the ADB port to local machine, # a newly launched ADB client will bind to the existing connection # (which is our physical device) instead of launching a daemon on # the remote machine. # # Restart code-server # ADB doesn't detect devices if code-server is managed by systemctl # WARNING, killing all codee-server, Node, Dart, and ADB processes here. Customize if needed. # # 1. ADB forwarding Local -> Remote # 2. Dart Dev Server forwarding Remote -> Local # 3. Dart on-device debugger client forwarding Local -> Remote # 4. Flutter DevTools Remote -> Local forwarding ssh -R $CE_ADB_PORT:$CE_LOCALHOST:$CE_ADB_PORT \ -L $CE_DDS_PORT:$CE_LOCALHOST:$CE_DDS_PORT \ -R $CE_HOST_VMSERVICE_PORT:$CE_LOCALHOST:$CE_HOST_VMSERVICE_PORT \ -L $CE_DEVTOOLS_PORT:$CE_LOCALHOST:$CE_DEVTOOLS_PORT \ $CE_MACHINE "killall code-server; killall node; killall dart; killall adb; $CE_ADB_EXECUTABLE devices; VSCODE_PROXY_URI= code-server" 

Additional port management

Sometimes connection can be broken unexpectedly, so ports will be busy and forwarding won't work. You can add these lines after the adb devices line to the script above:

 payload() { cat <<EOF for port in $CE_ADB_PORT $CE_DDS_PORT $CE_HOST_VMSERVICE_PORT $CE_DEVTOOLS_PORT; do pid="\$(sudo ss -tulpn | grep ":\$port" | grep -Po 'pid=\\d+,' | grep -Po '\\d+' | uniq)" if [ ! -z \$pid ]; then kill "\$pid" echo Freed :\$port from PID \$pid fi done EOF } if [ ! -z "$1" ]; then payload | ssh $CE_MACHINE /bin/bash fi 

With the code added, if you pass any argument to the script, it will attempt to free the used ports on the remote machine (e.g. script.sh kill).

This code enumerates processes that keep the used ports busy, and then kills them.

For sudo ss to work in the script for non-superuser, you'll need to add the following line to /etc/sudoers by opening the file with sudo visudo command. Doing this will allow your user run sudo ss without entering password:

myusername ALL=(root) NOPASSWD: /usr/bin/ss 

Alternatively, you can store your password in the script and change the script line the following way:

echo YOURPASSWORD | sudo -S 
Sign up to request clarification or add additional context in comments.

10 Comments

Even with port forwarding, I still get the following error, am I missing something else [CHROME]:[13746:13746:1222/081308.099592:ERROR:ozone_platform_x11.cc(238)] Missing X server or $DISPLAY [CHROME]:[13746:13746:1222/081308.099634:ERROR:env.cc(255)] The platform failed to initialize. Exiting. Failed to launch browser after 3 tries. Command used to launch it: google-chrome --user-data-dir ...... Failed to launch browser. Make sure you are using an up-to-date Chrome or Edge. Otherwise, consider using -d web-server instead and filing an issue at github.com/flutter/flutter/issues.
and it keeps adding more ports to forward in vscode
I use a different web app development platform that forwards the port and waits instead of terminating and shows me the URL, which I open in chrome browser and all works fine. Looks like Flutter is exiting after failing to launch browser
When I pick the Webserver as the device instead of chrome, it is able to spawn web-browser, it might be a new feature, should investigate further
flutter run -d web-server on cmd line worked for me
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.