Developing flutter on Nixos

Hi there. I’ve been trying for hours to get flutter development up and running on NixOS. Ideally, I want to build for all supported platforms, but Linux and Android are most important to get started.

I’ve got a flake that makes it work for linux. But the Android build fails because something is trying to write to the flutter install dir, which is in the store. Interestingly, another flutter project doesn’t have that issue, but it might be an older flutter version or something. I’m not very familiar with flutter or Android.

There are a few example flakes/repos out there, but they are quite outdated.

I’ve now tried just downloading the official flutter SDK and making it run with nix-ld (as that would allow writing to the flutter SDK dir), but even that fails because I just can’t make it find libepoxy.so.0 and libfontconfig.so.1, though I have added fontconfig and libepoxy and libepoxy.dev to the NIX_LD_LIBRARY_PATH.

Does anyone have a working config for flutter development (a flake, preferably) that will allow me to get up and running on a recent flutter version for Linux and Android? At my wits end, afraid of having to do this in a Ubuntu VM

This is my current nix-ld shell.nix:

{pkgs ? import <nixpkgs> {}}:
with pkgs;
  mkShell {
    buildInputs = [
      cmake
      clang
      ninja
      pkg-config
      gtk3
      fontconfig
    ];

    NIX_LD_LIBRARY_PATH = lib.makeLibraryPath [
      libsysprof-capture
      libepoxy
      fontconfig.lib
      alsa-lib
      at-spi2-atk
      at-spi2-core
      atk
      cairo
      cups
      curl
      dbus
      expat
      fontconfig
      freetype
      fuse3
      gdk-pixbuf
      glib
      gtk3
      icu
      libGL
      libappindicator-gtk3
      libdrm
      libglvnd
      libnotify
      libpulseaudio
      libunwind
      libusb1
      libuuid
      libxkbcommon
      libxml2
      mesa
      nspr
      nss
      openssl
      pango
      pipewire
      stdenv.cc.cc
      systemd
      vulkan-loader
      xorg.libX11
      xorg.libXScrnSaver
      xorg.libXcomposite
      xorg.libXcursor
      xorg.libXdamage
      xorg.libXext
      xorg.libXfixes
      xorg.libXi
      xorg.libXrandr
      xorg.libXrender
      xorg.libXtst
      xorg.libxcb
      xorg.libxkbfile
      xorg.libxshmfence
      zlib
    ];
    NIX_LD = lib.fileContents "${stdenv.cc}/nix-support/dynamic-linker";
    shellHook = ''
      export PATH="$PATH:/home/myuser/flutter_sdk/flutter/bin";
    '';
  }
  • This should be packages in a mkShell environment
    • As with regular packages, nativeBuildInputs should be used for buildtime tooling and hooks
  • If you want to use clang, you should override stdenv, not put it in the shell’s packages list, i.e. pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } { ... }

That’s odd, have you tried ldd-ing the binary that throws the error to see if it actually uses the nix-ld linker, and what loadpaths it ends up with?

I’ve had similar issues with flutter in the past, nix-ld eventually got me to something functional, but I still had other issues which made me pull up the ubuntu VM eventually. Distrobox may also be usable if you need to run an android VM as part of this and therefore can’t nest VMs.

Bit narrow there, this is the exact kind of use case which makes alternatives to nix-ld hard. The issue is that one of the libraries involved in an opaque rube goldberg machine of flutter build scripts is trying to write to the store path when they use the nixpkgs-provided flutter, so there needs to be some escaping of nix’ guarantees.

Which means either knowing intricately which dependency does this insane thing and convincing upstream to stop doing that, a lot of time learning flutter’s internals to write an appropriate buildFhsUserEnv which invokes an overlayfs to make the flutter sdk writeable, or setting an env variable in a shell.nix and letting NIX_LD figure it out - neither of these approaches follows nix ideology or causes long term issues that can’t be solved by a rebuild (any issues arising from the linker will be present when running the binary against every target platform including NixOS anyway, regardless of whether nix-ld was used), so… Why waste months shaving yaks for something that will only be used in development?

packages is just an alias for nativeBuildInputs in case that’s not obvious here. As long as you’re not cross-compiling (and expressing the fact that you are in your shell.nix) this only matters for shellhooks.

Given pkg-config is involved you probably want shellhooks to be executed though, so yeah, follow @waffle8946 's sage advice.

I’ll try and summarize my issues here, though they are not exactly clear in my head, yet.

Firstly, I am concerned that entering the Flutter ecosystem on NixOS is going to be particularly painful as its dependency management seems to be exceptionally strange, copying them into the installation directory of Flutter itself (or was it Dart?). That seems to be asking for trouble on NixOS.

