How to create the local chat app to support two sides

the local chat app to support two sides:

  • You (messages on the right, blue bubble)
  • Friend / Bot (messages on the left, grey bubble)

We’ll simulate a “friend reply” automatically whenever you send a message.


Step 2: Chat App with Sender & Receiver

Replace your main.dart with this:

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Chat App',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: ChatScreen(),
    );
  }
}

// Message Model
class Message {
  final String text;
  final bool isMe;

  Message({required this.text, required this.isMe});
}

class ChatScreen extends StatefulWidget {
  @override
  State<ChatScreen> createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final List<Message> _messages = [];
  final TextEditingController _controller = TextEditingController();

  void _sendMessage() {
    if (_controller.text.trim().isEmpty) return;

    setState(() {
      _messages.add(Message(text: _controller.text.trim(), isMe: true));
    });

    // clear input
    _controller.clear();

    // simulate friend reply after 1 second
    Future.delayed(const Duration(seconds: 1), () {
      setState(() {
        _messages.add(Message(text: "Friend: Got your message!", isMe: false));
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Chat App")),
      body: Column(
        children: [
          // chat messages
          Expanded(
            child: ListView.builder(
              reverse: true,
              itemCount: _messages.length,
              itemBuilder: (context, index) {
                final message = _messages[_messages.length - 1 - index];
                return Align(
                  alignment:
                      message.isMe ? Alignment.centerRight : Alignment.centerLeft,
                  child: Container(
                    margin: const EdgeInsets.all(8),
                    padding: const EdgeInsets.all(12),
                    decoration: BoxDecoration(
                      color: message.isMe ? Colors.blueAccent : Colors.grey[300],
                      borderRadius: BorderRadius.circular(10),
                    ),
                    child: Text(
                      message.text,
                      style: TextStyle(
                        color: message.isMe ? Colors.white : Colors.black,
                        fontSize: 16,
                      ),
                    ),
                  ),
                );
              },
            ),
          ),

          // input box
          Container(
            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            color: Colors.grey[200],
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      hintText: "Type a message...",
                      border: InputBorder.none,
                    ),
                    onSubmitted: (_) => _sendMessage(),
                  ),
                ),
                IconButton(
                  icon: const Icon(Icons.send, color: Colors.blue),
                  onPressed: _sendMessage,
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}

New Features

  • Messages have two sides:
    • Your messages → Right side (blue bubble, white text).
    • Friend messages → Left side (grey bubble, black text).
  • Automatic fake reply after 1 second.
  • Chat feels like a two-person conversation.

Create, Read, Update, Delete data store data inside the app memory (List)

Step 1: Store Data in a List

We’ll create a simple Contact Manager where you can:
Add contact
Update contact
Delete contact


Full Example: Contact Manager App

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const ContactManager(),
    );
  }
}

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

  @override
  State<ContactManager> createState() => _ContactManagerState();
}

class _ContactManagerState extends State<ContactManager> {
  final TextEditingController nameController = TextEditingController();
  final TextEditingController emailController = TextEditingController();

  List<Map<String, String>> contacts = [];

  // Add Contact
  void addContact() {
    setState(() {
      contacts.add({
        "name": nameController.text,
        "email": emailController.text,
      });
      nameController.clear();
      emailController.clear();
    });
  }

  // Update Contact
  void updateContact(int index) {
    nameController.text = contacts[index]["name"]!;
    emailController.text = contacts[index]["email"]!;

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text("Update Contact"),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(controller: nameController, decoration: const InputDecoration(labelText: "Name")),
            TextField(controller: emailController, decoration: const InputDecoration(labelText: "Email")),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () {
              setState(() {
                contacts[index] = {
                  "name": nameController.text,
                  "email": emailController.text,
                };
                nameController.clear();
                emailController.clear();
              });
              Navigator.pop(context);
            },
            child: const Text("Update"),
          ),
        ],
      ),
    );
  }

  // Delete Contact
  void deleteContact(int index) {
    setState(() {
      contacts.removeAt(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Contact Manager")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // Input Fields
            TextField(
              controller: nameController,
              decoration: const InputDecoration(labelText: "Name"),
            ),
            TextField(
              controller: emailController,
              decoration: const InputDecoration(labelText: "Email"),
            ),
            const SizedBox(height: 10),
            ElevatedButton(
              onPressed: addContact,
              child: const Text("Add Contact"),
            ),

            const SizedBox(height: 20),

            // Contact List
            Expanded(
              child: ListView.builder(
                itemCount: contacts.length,
                itemBuilder: (context, index) {
                  return Card(
                    child: ListTile(
                      title: Text(contacts[index]["name"]!),
                      subtitle: Text(contacts[index]["email"]!),
                      trailing: Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          IconButton(
                            icon: const Icon(Icons.edit, color: Colors.blue),
                            onPressed: () => updateContact(index),
                          ),
                          IconButton(
                            icon: const Icon(Icons.delete, color: Colors.red),
                            onPressed: () => deleteContact(index),
                          ),
                        ],
                      ),
                    ),
                  );
                },
              ),
            )
          ],
        ),
      ),
    );
  }
}

