The power of Flutter lies in its ability to create applications for multiple platforms from a single codebase. When combined with Firebase, this power extends to creating cloud-connected apps quickly and efficiently.
In this guide, we’ll demonstrate this versatility by building a user registration app (name and CPF). Although our example focuses on compiling for Windows, the same principles apply to web, mobile, and other desktop platforms with very few changes.
Prerequisites
Before you begin, ensure you have your environment set up:
- The Flutter SDK installed and added to your PATH.
- Support for Windows development enabled in Flutter. You can check if everything is correct with the
flutter doctor
command. If anything is missing in the “Windows” section, follow the instructions provided by the command itself. - A Google account to access Firebase.
- Node.js and npm installed. They are required for the Firebase CLI. Download the LTS version from the official nodejs.org website.
Step 1: Preparing the Firebase Environment
This step prepares your computer to communicate with Firebase. If you’ve done this before, you can skip to the next step.
Install the CLI globally via npm. This adds the Firebase commands to your system:
npm install -g firebase-tools
Log in to your Google account to authenticate your machine:
firebase login
This command will open a window in your browser for authentication.
Step 2: Configure the Project in the Firebase Console
- Go to the Firebase Console.
- Click “Add project” and give it a name (e.g.,
flutter-firebase-db-example
). - Follow the setup steps. You don’t need to enable Google Analytics for this tutorial.
- In the left menu, go to Build > Firestore Database and click “Create database”.
- Select “Start in test mode”.
- Warning: Test mode allows anyone to read and write to your database. It’s great for development, but for a production app, you must configure more restrictive security rules.
- Choose a location for your data and click “Enable”.
Step 3: Creating the Flutter Project
With the environment ready, let’s create our Flutter project.
- Open your terminal or command prompt.
- Run the command below to create the project:
flutter create flutter_firebase_db_example
- Navigate into the newly created project folder. All subsequent commands should be run from here.
cd flutter_firebase_db_example
Step 4: Add Dependencies in Flutter
Now, let’s add the necessary packages for our Flutter app to communicate with Firebase. The quickest and safest way to do this is through the terminal.
- In the root of your project, run the command below to add the
firebase_core
andcloud_firestore
packages at once:This command will automatically find the latest versions of the packages, add them to yourflutter pub add firebase_core cloud_firestore
pubspec.yaml
file, and runflutter pub get
for you.
Troubleshooting on Windows: If you encounter an error like
ERROR_INVALID_FUNCTION
when running the command above, it’s very likely that your Flutter project is on a different drive than your SDK (e.g., project on theD:
drive and SDK on theC:
drive). To fix this, move your project folder to the same drive as the Flutter SDK (usuallyC:
) and try again.
Step 5: Connect the App to Firebase with FlutterFire
The FlutterFire CLI automates the process of connecting your project to Firebase.
Install the FlutterFire CLI (if you haven’t already):
dart pub global activate flutterfire_cli
Attention to the “Warning” on Windows: After running the command above, you might see a warning that the
...Pub\Cache\bin
directory is not in your “Path”. This warning is important! It means the terminal won’t find theflutterfire
command in the next step.How to fix it:
- Search for “Environment Variables” in the Windows Start Menu and open “Edit the system environment variables”.
- In the window that opens, click “Environment Variables…”.
- In the “User variables” section, find and select the
Path
variable and click “Edit…”. - Click “New” and paste the path that appeared in your warning (usually
C:\Users\YOUR_USER\AppData\Local\Pub\Cache\bin
). - Click “OK” on all windows to save.
- Close and reopen your terminal for the changes to take effect.
In the root of your project, run the configuration command:
flutterfire configure
The command will detect your Firebase project and ask which platforms you want to configure. Make sure to select the
windows
option. It will then generate thelib/firebase_options.dart
file with the correct keys for your desktop application.Tip: You don’t have to select all platforms at once. The best practice is to select only the ones you are currently developing for (like
windows
for this guide). If you decide to build your app for Android, iOS, or web in the future, just run theflutterfire configure
command again and add the new platform. The choice is not permanent!
Step 6: The Application Code
Now for the fun part! Replace the entire content of your lib/main.dart
file with the complete code below. It creates the interface to list and add users.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'firebase_options.dart'; // Imports the configuration generated by FlutterFire
// Main entry point of the application
void main() async {
// Ensures that the Flutter bindings are initialized
WidgetsFlutterBinding.ensureInitialized();
// Initializes Firebase using the options for the current platform (Windows, in this case)
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Desktop App with Firestore',
theme: ThemeData(
primarySwatch: Colors.indigo,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const HomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Controllers for the dialog text fields
final TextEditingController _nomeController = TextEditingController();
final TextEditingController _cpfController = TextEditingController();
// Reference to the 'usuarios' collection in Firestore
final CollectionReference _usuarios = FirebaseFirestore.instance.collection(
'usuarios',
);
// Function to display the add user dialog
Future<void> _mostrarDialogoAdicionar() async {
// Clears the controllers before opening the dialog
_nomeController.clear();
_cpfController.clear();
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add New User'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _nomeController,
decoration: const InputDecoration(labelText: 'Name'),
),
TextField(
controller: _cpfController,
decoration: const InputDecoration(labelText: 'CPF'),
),
],
),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
ElevatedButton(
child: const Text('Add'),
onPressed: () {
final String nome = _nomeController.text;
final String cpf = _cpfController.text;
if (nome.isNotEmpty && cpf.isNotEmpty) {
// Adds a new document to the 'usuarios' collection
_usuarios.add({"nome": nome, "cpf": cpf});
// Closes the dialog
Navigator.of(context).pop();
}
},
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('User Registration')),
// StreamBuilder listens to changes in the Firestore collection in real-time
body: StreamBuilder(
stream: _usuarios.snapshots(), // The stream to listen to
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
// If there is data, build a ListView
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
title: Text(documentSnapshot['nome']),
subtitle: Text(documentSnapshot['cpf']),
),
);
},
);
}
// While data is loading, show a progress indicator
return const Center(child: CircularProgressIndicator());
},
),
// Floating action button to add new users
floatingActionButton: FloatingActionButton(
onPressed: () => _mostrarDialogoAdicionar(),
child: const Icon(Icons.add),
),
);
}
}
Note: The complete source code for this project is available on GitHub: flutter_firebase_db_example.
Step 7: Test and Build the Executable
With the code ready, let’s run and compile our application for Windows.
Test in debug mode:
flutter run -d windows
A native Windows window will open with your application. Try adding a few users and see the real-time magic happen.
Generate the executable (
.exe
):flutter build windows
After completion, Flutter will tell you where to find the executable. It will usually be in the
build\windows\runner\Release
folder. You can take the entire contents of this folder, zip it, and distribute it to other Windows computers!
Conclusion
Congratulations! You have created a native desktop application for Windows with a cloud database, using a single codebase with Flutter. This demonstrates the incredible flexibility of the tool, allowing you to bring your ideas to virtually any platform without rewriting everything from scratch.