Secondly, I’m not particularly concerned with building for NixOS, at least not yet. This thing uses Flutter to be multi-platform, and as such I’d like to make this easy to build on any system, not just NixOS. Eventually, a nice NixOS package would be good to have, but it’s no priority. Is that a strange requirement? Is it typical to assume that a package can be built only on the target platform itself (only build for Debian on Debian, on Windows for Windows and on MacOS for MacOS)? I’m coming from the Web, so I don’t know these issues.

Regarding your points:

Relying on nix-ld is basically ensuring that your package will be broken

Yes, for sure, broken on NixOS. That doesn’t concern me when I try to build an APK

Any reason you’re not using the flutter from nixpkgs?

As I said, I’m less concerned with building a package, I just need a working dev shell for now. And there is no straightforward example to create such a thing in those docs. Maybe with some tinkering, but this is not something I can do quickly.

This should be packages in a mkShell environment

That is interesting

If you want to use clang, you should override stdenv, not put it in the shell’s packages list, i.e. pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } { ... }

Probably a good point, though I don’t know why exactly. I have that working.

My sad predicament at the moment is that I have a flake.nix with which I can build on/for NixOS, but not for Android (as that build process tries to write to the store, where my Flutter SDK is) and a separate shell.nix (similar to initial post) that uses nix-ld and a manually downloaded Flutter SDK and is fine for building for Android. While this works, it sucks. So my question is plainly this: Is it possible to truly build cross-platform Flutter apps on NixOS, or will I have a better time changing OS for that? And if it’s possible, can somebody provide a working example of such a dev shell?

So you’ve figured out the issue from your post since? What’s preventing using the shell.nix for both targets?

Trying to figure out what the thing was that made the shell work for Android, I reverted back to the flake.nix. To my surprise, I could still build the app. What fails is the gradle sync. The error is

Caused by: org.gradle.api.UncheckedIOException: Failed to create directory '/nix/store/sdc47m50vj3gk27p1kvxss6c9zikyjzc-flutter-wrapped-3.27.0-0.1.pre-sdk-links/packages/flutter_tools/gradle/.gradle/buildOutputCleanup'
	at org.gradle.util.internal.GFileUtils.mkdirs(GFileUtils.java:322)
	at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:87)
...

This seems to be fairly general. Maybe I can find a way to get around that as well.

Of course, I can probably use the “external” Flutter toolchain for the Linux build as well. That would just sacrifice reproducibility even on a platform where that’s not necessary

Addon:
This line in my settings.gradle doesn’t look great

includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")

You can probably run export _JAVA_OPTIONS="-Dorg.gradle.projectcachedir=$(mktemp -d)" in a shellHook to fix that.

A dependency on CMake, it turns out! Desktop (Linux) builds require CMake. This is not being found when I use “external” flutter. I can add that using nix-ld, but eventually it comes down to the libepoxy and libfontconfig dependencies not being found, no matter if I try adding them with nix-ld or buildInputs/packages. I don’t understand why not. However, when using the nixpkgs flutter, CMake and everything else is found and Linux is building fine (but now Android is broken). I suppose I could use separate devShells, but boy would that suck.

You can probably run export _JAVA_OPTIONS="-Dorg.gradle.projectcachedir=$(mktemp -d)" in a shellHook to fix that.

Regarding this, I’m not 100% sure what it does, but the result when doing a gradle sync with that is

Build not found: BuildId(buildRoot=/home/me/.cache/flutter/nix-flutter-tools-gradle/af0f0d559c) 
available builds {BuildId(buildRoot=/home/me/filerequest/filerequest_fe/android)=:, BuildId(buildRoot=/nix/store/sdc47m50vj3gk27p1kvxss6c9zikyjzc-flutter-wrapped-3.27.0-0.1.pre-sdk-links/packages/flutter_tools/gradle)=:gradle}

Anyway, some progress is made and I appreciate it a lot! :slight_smile:

It does what it says on the tin, tells java to set a variable which gradle uses to figure out where to put its cache dir - by default it puts it in the project root, which is in the nix store, so telling it to put it literally anywhere else solves it trying to create cache files in the store.

You could also put it in $XDG_CACHE_DIR/gradle or such to persist it between shell invocations, of course.

I think those errors are unrelated to the cache, but then it seems to want to pick up build files from there. I’ve also reached the end of my java ecosystem knowledge, so I’ll bow out here. Java is my least favorite language for a reason, and its derivatives are only marginally better. Good luck!