Flutter build for windows

This page discusses considerations unique to building Windows apps with Flutter, including shell integration and distribution of Windows apps through the Microsoft Store on Windows.

Integrating with Windows

#

The Windows programming interface combines traditional Win32 APIs, COM interfaces and more modern Windows Runtime libraries. As all these provide a C-based ABI, you can call into the services provided by the operating system using Dart’s Foreign Function Interface library (dart:ffi). FFI is designed to enable Dart programs to efficiently call into C libraries. It provides Flutter apps with the ability to allocate native memory with malloc or calloc, support for pointers, structs and callbacks, and ABI types like long and size_t.

For more information about calling C libraries from Flutter, see C interop using dart:ffi.

In practice, while it is relatively straightforward to call basic Win32 APIs from Dart in this way, it is easier to use a wrapper library that abstracts the intricacies of the COM programming model. The win32 package provides a library for accessing thousands of common Windows APIs, using metadata provided by Microsoft for consistency and correctness. The package also includes examples of a variety of common use cases, such as WMI, disk management, shell integration, and system dialogs.

A number of other packages build on this foundation, providing idiomatic Dart access for the Windows registry, gamepad support, biometric storage, taskbar integration, and serial port access, to name a few.

More generally, many other packages support Windows, including common packages such as url_launcher, shared_preferences, file_selector, and path_provider.

Supporting Windows UI guidelines

#

While you can use any visual style or theme you choose, including Material, some app authors might wish to build an app that matches the conventions of Microsoft’s Fluent design system. The fluent_ui package, a Flutter Favorite, provides support for visuals and common controls that are commonly found in modern Windows apps, including navigation views, content dialogs, flyouts, date pickers, and tree view widgets.

In addition, Microsoft offers fluentui_system_icons, a package that provides easy access to thousands of Fluent icons for use in your Flutter app.

Lastly, the bitsdojo_window package provides support for «owner draw» title bars, allowing you to replace the standard Windows title bar with a custom one that matches the rest of your app.

Customizing the Windows host application

#

When you create a Windows app, Flutter generates a small C++ application that hosts Flutter. This «runner app» is responsible for creating and sizing a traditional Win32 window, initializing the Flutter engine and any native plugins, and running the Windows message loop (passing relevant messages on to Flutter for further processing).

You can, of course, make changes to this code to suit your needs, including modifying the app name and icon, and setting the window’s initial size and location. The relevant code is in main.cpp, where you will find code similar to the following:

cpp

Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"myapp", origin, size))
{
    return EXIT_FAILURE;
}

Replace myapp with the title you would like displayed in the Windows caption bar, as well as optionally adjusting the dimensions for size and the window coordinates.

To change the Windows application icon, replace the app_icon.ico file in the windows\runner\resources directory with an icon of your preference.

The generated Windows executable filename can be changed by editing the BINARY_NAME variable in windows/CMakeLists.txt:

cmake

cmake_minimum_required(VERSION 3.14)
project(windows_desktop_app LANGUAGES CXX)

# The name of the executable created for the application.
# Change this to change the on-disk name of your application.
set(BINARY_NAME "YourNewApp")

cmake_policy(SET CMP0063 NEW)

When you run flutter build windows, the executable file generated in the build\windows\runner\Release directory will match the newly given name.

Finally, further properties for the app executable itself can be found in the Runner.rc file in the windows\runner directory. Here you can change the copyright information and application version that is embedded in the Windows app, which is displayed in the Windows Explorer properties dialog box. To change the version number, edit the VERSION_AS_NUMBER and VERSION_AS_STRING properties; other information can be edited in the StringFileInfo block.

Compiling with Visual Studio

#

For most apps, it’s sufficient to allow Flutter to handle the compilation process using the flutter run and flutter build commands. If you are making significant changes to the runner app or integrating Flutter into an existing app, you might want to load or compile the Flutter app in Visual Studio itself.

Follow these steps:

  1. Run flutter build windows to create the build\ directory.

  2. Open the Visual Studio solution file for the Windows runner, which can now be found in the build\windows directory, named according to the parent Flutter app.

  3. In Solution Explorer, you will see a number of projects. Right-click the one that has the same name as the Flutter app, and choose Set as Startup Project.

  4. To generate the necessary dependencies, run Build > Build Solution

    You can also press/ Ctrl + Shift + B.

    To run the Windows app from Visual Studio, go to Debug > Start Debugging.

    You can also press F5.

  5. Use the toolbar to switch between Debug and Release configurations as appropriate.

Distributing Windows apps

#

There are various approaches you can use for distributing your Windows application. Here are some options:

  • Use tooling to construct an MSIX installer (described in the next section) for your application and distribute it through the Microsoft Windows App Store. You don’t need to manually create a signing certificate for this option as it is handled for you.
  • Construct an MSIX installer and distribute it through your own website. For this option, you need to give your application a digital signature in the form of a .pfx certificate.
  • Collect all of the necessary pieces and build your own zip file.

MSIX packaging

#

MSIX, the new Windows application package format, provides a modern packaging format and installer. This format can either be used to ship applications to the Microsoft Store on Windows, or you can distribute app installers directly.

The easiest way to create an MSIX distribution for a Flutter project is to use the msix pub package. For an example of using the msix package from a Flutter desktop app, see the Desktop Photo Search sample.

Create a self-signed .pfx certificate for local testing

#

For private deployment and testing with the help of the MSIX installer, you need to give your application a digital signature in the form of a .pfx certificate.

For deployment through the Windows Store, generating a .pfx certificate is not required. The Windows Store handles creation and management of certificates for applications distributed through its store.

Distributing your application by self hosting it on a website requires a certificate signed by a Certificate Authority known to Windows.

Use the following instructions to generate a self-signed .pfx certificate.

  1. If you haven’t already, download the OpenSSL toolkit to generate your certificates.
  2. Go to where you installed OpenSSL, for example, C:\Program Files\OpenSSL-Win64\bin.
  3. Set an environment variable so that you can access OpenSSL from anywhere:
    "C:\Program Files\OpenSSL-Win64\bin"
  4. Generate a private key as follows:
    openssl genrsa -out mykeyname.key 2048
  5. Generate a certificate signing request (CSR) file using the private key:
    openssl req -new -key mykeyname.key -out mycsrname.csr
  6. Generate the signed certificate (CRT) file using the private key and CSR file:
    openssl x509 -in mycsrname.csr -out mycrtname.crt -req -signkey mykeyname.key -days 10000
  7. Generate the .pfx file using the private key and CRT file:
    openssl pkcs12 -export -out CERTIFICATE.pfx -inkey mykeyname.key -in mycrtname.crt
  8. Install the .pfx certificate first on the local machine in Certificate store as Trusted Root Certification Authorities before installing the app.

Building your own zip file for Windows

#

The Flutter executable, .exe, can be found in your project under build\windows\runner\<build mode>\. In addition to that executable, you need the following:

  • From the same directory:

    • all the .dll files
    • the data directory
  • The Visual C++ redistributables. You can use any of the methods shown in the deployment example walkthroughs on the Microsoft site to ensure that end users have the C++ redistributables. If you use the application-local option, you need to copy:

    • msvcp140.dll
    • vcruntime140.dll
    • vcruntime140_1.dll

    Place the DLL files in the directory next to the executable and the other DLLs, and bundle them together in a zip file. The resulting structure looks something like this:

    Release
    │   flutter_windows.dll
    │   msvcp140.dll
    │   my_app.exe
    │   vcruntime140.dll
    │   vcruntime140_1.dll
    
    └───data
    │   │   app.so
    │   │   icudtl.dat
    
    ...

At this point if desired it would be relatively simple to add this folder to a Windows installer such as Inno Setup, WiX, etc.

Additional resources

#

To learn how to build an .exe using Inno Setup to distribute your Flutter desktop app for Windows, check out the step-by-step Windows packaging guide.

Was this page’s content helpful?

Thank you for your feedback! Please let us know what we can do to improve.

Provide details

Unless stated otherwise, the documentation on this site reflects the latest stable version of Flutter. Page last updated on 2024-04-12. View source or report an issue.

Software requirements

#

To write and compile Flutter code for desktop, you must have the following version of Windows and the listed software packages.

Operating system

#