What Happens Here?

  1. Add → User types Name + Email → Click “Add Contact” → Saved into a List.
  2. Update → Click Edit → Opens a dialog → Change values → Saved.
  3. Delete → Click Delete → Removes contact.
  4. Contacts are displayed in a ListView.

How to make contact manager app to store data permanently

make your Contact Manager app store data permanently.
That means, even if you close the app and reopen, contacts will still be there.

Flutter has two common storage options:

  1. SQLite → Relational database (like MySQL/PostgreSQL).
    • Best for structured data (tables, queries).
    • Needs sqflite package.
  2. Hive → NoSQL, very fast, stores key–value pairs/objects.
    • Best for simple apps.
    • No SQL queries needed.

Since you’re just starting, I’ll teach you Hive first (easier). Later we can do SQLite.


Step 1: Add Hive to Your Project

Open pubspec.yaml and add dependencies:

dependencies:
  flutter:
    sdk: flutter
  hive: ^2.2.3
  hive_flutter: ^1.1.0

dev_dependencies:
  hive_generator: ^2.0.0
  build_runner: ^2.4.8

Run:

flutter pub get

Step 2: Initialize Hive in main.dart

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Hive
  await Hive.initFlutter();

  // Open a box (like a database table)
  await Hive.openBox('contactsBox');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const ContactManager(),
    );
  }
}

Step 3: Use Hive Box for CRUD

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

  @override
  State<ContactManager> createState() => _ContactManagerState();
}

class _ContactManagerState extends State<ContactManager> {
  final TextEditingController nameController = TextEditingController();
  final TextEditingController emailController = TextEditingController();

  final contactsBox = Hive.box('contactsBox');

  // Add Contact
  void addContact() {
    contactsBox.add({
      "name": nameController.text,
      "email": emailController.text,
    });
    nameController.clear();
    emailController.clear();
  }

  // Update Contact
  void updateContact(int index, Map contact) {
    nameController.text = contact["name"];
    emailController.text = contact["email"];

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text("Update Contact"),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(controller: nameController, decoration: const InputDecoration(labelText: "Name")),
            TextField(controller: emailController, decoration: const InputDecoration(labelText: "Email")),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () {
              contactsBox.putAt(index, {
                "name": nameController.text,
                "email": emailController.text,
              });
              nameController.clear();
              emailController.clear();
              Navigator.pop(context);
            },
            child: const Text("Update"),
          ),
        ],
      ),
    );
  }

  // Delete Contact
  void deleteContact(int index) {
    contactsBox.deleteAt(index);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Contact Manager")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(controller: nameController, decoration: const InputDecoration(labelText: "Name")),
            TextField(controller: emailController, decoration: const InputDecoration(labelText: "Email")),
            const SizedBox(height: 10),
            ElevatedButton(
              onPressed: addContact,
              child: const Text("Add Contact"),
            ),
            const SizedBox(height: 20),

            // Contacts List (auto updates when box changes)
            Expanded(
              child: ValueListenableBuilder(
                valueListenable: contactsBox.listenable(),
                builder: (context, box, _) {
                  return ListView.builder(
                    itemCount: box.length,
                    itemBuilder: (context, index) {
                      final contact = box.getAt(index) as Map;
                      return Card(
                        child: ListTile(
                          title: Text(contact["name"]),
                          subtitle: Text(contact["email"]),
                          trailing: Row(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              IconButton(
                                icon: const Icon(Icons.edit, color: Colors.blue),
                                onPressed: () => updateContact(index, contact),
                              ),
                              IconButton(
                                icon: const Icon(Icons.delete, color: Colors.red),
                                onPressed: () => deleteContact(index),
                              ),
                            ],
                          ),
                        ),
                      );
                    },
                  );
                },
              ),
            )
          ],
        ),
      ),
    );
  }
}

