September 24, 2024

Minimalist Configuration for NeoVim and Flutter

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,
  },
}
```lua
-- 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.