Flutter supports 64-bit version of Microsoft Windows 10 or later. These versions of Windows should include the required Windows PowerShell 5 or later.

Development tools

#

Download and install the Windows version of the following packages:

  • Git for Windows 2.27 or later to manage source code.

  • Visual Studio 2022 to debug and compile native C++ Windows code. Make sure to install the Desktop development with C++ workload. This enables building Windows app including all of its default components. Visual Studio is an IDE separate from Visual Studio Code.

The developers of the preceding software provide support for those products. To troubleshoot installation issues, consult that product’s documentation.

When you run the current version of flutter doctor, it might list a different version of one of these packages. If it does, install the version it recommends.

Configure a text editor or IDE

#

You can build apps with Flutter using any text editor or integrated development environment (IDE) combined with Flutter’s command-line tools.

Using an IDE with a Flutter extension or plugin provides code completion, syntax highlighting, widget editing assists, debugging, and other features.

Popular options include:

  • Visual Studio Code 1.86 or later with the Flutter extension for VS Code.
  • Android Studio 2024.1.1 (Koala) or later with the Flutter plugin for IntelliJ.
  • IntelliJ IDEA 2024.1 or later with the Flutter plugin for IntelliJ.

Install the Flutter SDK

#

To install the Flutter SDK, you can use the VS Code Flutter extension or download and install the Flutter bundle yourself.

  • Use VS Code to install
  • Download and install

Use VS Code to install Flutter

#

To install Flutter using these instructions, verify that you have installed Visual Studio Code 1.86 or later and the Flutter extension for VS Code.

Prompt VS Code to install Flutter

#

  1. Launch VS Code.

  2. To open the Command Palette, press Control + Shift + P.

  3. In the Command Palette, type flutter.

  4. Select Flutter: New Project.

  5. VS Code prompts you to locate the Flutter SDK on your computer.

    1. If you have the Flutter SDK installed, click Locate SDK.

    2. If you do not have the Flutter SDK installed, click Download SDK.

      This option sends you the Flutter install page if you have not installed Git for Windows as directed in the development tools prerequisites.

  6. When prompted Which Flutter template?, ignore it. Press Esc. You can create a test project after checking your development setup.

Download the Flutter SDK

#

  1. When the Select Folder for Flutter SDK dialog displays, choose where you want to install Flutter.

    VS Code places you in your user profile to start. Choose a different location.

    Consider %USERPROFILE% or C:\dev.

  2. Click Clone Flutter.

    While downloading Flutter, VS Code displays this pop-up notification:

    Downloading the Flutter SDK. This may take a few minutes.

    This download takes a few minutes. If you suspect that the download has hung, click Cancel then start the installation again.

  3. Once it finishes downloading Flutter, the Output panel displays.

    Checking Dart SDK version...
    Downloading Dart SDK from the Flutter engine ...
    Expanding downloaded archive...

    When successful, VS Code displays this pop-up notification:

    Initializing the Flutter SDK. This may take a few minutes.

    While initializing, the Output panel displays the following:

    Building flutter tool...
    Running pub upgrade...
    Resolving dependencies...
    Got dependencies.
    Downloading Material fonts...
    Downloading Gradle Wrapper...
    Downloading package sky_engine...
    Downloading flutter_patched_sdk tools...
    Downloading flutter_patched_sdk_product tools...
    Downloading windows-x64 tools...
    Downloading windows-x64/font-subset tools...

    This process also runs flutter doctor -v. At this point in the procedure, ignore this output. Flutter Doctor might show errors that don’t apply to this quick start.

    When the Flutter install succeeds, VS Code displays this pop-up notification:

    Do you want to add the Flutter SDK to PATH so it's accessible
    in external terminals?
  4. Click Add SDK to PATH.

    When successful, a notification displays:

    The Flutter SDK was added to your PATH
  5. VS Code may display a Google Analytics notice.

    If you agree, click OK.

  6. To enable flutter in all PowerShell windows:

    1. Close, then reopen all PowerShell windows.
    2. Restart VS Code.

Download then install Flutter

#

To install Flutter, download the Flutter SDK bundle from its archive, move the bundle to where you want it stored, then extract the SDK.

  1. Download the following installation bundle to get the latest stable release of the Flutter SDK.

    (loading…)

    For other release channels, and older builds, check out the SDK archive.

    The Flutter SDK should download to the Windows default download directory: %USERPROFILE%\Downloads.

    If you changed the location of the Downloads directory, replace this path with that path. To find your Downloads directory location, check out this Microsoft Community post.

  2. Create a folder where you can install Flutter.

    Consider creating a directory at %USERPROFILE% (C:\Users\{username}) or %LOCALAPPDATA% (C:\Users\{username}\AppData\Local).

  3. Extract the file into the directory you want to store the Flutter SDK.

    PS C:\> Expand-Archive `
          –Path $env:USERPROFILE\Downloads\flutter_sdk_v1.0.0.zip `
          -Destination $env:USERPROFILE\dev\

    When finished, the Flutter SDK should be in the C:\user\{username}\dev\flutter directory.

Update your Windows PATH variable

#

Help

To run Flutter commands in PowerShell, add Flutter to the PATH environment variable. This section presumes that you installed the Flutter SDK in %USERPROFILE%\dev\flutter.

  1. Press Windows + Pause.

    If your keyboard lacks a Pause key, try Windows + Fn + B.

    The System > About dialog displays.

  2. Click Advanced System Settings > Advanced > Environment Variables…

    The Environment Variables dialog displays.

  3. In the User variables for (username) section, look for the Path entry.

    1. If the entry exists, double-click on it.

      The Edit Environment Variable dialog displays.

      1. Double-click in an empty row.

      2. Type %USERPROFILE%\dev\flutter\bin.

      3. Click the %USERPROFILE%\dev\flutter\bin entry.

      4. Click Move Up until the Flutter entry sits at the top of the list.

      5. Click OK three times.

    2. If the entry doesn’t exist, click New….

      The Edit Environment Variable dialog displays.

      1. In the Variable Name box, type Path.

      2. In the Variable Value box, type %USERPROFILE%\dev\flutter\bin

      3. Click OK three times.

  4. To enable these changes, close and reopen any existing command prompts and PowerShell instances.

Check your development setup

#

Help

Run Flutter doctor

#

The flutter doctor command validates that all components of a complete Flutter development environment for Windows.

  1. Open PowerShell.

  2. To verify your installation of all the components, run the following command.

As you chose to develop for desktop, you do not need all components. If you followed this guide, the result of your command should resemble:

Running flutter doctor...
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.29.1, on Microsoft Windows 11 [Version 10.0.22621.3155], locale en)
[✓] Windows version (Installed version of Windows is version 10 or higher)
[!] Android toolchain - develop for Android devices
[!] Chrome - develop for the web
[✓] Visual Studio - develop Windows apps (version 2022)
[!] Android Studio (not installed)
[✓] VS Code (version 1.96)
[✓] Connected device (1 available)
[✓] Network resources


! Doctor found issues in 3 categories.

Troubleshoot Flutter doctor issues

#

When the flutter doctor command returns an error, it could be for Flutter, VS Code, Visual Studio, the connected device, or network resources.

If the flutter doctor command returns an error for any of these components, run it again with the verbose flag.

Check the output for other software you might need to install or further tasks to perform.

If you change the configuration of your Flutter SDK or its related components, run flutter doctor again to verify the installation.

Start developing Windows desktop apps with Flutter

#

Congratulations. Having installed all prerequisites and the Flutter SDK, you can start developing Flutter apps for Windows desktop.

To continue on your learning journey, consult the following guides:

  • Learn how to write your first Flutter app
  • Flutter fundamentals docs

Manage your Flutter SDK

#

To learn more about managing your Flutter SDK install, consult the following resources.

  • Upgrade Flutter
  • Add Android compilation tools
  • Add web as a build target
  • Uninstall Flutter

Was this page’s content helpful?

Thank you for your feedback! Please let us know what we can do to improve.

Provide details

Unless stated otherwise, the documentation on this site reflects the latest stable version of Flutter. Page last updated on 2024-10-28. View source or report an issue.

Building a Flutter App for Windows: A Step-by-Step Guide

