Flutter for Windows Desktop: Getting Started

Learn how to set up a development environment and create a simple Flutter calculator app for Windows Desktop. By Karol Wrótniak.

4.7 (3) · 2 Reviews

Download materials
Save for later
Share

Developers often use Flutter to develop mobile apps. Starting from version 2.10, it supports Windows as a target platform. Let’s check how to develop the production-level Windows apps with Flutter.

In this tutorial, you’ll learn how to set up a development environment and create a simple calculator app. We’ll focus on key concepts and basic business logic operations. In the process, you’ll learn:

  • Setting up environment.
  • Creating a Flutter desktop project.
  • Handling keyboard input.
  • Managing window size.

This tutorial covers the Windows platform only. But, almost everything should work on macOS and Linux. You only have to adjust the keyboard shortcuts.

Getting Started

To compile the Flutter desktop application for Windows, use a PC with Windows OS. A virtual machine like VMware or VirtualBox with a Windows guest also will work. Unfortunately, Flutter isn’t currently supporting cross-compilation.

To build Flutter apps on Windows you’ll need:

  1. Flutter SDK
  2. IDE which supports Flutter eg. Android Studio
  3. Visual Studio 2022

Installing Visual Studio

Note: Visual Studio is not the same as Visual Studio Code!

This tutorial doesn’t cover Flutter SDK and IDE setup processes. Instead, we assume you can already build Flutter mobile apps. If not, check the Windows install documentation of Flutter SDK and Android Studio.

When installing Visual Studio, select Desktop development with C++ workload. It should contain the MSVC v143 for your PC ABI (x64/x86 in case of Intel and compatible CPUs):
Visual Studio 2022 installation

Enabling Desktop Support in Flutter

Before you open or create a desktop project, enable the Windows support in Flutter. This is a global setting. You have to do it only once. Execute the following command in the terminal:

flutter config --enable-windows-desktop

Open and run the starter project from the attachment to this tutorial. You can press Shift+F10 in Android Studio. This will check whether your setup is correct. If all is OK, you should see the standard Flutter Demo app:
Flutter Demo app on Windows

If you see CMake compilation errors, it usually means inconsistent Visual Studio installation. Specific versions of Flutter only supports specific versions of Visual Studio and MSVC. In the case of Flutter 2.10, it’s Visual Studio 2022 and MSVC v143.

The physical calculator

Creating the Project

You’ll create the FCalc app, which is a simple calculator. It supports only integers (no fractions), two arithmetic operations: + and -, and clearing (AC).
You can enter data by clicking buttons, using a keyboard or pasting them from the system clipboard. The final layout looks like this:
FCalc app on Windows 11

Developing the Button

Start from the widget for a button. It’s an elementary building block of the calculator. First, add the following code in a separate file tile.dart.

import 'package:flutter/material.dart';

class Tile extends StatelessWidget {
  final String symbol;
  final Function(String) onTap;

  const Tile(
    this.symbol,
    this.onTap, {
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) => Material(
        child: InkWell(
          //1
          onTap: () => onTap(symbol),
          //2
          child: Center(
            child: Text(
              symbol,
              style: const TextStyle(
                fontSize: 26,
                color: Color(0xFF333333), //grey
              ),
            ),
          ),
        ),
        color: const Color(0xFFFCB526), //yellow
      );
}

It contains the following elements:

  1. A tap handler, mouse clicks and presses of widgets that currently have a focus (moved using Tab).
  2. Centered label using Ray Wenderlich brand colors.

The Material and InkWell widgets are here to provide a visual feedback on taps.

Developing the Grid of Buttons

Create a CalculatorBody widget with grid of Tile widgets and a placeholder for a display. It’s a skeleton of the calculator. Note the order of digits: 7, 8, 9, 6, 5 and so on. After that, place the following code inside a calculator_body.dart file, and press Alt+Enter to add missing imports:

class CalculatorBody extends StatelessWidget {

  CalculatorBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Column(
        //1
        textDirection: TextDirection.ltr,
        crossAxisAlignment: CrossAxisAlignment.end,

        children: [
          //TODO display
          GridView.count(
            //2
            shrinkWrap: true,
            //3
            padding: const EdgeInsets.all(20),
            //4
            crossAxisSpacing: 10,
            mainAxisSpacing: 10,
            //5
            crossAxisCount: 3,
            children: [
              Tile('7', (symbol) {
                //TODO action
              }),
              //TODO rest of the tiles
            ],
          ),
        ],
      );
}

Here’s what the code above does:

  1. Aligns the display on the right side. Even if the locale uses right-to-left direction, e.g., Arabic or Hebrew.
  2. Shrink the content to prevent infinite expansion.
  3. Set the gaps between the grid and window borders.
  4. Set the distance between adjacent buttons.
  5. Set number of columns.

Building the Business Logic

The business logic should have some unit tests and formal descriptions, e.g., using a state machine diagram. This article focuses on getting started on desktop app development, not on the arithmetic or the clean code. So it will only describe the algorithm shortly.

Start from creating the class for a calculator with ValueNotifier for an output. All the business logic will go there. Put the code in calculator.dart file.

class Calculator {
  final displayNotifier = ValueNotifier('0');
}

Note that CalculatorBody was a stateless widget. Only the value notifier holds the state.

You need two arithmetic operations. Add a simple enum for them in the same calculator.dart file:

enum Operator { plus, minus }

Developing the Calculation Engine

Now you’re ready to add the core logic, specifically the ability to append digits and operators to the current state. Add the following code to the Calculator class:

Operator? _pendingOperation;
int? _operand;
bool _wasDigitJustAppended = false;

void appendOperator(Operator operation) {
  if (_wasDigitJustAppended) {
    //1
    switch (_pendingOperation) {
      case Operator.plus:
        _updateDisplay(_operand! + int.parse(displayNotifier.value));
        break;
      case Operator.minus:
        _updateDisplay(_operand! - int.parse(displayNotifier.value));
        break;
      //2
      case null:
        _operand = int.parse(displayNotifier.value);
        break;
    }
  }
  _wasDigitJustAppended = false;
   //3
  _pendingOperation = operation;
}

void _updateDisplay(int result) {
  _operand = result;
  displayNotifier.value = result.toString();
}

void appendDigit(String digit) {
  //4
  if (!_wasDigitJustAppended) {
    displayNotifier.value = '';
  }
  //5
  _wasDigitJustAppended = true;
  displayNotifier.value += digit;
}

At the beginning, a display shows 0. There are no pending operations and no operands (numbers) on the stack. That’s because a user has not pressed any key yet. The result depends on what kind of input has arrived (a digit or an operation) and what kind of action was previously taken. If no previous actions were taken, we take the initial state into account.

The algorithm works as follows (the order of the lines of code doesn’t match the order of a typical use scenario):

  1. If an operation is pending, compute the result out of the first operand. Then, the current display content.
  2. If a user has pressed the plus or minus (and only digits before), remember the number entered so far.
  3. Remember the last operation as a new pending one.
  4. If a previous key (before a digit) was the plus or minus sign, clear the display. It contains the result of some operation, not the digits of the current number.
  5. If a user has pressed the digit, append it to a display.

Note the text (String) is a storage of a content of a display. We convert texts to numbers (int type) only before the arithmetic operations. It’s easier to build the multidigit numbers as texts if the user can append single digits.