What Changed?

  • Instead of using a List, we use Hive Box (contactsBox).
  • add() → inserts new contact.
  • putAt() → updates contact at index.
  • deleteAt() → deletes contact.
  • ValueListenableBuilder → auto-refresh UI when data changes.
  • Data is saved permanently in your device storage.

how to add navigation (second screen), so when you click “Hire Me” button, it opens a new page with more details

Let’s add Navigation in Flutter so your “Hire Me” button opens a new page.

In Flutter, we use Navigator.push to move to another screen and Navigator.pop to go back.


Step 1: Create a Second Screen

We’ll make a new class HireMePage for the second page.

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const BusinessCard(),
    );
  }
}

// First Screen
class BusinessCard extends StatelessWidget {
  const BusinessCard({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blueGrey[900],
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const CircleAvatar(
              radius: 60,
              backgroundImage: NetworkImage("https://picsum.photos/200"),
            ),
            const SizedBox(height: 20),
            const Text(
              "Aditya Kumar Singh",
              style: TextStyle(fontSize: 28, color: Colors.white, fontWeight: FontWeight.bold),
            ),
            const Text(
              "Flutter Developer",
              style: TextStyle(fontSize: 18, color: Colors.white70, letterSpacing: 2.0),
            ),
            const SizedBox(height: 30),

            // Button with navigation
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const HireMePage()),
                );
              },
              child: const Text("Hire Me"),
            ),
          ],
        ),
      ),
    );
  }
}

// Second Screen
class HireMePage extends StatelessWidget {
  const HireMePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Hire Me")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              "Thank you for your interest!",
              style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 20),
            const Text("Email: adityaypi@yahoo.com"),
            const Text("Phone: +91 9555699081"),
            const SizedBox(height: 30),

            ElevatedButton(
              onPressed: () {
                Navigator.pop(context); // Go back
              },
              child: const Text("Go Back"),
            ),
          ],
        ),
      ),
    );
  }
}

What Happened Here?

  1. BusinessCard Page → first screen
    • When you tap Hire Me, it opens HireMePage.
  2. HireMePage → second screen
    • Has a “Go Back” button → returns to first screen.

Run this → Now your app behaves like a real professional app with multiple pages.

build a Mini Business Card App using the widgets (Text, Container, Row, Column, Image, Button)

Let’s build a Mini Business Card App using the widgets you just learned (Text, Container, Row, Column, Image, Button).

This will give you a real app-like UI to practice.


Business Card App (Flutter Example)

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        backgroundColor: Colors.blueGrey[900],
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // Profile Image
              const CircleAvatar(
                radius: 60,
                backgroundImage: NetworkImage(
                  "https://picsum.photos/200", // you can replace with your own photo
                ),
              ),

              const SizedBox(height: 20),

              // Name
              const Text(
                "Aditya Kumar Singh",
                style: TextStyle(
                  fontSize: 28,
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                ),
              ),

              // Job Title
              const Text(
                "Flutter Developer",
                style: TextStyle(
                  fontSize: 18,
                  color: Colors.white70,
                  letterSpacing: 2.0,
                ),
              ),

              const SizedBox(height: 30),

              // Contact Card - Phone
              Card(
                margin: const EdgeInsets.symmetric(horizontal: 25, vertical: 10),
                child: ListTile(
                  leading: const Icon(Icons.phone, color: Colors.blueGrey),
                  title: const Text(
                    "+91 9555699081",
                    style: TextStyle(fontSize: 18),
                  ),
                ),
              ),

              // Contact Card - Email
              Card(
                margin: const EdgeInsets.symmetric(horizontal: 25, vertical: 10),
                child: ListTile(
                  leading: const Icon(Icons.email, color: Colors.blueGrey),
                  title: const Text(
                    "adityaypi@yahoo.com",
                    style: TextStyle(fontSize: 18),
                  ),
                ),
              ),

              const SizedBox(height: 20),

              // Button
              ElevatedButton(
                onPressed: () {
                  print("Hire Me Button Pressed!");
                },
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.amber,
                  padding:
                      const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
                ),
                child: const Text(
                  "Hire Me",
                  style: TextStyle(fontSize: 18, color: Colors.black),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Features in This Mini App

Profile Picture (CircleAvatar)
Name + Job Title
Contact info (Phone & Email in nice cards)
“Hire Me” button

This app already looks like a professional mini portfolio card.

Now let’s start learning Flutter Widgets step by step using VS Code.

Now let’s start learning Flutter Widgets step by step using VS Code.
We’ll go from the very basics and build small UIs together .


Step 1: Text Widget

Shows simple text on screen.

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text("Text Widget Example")),
        body: const Center(
          child: Text(
            "Hello Flutter ",
            style: TextStyle(
              fontSize: 30,
              fontWeight: FontWeight.bold,
              color: Colors.blue,
            ),
          ),
        ),
      ),
    );
  }
}