Flutter, Google’s UI toolkit for building natively compiled applications, has expanded its capabilities to include desktop platforms such as Windows. If you’re looking to build a Flutter app for Windows, this step-by-step guide will walk you through the process.

Prerequisites:

Before you begin, make sure you have the following prerequisites installed on your system:

  1. Flutter SDK:

    • Download and install the Flutter SDK from Flutter’s official website.
  2. Windows Development Environment:

    • Set up a Windows development environment with Visual Studio. Flutter desktop support relies on Visual Studio, so ensure it’s installed on your machine.
  3. Git:

    • Install Git from Git’s official website for version control and dependency management.

Steps to Build Flutter for Windows:

Step 1: Enable Windows Desktop Support

Open a terminal and execute the following command to enable Windows desktop support:

flutter config --enable-windows-desktop

Step 2: Check for Additional Requirements

Run the following command to check for additional requirements:

Address any reported issues by following the instructions provided by flutter doctor.

Step 3: Create a New Flutter Project

Create a new Flutter project by executing the following commands:

flutter create my_windows_app
cd my_windows_app

Step 4: Open Project in Visual Studio Code

Open the project in Visual Studio Code or your preferred IDE:

Step 5: Run the App on Windows

Run the app on Windows using the following command:

This command will launch your Flutter app on the Windows desktop.

Step 6: Build a Release Version

To build a release version of your app for Windows, use the following command:

This command generates an executable file in the build\windows directory.

Tips and Troubleshooting:

  • Visual Studio Issues:

    • Ensure that Visual Studio is properly installed and updated.
    • If issues arise, refer to the Flutter documentation for troubleshooting steps.
  • Plugin Compatibility:

    • Check if the plugins used in your Flutter project support Windows desktop.
  • Flutter Desktop Documentation:

    • Keep an eye on the official Flutter Desktop documentation for any updates or changes.
  • Community Support:

    • Join the Flutter community forums or GitHub discussions for help and to share experiences.

Building a Flutter app for Windows is an exciting endeavor, and as Flutter’s desktop support evolves, staying informed through official documentation and community resources is key. Happy coding!

Flutter is Google’s portable UI toolkit for crafting beautiful, natively compiled applications for mobile, web, and flutter desktop from a single codebase. Flutter is free and open-source, and it is utilized by developers and organizations all over the world. Dart, a client-optimized language for fast apps on any platform, is the language used to create Flutter.

  • I. Creating a New Flutter Project
  • II. Building the UI for login, registration, and home screens
  • III. Setting the up SQLite DB
  • IV. Implementing the Login Functionality
  • V. Implementing the Registration Functionality
  • VI. Persisting the database
  • VII. Storing the login state using Shared Preferences
  • VIII. Creating Database Helper for maintainability
  • IX. Building the Windows application
  • X. Customizing the application’s icon
  • XI. Source Code

In this tutorial, you will learn how to build a login App in flutter desktop using the SQLite database. This tutorial assumes you have prior knowledge of Dart and Flutter. This tutorial will work on Windows, MacOS, and Linux computers but a Windows computer is required to build the Windows app. To begin, make sure you have the following:

  1. A Windows device that has Flutter installed. You can follow the directions in the flutter desktop manual, which is located at https://docs.flutter.dev/get-started/install/windows, to install Flutter on your Windows device.
  2. A code editor. You may use Visual Studio Code (VS Code) or Android Studio as your editor but this tutorial uses VS Code; and
  3. A basic understanding of Dart, Flutter, and SQLite.

Overview:

Without further ado, let’s start building!

I. Creating a New Flutter Project

1. To create a new Flutter project, open the command prompt or Windows Powershell, type the following code and press enter. An Internet connection is required when creating a project. This command will create a folder in the directory you executed the command named “windows_login_app” or whatever you called your application.

2. Open the project folder in your preferred code editor. When working with a Flutter project, the most important folder to pay attention to is the lib folder where the entry point for every project can be found, the main.dart. This is where all your Dart code will go.

3. Open lib/main.dart.

Figure . The Flutter Project boilerplate code.

4. Examine the contents of the main.dart file. You will see that a boilerplate code has been provided. The current code in the widget MyApp creates a simple Flutter application that has an AppBar, a text in the center of the screen, and a floating action button that increments the value of the text.

Figure. The initial Flutter project.

5. To ensure that your setup is working, open the Run and Debug tab(Ctrl+Shift + t) on Visual Studio Code. Then, click the Run and Debug button to run and debug your application. See the image below.

Figure. Run and debug feature of VS Code.

After confirming that this step is successful, you may proceed with the next steps by building the user interface.

II. Building the UI for login, registration, and home screens

To effectively demonstrate how we can create a simple Windows app powered by a SQLite database, you will create three screens for the application: the login screen, the register screen, and the home screen.

The login screen will have a username and password TextFormField and two buttons for login and navigating to the register screen. The register screen will have the same contents as the login screen except for the addition of another TextFormField for the user’s name. Lastly, the home screen displays the text “Welcome to Flutter!” and a logout button.

Let’s get started.

1. We will begin by cleaning up the existing boilerplate code. The remaining codes should look as follows after removing all the codes from the main.dart:

import 'package:flutter/material.dart';

void main() async {

  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
@override
  Widget build(BuildContext context) {
      return MaterialApp(
   // hidet the debug banner shown at the top right of the screen
      debugShowCheckedModeBanner: false,
      title: 'Windows Login with SQLite DB',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      );
   }
}

2. Create a folder called screens inside of the lib folder. Create three files and name them login_screen.dart, register_screen.dart, and home_screen.dart. While there are no rules in naming dart files, prefixing “_screen” to the filename, makes it easier for you to have an idea of the contents of the file without opening them.

Figure. Created files for screens

3. Open login_screen.dart, and type in the codes as shown below:

import 'package:flutter/material.dart';
import 'package:windows_login_app/screens/home_screen.dart';
import 'package:windows_login_app/screens/register_screen.dart';

