Minimalist Configuration for NeoVim and Flutter

I recently started using NeoVim as my main IDE for all my projects, including Flutter.

A terminal : WezTerm

Since I use Flutter primarily to develop mobile applications, I have no choice but to work on macOS, as it is required to build iOS applications.

I chose to use the WezTerm terminal, which offers several important features for me:

  • Open source: Having access to the source code of such a sensitive application as a terminal is essential so that everyone can ensure its security.

  • No server: For the same security reasons, the terminal must not send data to a remote server (often for AI-related features).

  • Configurable with the Lua language: The configuration method is quite similar to NeoVim.

  • Cross-platform: in case I want to use it on Linux or Windows, I can use the same environment and configuration.

A distribution : LazyVim

As for the default configuration of NeoVim, I decided to use a well-known distribution: LazyVim!

This distribution includes a set of plugins, shortcuts, and themes. It also contains a specific plugin called LazyExtra, which makes it easier to install and configure languages or plugins.

However, there's a small issue... Dart is not included among the supported languages by default, meaning no support for Flutter.

Avoiding flutter-tools.nvim

There is a dedicated plugin for Flutter called flutter-tools.nvim.

But after trying it out for some time, I decided to stop using it. In fact, a lot of compatibility errors occurred due to dependency updates or conflicts with my LazyVim configuration. After trying to tweak these tools, I eventually came to the conclusion that I didn't need everything included: a simple connection to the Dart Language Server Protocol should be enough for me.

Setup the Language Server Protocol

So, I searched for minimalist configurations and was inspired by Robert's configuration (thanks to him!) to adapt it to the LazyVim setup. Instead of using lsp-config directly like he did, I reused the default LSP plugin configuration format from LazyVim and added the dartls server with the corresponding options!

Here is my working addition to the dotfiles/.config/nvim/lua/plugins/plugins.lua file.

return {
  -- ...
  {
    "neovim/nvim-lspconfig",
    event = "LazyFile",
    dependencies = {
      "mason.nvim",
      { "williamboman/mason-lspconfig.nvim", config = function() end     },
    },
    opts = function()
      local ret = {
        diagnostics = {},
        inlay_hints = {
          enabled = true,
        },
        codelens = {
          enabled = true,
        },
        document_highlight = {
          enabled = true,
        },
        capabilities = {
          workspace = {
            fileOperations = {
              didRename = true,
              willRename = true,
            },
          },
        },
        format = {
          formatting_options = nil,
          timeout_ms = nil,
        },
        servers = {
          dartls = {

            mason = false,
            cmd = {
              "dart",
              "language-server",
              "--protocol=lsp",
            },
            filetypes = { "dart" },
            init_options = {
              onlyAnalyzeProjectsWithOpenFiles = false,
              suggestFromUnimportedLibraries = true,
              closingLabels = true,
              outline = false,
              flutterOutline = false,
            },
            settings = {
              dart = {
                analysisExcludedFolders = {
                  vim.fn.expand("$HOME/AppData/Local/Pub/Cache"),
                  vim.fn.expand("$HOME/.pub-cache"),
                  vim.fn.expand("/opt/homebrew/"),
                  vim.fn.expand("$HOME/tools/flutter/"),
                },
                updateImportsOnRename = true,
                completeFunctionCalls = true,
                showTodos = true,
              },
            },
          },
          -- ...
        },
        setup = {},
      }
      return ret
    end,
  },
}

This way, I inherit all the preconfigured shortcuts from LazyVim in my Flutter configuration.

Launch the app in NeoVim

Even though you can launch your application from any terminal, it can be useful to have a way to launch the application directly from NeoVim, along with adapted shortcuts.

To facilitate navigation between the process running Flutter and the one running NeoVim, a terminal multiplexer is practically indispensable.

Many people use tmux, but WezTerm integrates a multiplexer and APIs to interact with the different sessions.

You just need to associate NeoVim mappings with these command lines, and voilà!

I then created two small commands in my “config/keymaps.lua” file:

  • FlutterRun, which will launch the flutter run command

  • FlutterInput, which will allow you to send keyboard inputs to session

-- Flutter commands
vim.api.nvim_create_user_command("FlutterRun", function(opts)
  -- Open a new wezterm pane on the right
  local command = "wezterm cli split-pane --right --percent 30 --cwd '"
    .. opts.args
    .. "' -- bash -c 'flutter run; exec bash'"

  -- Run the command to create the new pane and capture the pane ID
  local handle = io.popen(command)
  if handle ~= nil then
    local result = handle:read("*a")
    handle:close()

    -- Assuming the pane ID is printed after the pane is created
    -- Here you would normally extract the pane ID from the command output if necessary.
    -- In case you need to get pane details use `wezterm cli list --format json`.
    wezterm_pane_id = result:match("%d+") -- Extract pane ID
  end
end, { nargs = 1 })

vim.api.nvim_create_user_command("FlutterInput", function(input)
  if wezterm_pane_id then
    -- Send input to the stored pane ID
    local send_command = "wezterm cli send-text --pane-id " .. wezterm_pane_id .. " '" .. input.args .. "'"
    os.execute(send_command)
  else
    print("Error: No WezTerm pane created yet. Run :FlutterRun first.")
  end
end, { nargs = 1 })

With this, you can add actions to trigger a Hot Reload, a Hot Restart, or any other supported action with a simple shortcut!

-- Keymaps
vim.keymap.set("n", "<leader>off", function()
  vim.ui.input({ prompt = "In which directory?", default = vim.fn.getcwd() }, function(input)
    if input then
      vim.cmd("FlutterRun " .. input)
    end
  end)
end, {
  desc = "Start the app.",
  noremap = true,
  silent = true,
})

vim.api.nvim_set_keymap("n", "<leader>ofr", ":FlutterInput r<CR>", {
  desc = "Hot reload. 🔥🔥🔥",
  noremap = true,
  silent = true,
})

By the way, my full NeoVim configuration is available on GitHub.

Previous
Previous

How to Install a Local LLM on macOS in 10 Minutes (and use it in NeoVim)

Next
Next

Using Flutter as a source in OBS