Try it → You’ll see Hello Flutter in blue, bold, and big font.


Step 2: Container Widget

A box for styling (color, padding, margin, borders).

body: Center(
  child: Container(
    padding: const EdgeInsets.all(20),
    margin: const EdgeInsets.all(40),
    decoration: BoxDecoration(
      color: Colors.amber,
      borderRadius: BorderRadius.circular(15),
    ),
    child: const Text(
      "Inside a Container",
      style: TextStyle(fontSize: 22, color: Colors.white),
    ),
  ),
),

You’ll see a yellow rounded box with white text.


Step 3: Row & Column

For arranging widgets horizontally / vertically.

body: Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: const [
    Text("First Line", style: TextStyle(fontSize: 22)),
    Text("Second Line", style: TextStyle(fontSize: 22)),
    Text("Third Line", style: TextStyle(fontSize: 22)),
  ],
),

Changes ColumnRow to place them side by side.


Step 4: Image Widget

Show images.

body: Center(
  child: Image.network(
    "https://picsum.photos/200",
    width: 200,
    height: 200,
  ),
),

Loads a random internet image.
(You can also use Image.asset() for local images in your project.)


Step 5: ElevatedButton

For actions (like clicking).

body: Center(
  child: ElevatedButton(
    onPressed: () {
      print("Button Clicked!");
    },
    child: const Text("Click Me"),
  ),
),

When you press the button, it prints in Debug Console.


Now you know the 5 most important widgets:
Text, Container, Row, Column, Button

how to sign your APK/AAB with a keystore (so it’s ready for Play Store upload)

Let’s get your Flutter app signed so you can upload it to the Google Play Store.
By default, Flutter builds unsigned APKs, but Play Store requires a signed release.

Here’s the step-by-step


Step 1: Create a Keystore

Open Command Prompt (CMD/PowerShell) and run (replace your_name and your_company):

keytool -genkey -v -keystore C:\Users\<YourUsername>\my-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
  • It will ask for a password → remember it
  • It will generate a file my-key.keystore

Make sure to keep this file safe and backed up — you’ll need it for every future update of your app.


Step 2: Move Keystore to Project

Move my-key.keystore to your Flutter project, e.g.:

my_app/android/app/my-key.keystore

Step 3: Configure key.properties

Create a new file inside android folder:
android/key.properties

storePassword=your-password
keyPassword=your-password
keyAlias=my-key-alias
storeFile=my-key.keystore

Step 4: Update build.gradle

Open android/app/build.gradle and add this near the top:

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

Then inside android { signingConfigs { ... } }, add:

signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
        storePassword keystoreProperties['storePassword']
    }
}

And inside buildTypes { ... } update release:

buildTypes {
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        shrinkResources true
        // optionally enable proguard if needed
    }
}

Step 5: Build Signed APK/AAB

Now run:

flutter build apk --release

Output:

build/app/outputs/flutter-apk/app-release.apk

Or for Play Store AAB:

flutter build appbundle --release

Output:

build/app/outputs/bundle/release/app-release.aab

Now your APK/AAB is signed and ready to upload to Google Play Console

No Android SDK found. Try setting the ANDROID_HOME environment variable. in Visual Studio Code

absolutely! You can build Android APKs using Flutter with VS Code.
VS Code is just your editor, all the heavy work (compiling & building APK) is done by the Flutter SDK + Android SDK.

Here’s the full process


Step 1: Make Sure Android SDK is Installed

  • Install Android Studio (for Android SDK + tools).
  • Confirm SDK path: C:\Users\<YourName>\AppData\Local\Android\Sdk
  • Set ANDROID_HOME environment variable to this path.
  • Run: flutter doctor It should show [√] Android toolchain.