class LoginScreen extends StatefulWidget {
  const LoginScreen({super.key});

  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          decoration: BoxDecoration(
            border: Border.all(
              color: Colors.deepPurple,
            ),
            borderRadius: BorderRadius.circular(24),
          ),
          width: 400,
          child: Padding(
            padding: const EdgeInsets.all(24.0),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Text(
                  'Login to your Account',
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
                const SizedBox(
                  height: 48,
                ),
                // Username
                TextFormField(
                  decoration: InputDecoration(
                    hintText: 'Username',
                    prefixIcon: const Icon(Icons.alternate_email),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8.0),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 24,
                ),
                // Password
                TextFormField(
                  decoration: InputDecoration(
                    hintText: 'Password',
                    prefixIcon: const Icon(Icons.key),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8.0),
                    ),
                  ),
                  obscureText: true,
                ),
                const SizedBox(
                  height: 48,
                ),
                // Login button
                SizedBox(
                  height: 48,
                  width: double.infinity,
                  child: FilledButton(
                    style: FilledButton.styleFrom(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(8.0),
                      ),
                    ),
                    onPressed: () {
                      Navigator.of(context).push(MaterialPageRoute(
                          builder: (context) => const HomeScreen()));
                    },
                    child: const Text('LOGIN'),
                  ),
                ),
                const SizedBox(
                  height: 48,
                ),
                SizedBox(
                  height: 48,
                  width: double.infinity,
                  child: TextButton(
                    style: FilledButton.styleFrom(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(8.0),
                      ),
                    ),
                    onPressed: () {
                      //
                      Navigator.of(context).push(MaterialPageRoute(
                          builder: (context) => const RegisterScreen()));
                    },
                    child: const Text('Create Account'),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

This code will produce the following user interface. For now, we are setting the onPressed property of the login button to navigate to the home button. We will implement the login logic later.

Figure. The login screen

4. Open register_screen.dart, and type in the codes as shown below:

import 'package:flutter/material.dart';
import 'package:windows_login_app/screens/login_screen.dart';

class RegisterScreen extends StatefulWidget {
  const RegisterScreen({super.key});

  @override
  State<RegisterScreen> createState() => _RegisterScreenState();
}

class _RegisterScreenState extends State<RegisterScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          decoration: BoxDecoration(
            border: Border.all(
              color: Colors.deepPurple,
            ),
            borderRadius: BorderRadius.circular(24),
          ),
          width: 400,
          child: Padding(
            padding: const EdgeInsets.all(24.0),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Text(
                  'Register a New Account',
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
                const SizedBox(
                  height: 48,
                ),

                // User's name
                TextFormField(
                  decoration: InputDecoration(
                    hintText: 'Your name',
                    prefixIcon: const Icon(Icons.person),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8.0),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 24,
                ),

                // Username
                TextFormField(
                  decoration: InputDecoration(
                    hintText: 'Username',
                    prefixIcon: const Icon(Icons.alternate_email),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8.0),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 24,
                ),

                // Password
                TextFormField(
                  decoration: InputDecoration(
                    hintText: 'Password',
                    prefixIcon: const Icon(Icons.key),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8.0),
                    ),
                  ),
                  obscureText: true,
                ),
                const SizedBox(
                  height: 48,
                ),

                // Register
                SizedBox(
                  height: 48,
                  width: double.infinity,
                  child: FilledButton(
                    style: FilledButton.styleFrom(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(8.0),
                      ),
                    ),
                    onPressed: () {
                      Navigator.of(context).push(MaterialPageRoute(
                          builder: (context) => const LoginScreen()));
                    },
                    child: const Text('REGISTER'),
                  ),
                ),
                const SizedBox(
                  height: 48,
                ),

                // Back to login
                SizedBox(
                  height: 48,
                  width: double.infinity,
                  child: TextButton(
                    style: FilledButton.styleFrom(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(8.0),
                      ),
                    ),
                    onPressed: () {
                      //
                      Navigator.of(context).push(MaterialPageRoute(
                          builder: (context) => const LoginScreen()));
                    },
                    child: const Text('Back to login'),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

This code produces the following user interface. Like the login button, we are only navigating back to the login screen when we click the register button here.

Figure. The registration screen

5. Open home_screen.dart, and type in the codes as shown below:

import 'package:flutter/material.dart';
import 'package:windows_login_app/screens/login_screen.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'Home',
          style: TextStyle(
            color: Colors.white,
          ),
        ),
        backgroundColor: Theme.of(context).primaryColor,
        actions: [
          // logout button
          IconButton(
            onPressed: () {
              Navigator.of(context).pushAndRemoveUntil(
                MaterialPageRoute(
                  builder: (context) => const LoginScreen(),
                ),
                (route) => false,
              );
            },
            icon: const Icon(
              Icons.exit_to_app,
              color: Colors.white,
            ),
          ),
        ],
      ),
      body: Center(
        child: Text(
          'Welcome to Flutter!',
          style: Theme.of(context).textTheme.bodyLarge,
        ),
      ),
    );
  }
}

This produces the following user interface. The logout button currently navigates only to the login screen.

Figure. The home screen

6. Lastly, define a routes property inside your root widget (MyApp) and define the path that navigates to the specific screen. Set the initialRoute to the login screen.

import 'package:flutter/material.dart';

import 'package:windows_login_app/screens/home_screen.dart';
import 'package:windows_login_app/screens/login_screen.dart';
import 'package:windows_login_app/screens/register_screen.dart';


void main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // hidet the debug banner shown at the top right of the screen
      debugShowCheckedModeBanner: false,
      title: 'Windows Login with SQLite DB',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),

      // when the application loads, show the login screen if the user is not logged in
      // else show home
      initialRoute:'/login',

      // define the routes for login and home screen
      routes: {
        '/register': (context) => const RegisterScreen(),
        '/login': (context) => const LoginScreen(),
        '/': (context) => const HomeScreen(),
      },
    );
  }
}

Building user interfaces in Flutter is made easy through its widgets. Currently, there are 14 different categories of widgets. Visit the Widget index in the Flutter docs to discover more about widgets.(https://docs.flutter.dev/reference/widgets).

The codes above only prepare the user interface that the user will interact with the application. To get the application to work, you will need a working database to store and retrieve the user’s information. In the next step, you will install the SQLite package for Windows.

III. Setting the up SQLite DB

To get SQLite to work in your application, you must find a package that supports its functionality in the selected platform, in this case, Windows.

1. To do so, go to pub.dev and search for SQLite packages. Ensure that the package you will use has Windows in the supported Platform.

Based on the search result, sqflite_common_ffi seems to be the package for SQLite that supports the Windows platform. The most popular SQLite package does not support Windows.

2. To install, run the following code in the command prompt/Powershell in the root of your project directory:

3. Once installed, import it in the main.dart file and initialize the database. Update your main.dart file to the following code snippet:

import 'package:flutter/material.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:windows_login_app/screens/home_screen.dart';
import 'package:windows_login_app/screens/login_screen.dart';
import 'package:windows_login_app/screens/register_screen.dart';

/// create a reference for the sqlite database that
/// we can refer to in other parts of the app
late Database db;
void main() async {
// need to be called when initializing the app asynchronously
  WidgetsFlutterBinding.ensureInitialized();
// initialize the ffi loader
  sqfliteFfiInit();

// create a database in-memory
// no file will be created
  db = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath);
// create a users table where we will store the credentials of the user
  await db.execute('''
CREATE TABLE IF NOT EXISTS users (
     id INTEGER PRIMARY KEY,
     name TEXT,
     username TEXT NOT NULL UNIQUE,
     password TEXT NOT NULL
  )
''');
// insert sample record
  await db.insert('users', <String, Object?>{
    'name': 'Windows User',
    'username': 'user',
    'password': 'password',
  });

// retrieve the created record
  var result = await db.query('users');

// this should print the text in the debug console
// flutter: [{id: 1, name: Windows User, username: user, password: password}]
  print(result);

// close the database after creating
  await db.close();

  runApp(const MyApp());
}

Since SQLite requires asynchronous initialization, the main function needs to be converted into an asynchronous function by adding the async keyword. WidgetsFlutterBinding.ensureInitialized() needs to be called before any other asynchronous functions inside the main. sqfliteFFiInit() is used to initialize the file loader.

4. Next, we define and open the database using the databaseFactory.openDatabase(inMemoryDatabasePath) and store it to the db variable defined before the main function. This will make the db variable available to be imported to other files in the project.

The rest of the code is to ensure that the SQLite setup is working by creating a table and inserting and retrieving methods.

5. Run your application and check for the result printed in the Debug console, that way, you can be sure that your setup is correct.

For now, it is enough to use the inMemoryDatabasePath for testing the setup since persisting the database requires more packages and more configuration.

IV. Implementing the Login Functionality

1. In this next step, the login functionality will be implemented. Copy the code below to the body of the _LoginScreenState.

// create controllers for the TextFormField to capture user inputs  
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();

  @override
  void dispose() {
    // dispose of the controllers before the widget is disposed
    // to prevent memory leaks
    _usernameController.dispose();
    _passwordController.dispose();

    super.dispose();
  }

This defines controllers that we can assign to the respective TextFormField which is done by assigning the variables to the controller property. To avoid memory leaks, the controllers need to be disposed of before the widget is destroyed.

2. Below is the code snippet that defines a controller for Username TextFormfield:

 TextFormField(
                  controller: _usernameController,
                  decoration: InputDecoration(
                    hintText: 'Username',
                    prefixIcon: const Icon(Icons.alternate_email),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8.0),
                    ),
                  ),
                ),

3. Below is the code snippet that defines a controller for Password TextFormfield:

TextFormField(
                  controller: _passwordController,
                  decoration: InputDecoration(
                    hintText: 'Password',
                    prefixIcon: const Icon(Icons.key),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8.0),
                    ),
                  ),
                  obscureText: true,
                ),

4. Next, create a function called _login which will call the login function, and copy the code below. The login function first assigns the value of the controllers to their respective variables.

5. Then, a user is retrieved from the database using the query function. Since the login function is asynchronous, it should be checked whether the context is still mounted or not. If it’s not, the function will throw an error.

6. Lastly, the value of the user is checked if a user was found. If it does, a Snackbar is shown to the user informing that the username and password matched a record in the database and the application navigates to the home screen. If not, a Snackbar showing that no user was found is shown instead.

