/>Deep Skylabs
Back to Engineering Logs
2026-07-22Engineering5 min read

How We Built WinSearch: A Spotlight-Style Launcher for Windows using Flutter & C#

Ishu Prabhakar

Ishu Prabhakar

Founder & Lead Engineer

Why Build Another Launcher?

If you spend your workday jumping between code editors, terminals, documentation tabs, and system settings, your keyboard launcher is your primary interface control plane.

On macOS, Spotlight and Raycast feel fast, focused, and predictable. On Windows, default search often drags in web suggestions you didn't ask for, while tools like PowerToys Run can feel heavier than necessary or lack seamless deep file search with typed qualifier syntax.

We built WinSearch to fix this for our own workflows: a single global hotkey (Alt+Space) overlay that feels instantaneous, looks clean with native Windows DWM acrylic blur, and exposes deep OS capabilities without bloating the UI process.


The Architecture: Flutter UI + C# .NET Sidecar

Flutter is great for building high-DPI desktop interfaces with smooth animations. But accessing low-level Windows features — like querying the Windows Search Index through OLE DB, enumerating system audio endpoints via Core Audio COM interfaces, or registering a native Win32 clipboard format listener — is either awkward or impossible using standard Flutter plugins.

Instead of writing complex FFI wrappers directly inside Dart for every Win32 API, we separated the app into two distinct layers connected by a Win32 named pipe:

  1. Flutter Desktop App: Controls window positioning, keybindings, state management, autocomplete ghost text, and results rendering.
  2. LauncherService.exe (C# .NET 8): A zero-window background sidecar process that executes low-level Windows APIs and responds to JSON-RPC 2.0 requests over \\.\pipe\LauncherServicePipe_v1.
// Low-level Win32 Named Pipe Client in Dart using dart:ffi
class NativeServiceClient {
  final String pipeName;
  late final PipeHandle _pipeHandle;

  Future<void> connect() async {
    // Opens \\.\pipe\LauncherServicePipe_v1 via CreateFileW Win32 API
    _pipeHandle = await Win32Pipe.open(pipeName);
  }

  Future<RpcResponse> sendRequest(RpcRequest request) async {
    final payload = jsonEncode(request.toJson());
    await _pipeHandle.writeFramed(payload);
    final responseBytes = await _pipeHandle.readFramed();
    return RpcResponse.fromJson(jsonDecode(responseBytes));
  }
}

If LauncherService.exe unexpectedly closes or crashes, the Flutter IPC manager detects the broken pipe, attempts an automated background respawn, and re-establishes RPC communication without freezing the launcher UI.


Scoped Modes & Qualifier Parsing

One of our primary requirements was structured query filtering. Rather than forcing users to remember complex UI filter menus, WinSearch parses qualifiers directly out of raw text.

  • Typing : activates mode selection (:file, :proc, :cb, :set).
  • Mode-scoped queries parse key-value tokens like ext:md, size:>5mb, order:desc, or status:running.
raw user input
      │
      ▼
┌────────────────────────┐
│    QueryTokenizer      │  ===> Identifies qualifiers (`key:value`) vs free text
└────────────────────────┘
      │
      ▼
┌────────────────────────┐
│      QueryParser       │  ===> Resolves mode specs & token resolvers (@today, @now)
└────────────────────────┘
      │
      ▼
┌────────────────────────┐
│     ParsedQuery        │  ===> Consumed concurrently by SearchProviders
└────────────────────────┘

UX Polish: Eliminating Spinner Flicker & Input Lag

When building an application that opens on a global hotkey, sub-100ms perception is everything. Two small design choices made a noticeable difference:

  1. The 180ms Spinner Delay: Fast queries (like matching installed apps from local memory) resolve in under 15ms. Showing a loading spinner for 15ms creates annoying UI flicker. We added a 180ms delay timer to the mode pill loading spinner — so fast queries never flash a spinner at all.
  2. 90ms Search Debounce: Typing rapidly sends query changes on every keystroke. Debouncing search execution by 90ms avoids churning named-pipe RPC traffic while still feeling instant when typing pauses.
  3. Chained Autocomplete: Autocomplete suggestions check qualifier key matches, enum values, and path shortcuts in sequence. Hitting Tab or Right Arrow instantly commits the suggestion without breaking typing flow.

State Management without Code-Gen

We chose Riverpod 3.x using explicit, manual Notifier classes rather than code generation (@riverpod). Keeping state classes explicit made debugging state flows straightforward, simplified unit testing, and kept build steps fast during hot-reload iterations.

Testing the entire app is covered by 94 unit and integration tests — including an integration test (test/ipc_test.dart) that actually launches the compiled LauncherService.exe, sends named-pipe requests, verifies response payloads, and tests crash-recovery respawns.


Lessons & What's Next

Building WinSearch reinforced a pattern we rely on often at DeepSky Labs: pairing a high-productivity UI engine (Flutter) with a native background process (C# .NET) over a lightweight IPC channel gives you the best of both worlds — instant UI rendering and full native OS access without fragile plugin glue.

WinSearch is built by DeepSky Labs as a privacy-focused desktop productivity utility. Reach out to our engineering team to learn more about our desktop architecture and product suite.