Step 2: Create Flutter Project in VS Code

In VS Code terminal:

flutter create my_app
cd my_app
code .

Step 3: Build Debug APK (for testing only)

flutter build apk

Output:

build/app/outputs/flutter-apk/app-debug.apk

Step 4: Build Release APK (for sharing / publishing)

flutter build apk --release

Output:

build/app/outputs/flutter-apk/app-release.apk

This APK can be installed on any Android device.


Step 5: Build Android App Bundle (for Play Store)

Google Play Store now prefers .aab files instead of .apk:

flutter build appbundle --release

Output:

build/app/outputs/bundle/release/app-release.aab

This is the file you upload to Google Play Console.


Step 6: Run Directly on Device

  • Connect your Android phone (enable USB Debugging).
  • Or start Android Emulator.
  • Run: flutter run

So yes, using Flutter + VS Code, you can:

  • Run on emulator/real phone
  • Build .apk for testing/sharing
  • Build .aab for Play Store publishing

how to package your Windows Flutter app into a professional installer (.exe setup wizard like normal software)

now you’re entering the deployment stage
By default, Flutter gives you only a .exe (inside build/windows/x64/runner/Release).
If you want a professional installer (like normal Windows apps that come with Next → Next → Finish wizard), you need a packaging tool.

There are 3 popular ways:


Option 1: Using MSIX (Recommended by Microsoft)

  1. Add msix package to your project: flutter pub add msix
  2. Configure pubspec.yaml: msix_config: display_name: My Flutter App publisher_display_name: My Company identity_name: com.mycompany.myapp msix_version: 1.0.0.0 logo_path: windows\runner\resources\app_icon.ico
  3. Build the installer: flutter pub run msix:create

Output: AppName.msix
This is a modern Windows installer (like apps from Microsoft Store).
Users can just double-click to install.


Option 2: Using Inno Setup (Classic .exe Installer)

  1. Download Inno Setup.
  2. Create a script installer.iss: [Setup] AppName=My Flutter App AppVersion=1.0 DefaultDirName={pf}\MyFlutterApp DefaultGroupName=My Flutter App OutputDir=installer OutputBaseFilename=MyFlutterAppSetup Compression=lzma SolidCompression=yes [Files] Source: "build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: recursesubdirs [Icons] Name: "{group}\My Flutter App"; Filename: "{app}\my_app.exe"
  3. Run the script in Inno Setup → it generates MyFlutterAppSetup.exe.
    This is the classic wizard installer (Next → Install → Finish).

Option 3: NSIS (Nullsoft Installer)

  • Install NSIS.
  • Write an .nsi script.
  • Compile it → .exe installer.
    (Used by apps like Winamp, FileZilla, etc.)

Best Choice?

  • If you want modern Windows style → use MSIX.
  • If you want classic .exe setup wizard → use Inno Setup.

how to build application for windows in flutter

Flutter can also build desktop apps for Windows (along with Android, iOS, Web, Linux, macOS). Let me show you how to build a Windows .exe application step by step


Step 1: Enable Windows Desktop Support

First, check if your Flutter installation supports Windows builds:

flutter doctor

You should see something like:

[√] Windows (desktop) • Windows version 10.x

If Windows is not enabled, run:

flutter config --enable-windows-desktop

Step 2: Create a Flutter Project

flutter create my_app
cd my_app

This creates a folder windows/ inside your project (it has native C++ code Flutter uses to build Windows apps).


Step 3: Run App on Windows

From your project folder:

flutter run -d windows

This will open your app as a native Windows desktop window.


Step 4: Build Release .exe

To generate a Windows executable (.exe):

flutter build windows

After build, your .exe will be here:

build/windows/x64/runner/Release/my_app.exe

You can share this .exe file (but if you want a proper installer like .msi or `.exe setup wizard**, you’ll need to use a packaging tool — e.g. Inno Setup, NSIS, or MSIX Packaging Tool).


Step 5: Package as Installer (Optional)

  • For quick sharing → just give the .exe file inside the Release folder.
  • For professional app distribution → package using MSIX (official Windows installer format).

Example command (if you install the msix tool):

flutter pub add msix
flutter pub run msix:create

This creates a Windows installer (.msix) you can double-click and install.


Now your Flutter app works like a real Windows application!