Future<void> _login() async {
    final username = _usernameController.text;
    final password = _passwordController.text;

    final user = await db.query(
      'users',
      where: 'username = ? AND password=?',
      whereArgs: [
        username,
        password,
      ],
    );

    if (!context.mounted) return;

    // provide feedback to the user on whether the query succeeded or failed
    if (user.isNotEmpty) {
      // User found, login successful
      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
        content: Text('Login successful!'),
        backgroundColor: Colors.green,
      ));

      // This code will replace the current route with the new HomeScreen
      // and remove all routes on top of it from the navigation stack.
      // This will prevent the user from clicking back button
      Navigator.of(context).pushAndRemoveUntil(
          MaterialPageRoute(builder: (context) => const HomeScreen()),
          (route) => false);
    } else {
      // User not found or incorrect password
      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
        content: Text('Incorrect username or password!'),
        backgroundColor: Colors.red,
      ));
    }
  }

You now have a simple working login system. In the next step, you will learn how to register a new user.

V. Implementing the Registration Functionality

In the last step, you could use the login function through a default user added during the creation of the user’s table. In this step, the user will be allowed to create a new account that can be used to log in.

1. Open the registration_screen.dart and copy the code below.

final TextEditingController _nameController = TextEditingController();
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();

  @override
  void dispose() {
    // ensure that the controllers are disposed off
    // to prevent memory leaks
    _nameController.dispose();
    _usernameController.dispose();
    _passwordController.dispose();

    super.dispose();
  }

The code is quite similar to the login_screen.dart except for the addition of the _nameController. Assign the respective controllers to the TextFormFields.

2. Next, define a register function, and copy the code below.

Future<void> _register() async {
    // store the values of controllers into variables for easy reference
    final name = _nameController.text;
    final username = _usernameController.text;
    final password = _passwordController.text;

    // result returns the ID of the latest inserted record
    int result = await db.insert(
      'users',
      <String, Object?>{
        'name': name,
        'username': username,
        'password': password
      },
      conflictAlgorithm: ConflictAlgorithm.replace,
    );

    if (!context.mounted) return;

    if (result > 0) {
      //
      // User found, login successful
      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
        content: Text('Successfully registered user!'),
        backgroundColor: Colors.green,
      ));

      // Navigate back to login screen so the user can test
      // the new account created
      Navigator.of(context).pushAndRemoveUntil(
          MaterialPageRoute(builder: (context) => const LoginScreen()),
          (route) => false);
    } else {
      //
      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
        content: Text('Failed to register user!'),
        backgroundColor: Colors.red,
      ));
    }
  }

If you noticed, the codes are quite similar to the login function, but this time the insert function is used. The conflictAlgorithm is important because this is how SQLite will resolve issues in the database operation.

In this insert transaction, if a similar record is found in the database, that record will be replaced with the incoming values.

3. Lastly, we display feedback to the user and go back to the login screen upon successful registration.

VI. Persisting the database

By now, you may have noticed that if you stop your application and restart it, the records you created through the register functionality are no longer present in the database. This is because you were using the inMemoryDatabasePath when you opened the database. As the variable name suggests, the inMemoryDatabasePath stores the data in memory, and so, once the application is restarted, the memory is cleared. Thus, you lose the existing data.

To persist the database, you need to create a file to store the data.

1. To do this, install the path and path_provider packages:

2. In the main.dart, define a file path that will serve as the destination for the database. Copy the code below and import the necessary packages above the file using the code below.

import 'dart:io' as io;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

3. Define the dbPath and replace the inMemoryDatabasePath with the new path.

// initialize the ffi loader
  sqfliteFfiInit();

//create path to store the database
  final io.Directory appDirectory = await getApplicationCacheDirectory();
  String dbPath = p.join(appDirectory.path, 'databases', 'mysqlite.db');

//verify the databse by navigating to this directory
  print(dbPath);
  
  //create the database in the dbPath
  db = await databaseFactoryFfi.openDatabase(dbPath);

4. Run your application and see the printed value on the Debug Console window. See the image below.

Now, even when you restart your application, because the database is saved into a file, you will be able to access it. However, when you restart the application, you may have noticed that even though you just logged in, you are still redirected to the login screen. In the next step, you will address this by persisting the logged-in information using another local storage solution, Shared Preferences.

Shared Preferences is a local storage solution that stores data in key-value pairs. Unlike SQLite, only the initialization and writing of data is asynchronous in Shared Preferences. Retrieval of data is synchronous.

Back to the application, you would want that when the application is started or restarted, the login state of the user will be preserved and ready for the next session.

1. Start by adding the shared_preferences to the dependencies of your project by running the code below.

2. In the main.dart file, create a variable to store the reference for the SharedPreferences instance. Inside the main function, initialize the SharedPreferences. The rest of the code has been omitted for brevity.

import 'package:shared_preferences/shared_preferences.dart';

late SharedPreferences sharedPreferences;
void main() async {
// need to be called when initializing the app asynchronously
  WidgetsFlutterBinding.ensureInitialized();

//innitialize shared preference
  sharedPreferences = await SharedPreferences.getInstance();

  runApp(const MyApp());
}

Full code » main()

import 'dart:io' as io;
import 'package:path/path.dart' as p;
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';

late Database db;
late SharedPreferences sharedPreferences;
void main() async {
// need to be called when initializing the app asynchronously
  WidgetsFlutterBinding.ensureInitialized();

//innitialize shared preference
  sharedPreferences = await SharedPreferences.getInstance();

  // initialize the ffi loader
  sqfliteFfiInit();

//create path to store the database
  final io.Directory appDirectory = await getApplicationCacheDirectory();
  String dbPath = p.join(appDirectory.path, 'databases', 'mysqlite.db');

  //create the database in the dbPath
  db = await databaseFactoryFfi.openDatabase(dbPath);

  runApp(const MyApp());
}

3. The next step is to store the login information inside the sharedPreferences variable. Copy the following code into the login function in the _LoginScreenState widget. The rest of the code was removed for brevity.

    if (user.isNotEmpty) {
      //store login state
      sharedPreferences.setBool('IS_LOGGED_IN', true);
}

4. The last step is to check for the IS_LOGGED_IN key in the MyApp widget and show the login or home screen in the initialRoute based on its value.

initialRoute: isLoggedIn == true ? '/' : '/login',
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    final bool? isLoggedIn = sharedPreferences.getBool('IS_LOGGED_IN');

    print('isLoggedIn $isLoggedIn');

    return MaterialApp(
      // hidet the debug banner shown at the top right of the screen
      debugShowCheckedModeBanner: false,
      title: 'Windows Login with SQLite DB',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),

      // when the application loads, show the login screen if the user is not logged in
      // else show home
      initialRoute: isLoggedIn == true ? '/' : '/login',

      // define the routes for login and home screen
      routes: {
        '/register': (context) => const RegisterScreen(),
        '/login': (context) => const LoginScreen(),
        '/': (context) => const HomeScreen(),
      },
    );
  }
}

5. To log out, all you have to do is clear the sharedPreferences when the logout button is pressed. In the home_screen.dart, edit the logout button, and copy the code inside onPressed property below.

  // logout button
          IconButton(
            onPressed: () {
              //clear the shared preference
              sharedPreferences.clear();
              // go back to login page
              Navigator.of(context).pushAndRemoveUntil(
                MaterialPageRoute(
                  builder: (context) => const LoginScreen(),
                ),
                (route) => false,
              );
            },
            icon: const Icon(
              Icons.exit_to_app,
              color: Colors.white,
            ),
          ),

Now, your user can log in and log out of the application, and when the application is restarted, their login status is preserved.

Congratulations! You have a fully functional application!

VIII. Creating Database Helper for maintainability

While you now have a functional application, the implementation of the database is not optimal. This is because you need to keep the database open the whole time your application is running! If you close the db anywhere else in your code, your succeeding database calls will throw an error that the database connection is closed.

While you can keep the database connection open, it can lead to unnecessary memory consumption especially when you are not frequently using it. In this case, after the user logs in and the user information is retrieved, you no longer need the database. However, in a real-world application, you would expect that you will be frequently using the database but not as frequently that you would want to keep it open all the time. After all, your application has other functionalities that will be using memory, right?

To address this problem, creating a singleton for the database reference is a good solution. A singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance. In the case of your application, it is to ensure that you are only opening and referencing a single connection to the database. This is done by checking whether a connection to the database has been opened before opening a new connection.

  1. Create a folder inside lib called helpers and create a new file called database_helper.dart.
  2. Copy the code below to this file.
import 'dart:io' as io;

import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';

class DatabaseHelper {
  // returns the instance of DatabaseHelper
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  // reference to the database marked as private
  static Database? _database;

  // factory method that returns an instance of DatabaseHelper
  // to ensure that DatabaseHelper has only one instance in the application
  DatabaseHelper._privateConstructor();

  // get the database, if it is not initialized, initialize it first
  Future<Database> get database async {
    if (_database != null) return _database!;

    _database = await _initDatabase();

    return _database!;
  }

  Future<Database> _initDatabase() async {
    // initialize the ffi loader to ensure that sqlite will work
    sqfliteFfiInit();

    // create path to store the database
    final io.Directory appDirectory = await getApplicationDocumentsDirectory();
    String dbPath = p.join(appDirectory.path, 'databases', 'mysqlite.db');

    final dbFactory = databaseFactoryFfi;

    // Open the database and return the reference
    return await dbFactory.openDatabase(
      dbPath,
      options: OpenDatabaseOptions(
        version: 1,
        onCreate: _onCreate,
      ),
    );
  }

  Future<void> _onCreate(
    Database db,
    int version,
  ) async {
    // create 'users' table where the user credential will be stored
    await db.execute('''
      CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY,
        name TEXT,
        username TEXT NOT NULL UNIQUE,
        password TEXT NOT NULL
      )
    ''');
  }
}

3. The function responsible for creating the singleton is _privateConstructor which returns an instance of the class. The private property _database stores the database connection which is initialized through the database getter which in turn calls the _initDatabase function. The database initialization has been moved to this file from main.dart.

4. In the _initDatabase function, the _onCreate method is called to create the users table if it does not exist yet. This is also a good place to put other database operations to create the tables for your database. The functions for createUser and loginUser have also been moved here to make it easier to refer to them in your code.

With that, you can replace the database initialization inside main.dart with a simple call to retrieve the database.

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:windows_login_app/helpers/database_helper.dart';
import 'package:windows_login_app/screens/home_screen.dart';
import 'package:windows_login_app/screens/login_screen.dart';
import 'package:windows_login_app/screens/register_screen.dart';

late SharedPreferences sharedPreferences;
void main() async {
// need to be called when initializing the app asynchronously
  WidgetsFlutterBinding.ensureInitialized();

  //initialize the database
  await DatabaseHelper.instance.database;

//innitialize shared preference
  sharedPreferences = await SharedPreferences.getInstance();

  runApp(const MyApp());
}

5. Inside your login function, you can replace the manual db reference with DatabaseHelper as shown below.

From:

    final user = await db.query(
      'users',
      where: 'username = ? AND password=?',
      whereArgs: [
        username,
        password,
      ],
    );

To:

 final user = await DatabaseHelper.instance
.loginUser(username: username, password: password);

6. And in the register function…

From:

   int result = await db.insert(
      'users',
      <String, Object?>{
        'name': name,
        'username': username,
        'password': password
      },
      conflictAlgorithm: ConflictAlgorithm.replace,
    );

To:

int result = await DatabaseHelper.instance
.createUser(name: name, username: username, password: password);

You can add more functions inside your DatabaseHelper that you can use in various parts of your application. This is a good pattern because it makes your code cleaner and more maintainable. Now, we have a maintainable application!

In the next step, you will learn how to build your Windows application so you can share it with your users.

IX. Building the Windows application

Building a Windows application in Flutter is simple. Just run the following command in your command prompt.

Figure. Initial build folder.

Currently, opening the windows_login_app.exe from the build folder will run your application. However, when you try to compress and distribute your application using the Release folder, you may have encountered that your application does not open. This is because if you looked at the documentation of the lite package you used, there is an additional instruction when used in Windows:

Figure. Additional instructions when building and distributing the app for different platforms.

This indicates that you need to download the sqlite3.dll and add it to your build folder inside the Release folder. You can download the file here: https://github.com/tekartik/sqflite/raw/master/sqflite_common_ffi/lib/src/windows/sqlite3.dll.

Your final Release folder should now look like this:

Figure. Final build directory

Finally, you can zip this entire folder and distribute it to other users so they can use your application. You can also use tools like Inno Setup to create a setup.exe installer for your application.

X. Customizing the application’s icon

Right now, the application uses the default Flutter icon. Of course, you would want your application to look unique. To achieve that, you can add your application icon.

To do so, you must have a .ico file of your icon. Ideally, it should have a dimension of 48×48 pixels. Replace the app_icon.ico file in windows/runner/resources with your file with the same name. If the name of the icon is other than app_icon.ico, proceed to change the IDI_APP_ICON value in the windows/runner/Runner.rc file to point to the new path.

Figure. Runner.rc

Figure. Final executable with an updated icon.

Note: If the application icon did not change after running the build command, run flutter clean first then run the flutter build windows –release again.

And you’re done! You have just created and built a Windows application using flutter desktop. To learn more about Flutter, you can visit the Flutter documentation at https://flutter.dev.

Keep coding!

Note: Currently, and quite possibly to remain the same in the future, the Flutter team does not have a plan to support 32-bit Windows as a target. See https://github.com/flutter/flutter/issues/37777 if you wish to track this issue

XI. Source Code

For your reference, you may download the source code from the Github repository.

For more flutter visit the Website Blog page » freecodespot.

Contents
  • Beta Snapshot in stable channel
  • Requirements
    • Additional Windows requirements
    • Additional macOS requirements
    • Additional Linux requirements
  • Create a new project
    • Set up
    • Create and run
      • Using an IDE
      • From the command line
  • Windows UWP
  • Build a release app
  • Distribution
    • Windows
      • MSIX packaging
      • Building your own zip file for Windows
    • macOS
    • Linux
  • Add desktop support to an existing Flutter app
  • macOS-specific support
    • Entitlements and the App Sandbox
      • Setting up entitlements
    • Hardened Runtime
  • Plugin support
    • Using a plugin
    • Writing a plugin
  • Samples and codelabs

Desktop support allows you to compile Flutter source code
to a native Windows, macOS, or Linux desktop app.
Flutter’s desktop support also extends to plugins—you
can install existing plugins that support the Windows,
macOS, or Linux platforms, or you can create your own.

Beta Snapshot in stable channel

To make it easier to try out desktop support for
Flutter, we are shipping a snapshot of Flutter’s
desktop beta on the stable channel.
This means that you can easily try desktop support
without needing to switch to the Flutter beta channel.
However, the snapshot included in the stable channel
won’t be updated until the next Flutter stable release.
If you want the latest version of desktop support,
you must switch to the Flutter beta channel.

Requirements

To create a Flutter application with desktop support,
you need the following software:

  • Flutter SDK. See the
    Flutter SDK installation instructions.
  • Optional: An IDE that supports Flutter.
    You can install Android Studio, IntelliJ IDEA,
    or Visual Studio Code and
    install the Flutter and Dart plugins
    to enable language support and tools for refactoring,
    running, debugging, and reloading your desktop app
    within an editor. See setting up an editor
    for more details.

Additional Windows requirements

For Windows desktop development,
you need the following in addition to the Flutter SDK:

  • Visual Studio 2019 (not to be confused with
    Visual Studio Code). For Win32 you need the
    “Desktop development with C++” workload installed,
    including all of its default components. For UWP
    you need the “Universal Windows Platform development”
    workload installed, with the optional UWP C++ tools.

Additional macOS requirements

For macOS desktop development,
you need the following in addition to the Flutter SDK:

  • Xcode
  • CocoaPods if you use plugins

Additional Linux requirements

For Linux desktop development,
you need the following in addition to the Flutter SDK:

  • Clang
  • CMake
  • GTK development headers
  • Ninja build
  • pkg-config

The easiest way to install the Flutter SDK along with these
dependencies is by using snapd.
For more information, see Installing snapd.

Once you have snapd, you can install Flutter
using the Snap Store, or at the command line:

$ sudo snap install flutter --classic

If snapd is unavailable on the Linux distro you’re using,
you might use the following command:

$ sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev

Create a new project

You can use the following steps
to create a new project with desktop support.

Set up

At the command line, perform the following commands to
make sure that you have the latest desktop support and that
it’s enabled. If you see “flutter: command not found”,
then make sure that you have installed the
Flutter SDK and that it’s in your path.

$ flutter config --enable-<platform>-desktop

Where <platform> is windows, macos, or linux:

$ flutter config --enable-windows-desktop
$ flutter config --enable-macos-desktop
$ flutter config --enable-linux-desktop

For Windows UWP desktop support perform the following commands to switch to
the dev channel, upgrade Flutter, and enable UWP.

$ flutter channel dev
$ flutter upgrade
$ flutter config --enable-windows-uwp-desktop

To ensure that desktop is enabled,
list the devices available.
You should see something like the following
(you’ll see Windows, macOS, or Linux,
depending on which platform you are running on):

$ flutter devices
1 connected device:

Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.18362.1082]
macOS (desktop)   • macos   • darwin-x64  • macOS 11.2 20D64 darwin-x64
Linux (desktop)   • linux   • linux-x64   • Linux

You might also run flutter doctor to see if there are
any unresolved issues. It should look something like
the following on Windows:

PS C:\> flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.6, on Microsoft Windows [Version 10.0.19042.804], locale en-AU)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2019 16.9.5)
[√] Android Studio (version 4.1.0)
[√] VS Code (version 1.56.2)
[√] Connected device (3 available)

! No issues found!

On macOS, you might see something like the following:

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.0.6, on macOS 11.3.1 20E241 darwin-x64, locale en)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.0)
[✓] VS Code (version 1.56.2)
[✓] Connected device (3 available)

• No issues found!

On Linux, you might see something like the following:

$ flutter doctor 
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, 1.27.0-1.0.pre, on Linux, locale en_AU.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Android Studio
[✓] Connected device (2 available)

If flutter doctor finds problems for a platform that
you don’t want to develop for, you can ignore those warnings.
You don’t have to install Android Studio and the Android SDK,
for example, if you’re writing a Linux desktop app.

After enabling desktop support, restart your IDE.
You should now see windows (desktop),
macOS (desktop), or linux (desktop)
in the device pulldown.

Create and run

Creating a new project with desktop support is no different
than creating a new Flutter project for other platforms.

Once you’ve configured your environment for desktop
support, you can create and run a desktop application
either in the IDE or from the command line.

Using an IDE

After you’ve configured your environment to support
desktop, make sure you restart the IDE if it was
already running.

Create a new application in your IDE and it automatically
creates iOS, Android, web, and desktop versions of your app.
From the device pulldown, select windows (desktop),
macOS (desktop), or linux (desktop)
and run your application to see it launch on the desktop.

From the command line

To create a new application that includes desktop support
(in addition to mobile and web support), run the following commands,
substituting myapp with the name of your project:

$ flutter create myapp
$ cd myapp

To launch your application from the command line,
enter one of the following commands from the top
of the package:

PS C:\> flutter run -d windows
$ flutter run -d macos
$ flutter run -d linux

Windows UWP

To get started with Windows UWP you need to be using Windows 10.
You need to install Visual Studio (not Visual Studio Code) with the
“Universal Windows Platform development” workload and the optional
Windows UWP C++ tools.

To configure Flutter for Windows UWP development,
perform the following commands to switch to
the dev channel, upgrade Flutter, and enable
Windows UWP desktop support.

PS C:\> flutter channel dev
PS C:\> flutter upgrade
PS C:\> flutter config --enable-windows-uwp-desktop

To create a new application, run the following commands:

PS C:\> flutter create myapp
PS C:\> cd myapp

Running Flutter with Windows UWP is complicated due to UWP’s
sandboxed runtime. You need to run an override for the sandbox
to enable the injection of Dart code into the running UWP
process to enable debugging and Hot Reload.

The suggested approach during development is to first run
flutter run -d winuwp from the command line, which will
give you a command that you need to run from a PowerShell
with Administrator privileges.

PS C:\myapp> flutter run -d winuwp
Launching lib\main.dart on Windows (UWP) in debug mode...
LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification [C:\src\flutter-projects\myapp\build\winuwp\runner_uwp\app.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VisualStudio\v16.0\AppxPackage\Microsoft.AppXPackage.Targets(3327,5): warning : APPX4001: Build property AppxBundlePlatforms is not explicitly set and is calculated based on currently building architecture. Use 'Create App Package' wizard or edit project file to set it. [C:\src\flutter-projects\myapp\build\winuwp\runner_uwp\app.vcxproj]
Building Windows UWP application...
Enable Flutter debugging from localhost.

Windows UWP apps run in a sandboxed environment. To enable Flutter debugging
and hot reload, you will need to enable inbound connections to the app from the
Flutter tool running on your machine. To do so:
  1. Launch PowerShell as an Administrator
  2. Enter the following command:
     checknetisolation loopbackexempt -is -n=[APP_CONTAINER_NAME]

Press "Y" once this is complete, or "N" to abort.:

Run this checknetisolation command as shown in a PowerShell
as Administrator. You can then leave this process running for
the length of your development session, restarting your UWP app
as required.

PS C:\> checknetisolation loopbackexempt -is -n=[APP_CONTAINER_NAME]

Network Isolation Debug Session started.
Reproduce your scenario, then press Ctrl-C when done.

Once you have this process running, you can deploy to
Windows UWP from within your IDE as normal, or run from
the command line as follows:

PS C:\myapp> flutter run -d winuwp

Build a release app

To generate a release build,
run one of the following commands:

PS C:\> flutter build windows
$ flutter build macos
$ flutter build linux

Distribution

We don’t recommend releasing a desktop
application until desktop support is stable,

however, here is some information that you
might still find useful.

Windows

There are various approaches you can use for
distributing your Windows application.
Here are some options:

  • Use tooling to construct an MSIX installer
    (described in the next section)
    for your application and distribute it through
    the Microsoft Windows App Store.
    You don’t need to manually create a signing
    certificate for this option as it is
    handled for you.
  • Construct an MSIX installer and distribute
    it through your own website. For this
    option, you need to to give your application a
    digital signature in the form of a
    .pfx certificate.
  • Collect all of the necessary pieces
    and build your own zip file.

MSIX packaging

MSIX, Microsoft Windows’ application package format,
provides a modern packaging experience to all Windows apps.
This format can either be used to ship applications
to Microsoft Windows’ Apps store, or you can
distribute application installers directly.

The easiest way to create an MSIX distribution
for a Flutter project is to use the
msix pub package.
For an example of using the msix package
from a Flutter desktop app,
see the Desktop Photo Search sample.

Create a self-signed .pfx certificate for local testing

For private deployment and testing with the help
of the MSIX installer, you need to give your application a
digital signature in the form of a .pfx certificate.

For deployment through the Windows Store,
generating a .pfx certificate is not required.
The Windows Store handles creation and management
of certificates for applications
distributed through its store.

Distributing your application by self hosting it on a
website requires a certificate signed by a
Certificate Authority known to Windows.

Use the following instructions to generate a
self-signed .pfx certificate.

  1. If you haven’t already, download the OpenSSL
    toolkit to generate your certificates.
  2. Go to where you installed OpenSSL, for example,
    C:\Program Files\OpenSSL-Win64\bin.
  3. Set an environment variable so that you can access
    OpenSSL from anywhere:
    "C:\Program Files\OpenSSL-Win64\bin"
  4. Generate a private key as follows:
    openssl genrsa -out mykeyname.key 2048
  5. Generate a certificate signing request (CSR)
    file using the private key:
    openssl req -new -key mykeyname.key -out mycsrname.csr
  6. Generate the signed certificate (CRT) file using
    the private key and CSR file:
    openssl x509 -in mycsrname.csr -out mycrtname.crt -req -signkey mykeyname.key -days 10000
  7. Generate the .pfx file using the private key and
    CRT file:
    openssl pkcs12 -export -out CERTIFICATE.pfx -inkey mykeyname.key -in mycrtname.crt
  8. Install the .pfx certificate first on the local machine
    in Certificate store as
    Trusted Root Certification Authorities
    before installing the app.

Building your own zip file for Windows

The Flutter executable, .exe, can be found in your
project under build\windows\runner\<build mode>\.
In addition to that executable, you need the following:

  • From the same directory:
    • all the .dll files
    • the data directory
  • The Visual C++ redistributables.
    You can use any of the methods shown in the
    deployment example walkthroughs on the Microsoft site
    to ensure that end users have the C++ redistributables.
    If you use the application-local option, you need to copy:

    • msvcp140.dll
    • vcruntime140.dll
    • vcruntime140_1.dll

    These 3 files can be found in C:\Windows\System32 if installed on your PC.
    Place the DLL files in the directory next to the executable
    and the other DLLs, and bundle them together in a zip file.
    The resulting structure will look something a little like this:

    Release
    │   flutter_windows.dll
    │   msvcp140.dll
    │   myapp.exe
    │   vcruntime140.dll
    │   vcruntime140_1.dll
    │
    └───data
    │   │   app.so
    │   │   icudtl.dat
    
    ...
    

At this point if desired it would be relatively simple to
add this folder to a Windows installer such as Inno Setup, WiX, etc.

macOS

To distribute your macOS application, you can either
distribute it through the macOS App Store,
or you can distribute the .app itself,
perhaps from your own website.
As of macOS 10.14.5, you need to notarize
your macOS application before distributing
it outside of the macOS App Store.

The first step in both of the above processes
involves working with your application inside of Xcode.
To be able to compile your application from inside of
Xcode you first need to build the application for release
using the flutter build command, then open the
Flutter macOS Runner application.

$ flutter build macos
$ open macos/Runner.xcworkspace

Once inside of Xcode, follow either Apple’s
documentation on notarizing macOS Applications,
or on distributing an application through the App Store.
You should also read through the
macOS-specific support
section below to understand how entitlements,
the App Sandbox, and the Hardened Runtime
impact your distributable application.

Linux

The executable binary can be found in your project under
build/linux/<build mode>/bundle/. Alongside your
executable binary in the bundle directory there are
two directories:

  • lib contains the required .so library files
  • data contains the application’s data assets,
    such as fonts or images

In addition to these files, your application also
relies on various operating system libraries that
it’s been compiled against.
You can see the full list by running ldd
against your application. For example,
assuming you have a Flutter desktop application
called linux_desktop_test you could inspect
the system libraries it depends upon as follows:

$ flutter build linux --release
$ ldd build/linux/release/bundle/linux_desktop_test

To wrap up this application for distribution
you need to include everything in the bundle directory,
and make sure the Linux system you are installing
it upon has all of the system libraries required.
This may be as simple as:

$ sudo apt-get install libgtk-3-0 libblkid1 liblzma5

For information on publishing a Linux application
to the Snap Store, see
Build and release a Linux application to the Snap Store.

As the tooling solidifies, stay tuned for updates
on other ways to distribute a Linux desktop app.

Add desktop support to an existing Flutter app

To add desktop support to an existing Flutter project,
run the following command in a terminal from the
root project directory:

$ flutter create --platforms=windows,macos,linux .

This adds the necessary desktop files and directories
to your existing Flutter project.
To add only specific desktop platforms,
change the platforms list to include only
the platform(s) you want to add.

macOS-specific support

The following information applies only to
macOS development.

Entitlements and the App Sandbox

macOS builds are configured by default to be signed,
and sandboxed with App Sandbox.
This means that if you want to confer specific
capabilities or services on your macOS app,
such as the following:

  • Accessing the internet
  • Capturing movies and images from the built-in camera
  • Accessing files

Then you must set up specific entitlements in Xcode.
The following section tells you how to do this.

Setting up entitlements

Managing sandbox settings is done in the
macos/Runner/*.entitlements files. When editing
these files, you shouldn’t remove the original
Runner-DebugProfile.entitlements exceptions
(that support incoming network connections and JIT),
as they’re necessary for the debug and profile
modes to function correctly.

If you’re used to managing entitlement files through
the Xcode capabilities UI, be aware that the capabilities
editor updates only one of the two files or,
in some cases, it creates a whole new entitlements
file and switches the project to use it for all configurations.
Either scenario causes issues. We recommend that you
edit the files directly. Unless you have a very specific
reason, you should always make identical changes to both files.

If you keep the App Sandbox enabled (which is required if you
plan to distribute your application in the App Store),
you need to manage entitlements for your application
when you add certain plugins or other native functionality.
For instance, using the file_chooser plugin
requires adding either the
com.apple.security.files.user-selected.read-only or
com.apple.security.files.user-selected.read-write entitlement.
Another common entitlement is
com.apple.security.network.client,
which you must add if you make any network requests.

Without the com.apple.security.network.client entitlement,
for example, network requests will fail with a message such as:

flutter: SocketException: Connection failed
(OS Error: Operation not permitted, errno = 1),
address = example.com, port = 443

For more information on these topics,
see App Sandbox and Entitlements
on the Apple Developer site.

Hardened Runtime

If you choose to distribute your application outside
of the App Store, you need to notarize your application
for compatibility with macOS 10.15+.
This requires enabling the Hardened Runtime option.
Once you have enabled it, you need a valid signing
certificate in order to build.

By default, the entitlements file allows JIT for
debug builds but, as with App Sandbox, you may
need to manage other entitlements.
If you have both App Sandbox and Hardened
Runtime enabled, you may need to add multiple
entitlements for the same resource.
For instance, microphone access would require both
com.apple.security.device.audio-input (for Hardened Runtime)
and com.apple.security.device.microphone (for App Sandbox).

For more information on this topic,
see Hardened Runtime on the Apple Developer site.

Plugin support

Flutter on the desktop supports using and creating plugins.

Using a plugin

To use a plugin that supports desktop,
follow the steps for plugins in using packages.
Flutter automatically adds the necessary native code
to your project, as with iOS or Android.

We recommend the following plugins,
which have been updated to work for desktop apps:

  • url_launcher
  • shared_preferences
  • path_provider

Use the following links to find all packages on pub.dev
that support desktop apps. These links lists all packages,
not just plugin packages.
(Remember that plugin packages, or plugins,
provide an interface to platform-specific services.)

  • Windows packages
  • macOS packages
  • Linux packages

Writing a plugin

When you start building your own plugins,
you’ll want to keep federation in mind.
Federation is the ability to define several
different packages, each targeted at a
different set of platforms, brought together
into a single plugin for ease of use by developers.
For example, the Windows implementation of the
url_launcher is really url_launcher_windows,
but a Flutter developer can simply add the
url_launcher package to their pubspec.yaml
as a dependency and the build process pulls in
the correct implementation based on the target platform.
Federation is handy because different teams with
different expertise can build plugin implementations
for different platforms.
You can add a new platform implementation to any
endorsed federated plugin on pub.dev,
so long as you coordinate this effort with the
original plugin author.

For more information, including information
about endorsed plugins, see the following resources:

  • Developing packages and plugins, particularly the
    Federated plugins section.
  • How to write a Flutter web plugin, part 2,
    covers the structure of federated plugins and
    contains information applicable to desktop
    plugins.
  • Modern Flutter Plugin Development covers
    recent enhancements to Flutter’s plugin support.
  • Federated Plugin proposal

Samples and codelabs

Write a Flutter desktop application
A codelab that walks you through building
a desktop application that integrates the GitHub
GraphQL API with your Flutter app.

You can run the following samples as desktop apps,
as well as download and inspect the source code to
learn more about Flutter desktop support.

Flutter Gallery running web app, repo
A samples project hosted on GitHub to help developers
evaluate and use Flutter. The Gallery consists of a
collection of Material design widgets, behaviors,
and vignettes implemented with Flutter.
You can clone the project and run Gallery as a desktop app
by following the instructions provided in the README.
Flokk announcement blogpost, repo
A Google contacts manager that integrates with GitHub and Twitter.
It syncs with your Google account, imports your contacts,
and allows you to manage them.
Photo Search app
A sample application built as a desktop application that
uses the following desktop-supported plugins:

  • file_chooser
  • menubar
  • url_launcher

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Как установить беспроводное подключение на windows xp
  • Как создать нового пользователя на windows 10 с правами администратора через командную строку
  • Как активировать windows 7 при помощи командной строки
  • Скрывать курсор мыши windows
  • Как принудительно изменить разрешение экрана windows 10