Flutter AI-Powered expense tracker Using Firebase AI

Introduction

What if your expense tracker didn’t require you to manually fill out forms?

Instead of selecting a category, entering an amount, and saving the expense yourself, you could simply say:

“I spent ₹500 on dinner.”

The application understands what you mean, identifies the required information, and saves the expense.

In this tutorial, we’ll build PocketPilot AI, a Flutter-based AI expense tracker using Firebase AI and a local SQLite database.

The interesting part isn’t just adding an AI chatbot to a Flutter application.

The AI can actually call specific functions provided by our application.

For example:

  • add_expense() — adds an expense to the database
  • get_category_total() — retrieves the total spent in a category

The AI decides when one of these functions is required, while the Flutter application remains responsible for executing the actual database operation.

What We Are Building

The final application looks like a simple AI-powered expense tracker.

Instead of interacting with traditional forms, the user can communicate naturally with the application.

For example:

I spent ₹500 on dinner

The AI understands:

Amount: 500
Category: Dinner
Description: Dinner

It then calls our add_expense function.

We can also ask:

How much have I spent on food?

The AI can call:

get_category_total

and return the result in a natural-language response.

The overall architecture looks like this:

User
  ↓
Flutter UI
  ↓
Firebase AI / Gemini
  ↓
Function Call
  ↓
AiService
  ↓
SQLite Database
  ↓
Function Response
  ↓
Firebase AI / Gemini
  ↓
Natural Language Response

This is the important concept behind the application.

The AI doesn’t directly access our database.

Instead, we expose controlled functions that the AI is allowed to request.

This project uses:

  • Flutter
  • Dart
  • Firebase AI
  • Gemini
  • Firebase App Check
  • SQLite
  • Material 3
  • UUID for expense IDs

The main Firebase packages used in the project include:

firebase_core: ^4.14.0
firebase_ai: ^4.0.0

Create the Flutter Project

Create a new Flutter project:

flutter create ai_expense
cd ai_expense

Then configure Firebase for the project using FlutterFire.

Once Firebase is configured, the project contains the generated firebase_options.dart file.

Initialize Firebase

The application starts from main.dart.

We don’t need a lot of code here.

The important part is initializing Firebase before launching the application.

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.android,
  );

  await FirebaseAppCheck.instance.activate(
    providerAndroid: AndroidDebugProvider(),
  );

  runApp(const PocketPilotApp());
}

The application then loads the main HomeScreen.

home: const HomeScreen(),

The App Check configuration shown above is intended for Android development. For a production application, App Check should be configured appropriately for the production environment.

Create the Expense Model

We need a model representing an expense.

The application stores information such as:

ID
Amount
Category
Description
Date

A simplified version looks like:

class Expense {
  final String id;
  final double amount;
  final String category;
  final String description;
  final DateTime date;
} 

This model is then stored in our local SQLite database.

Store Expenses in SQLite

The database layer is kept separate from the AI logic.

Our AppDatabase handles operations such as:

Add expense
Get expenses
Calculate category total

This separation is important.

The AI should not know how SQLite works.

It only needs to know:

“I have a function available that can add an expense.”

The database layer takes care of the implementation.

Connect Firebase AI

Now we reach the most interesting part of the application.

Our AiService is responsible for communicating with the Gemini model and handling function calls.

The service uses Firebase AI rather than placing a Gemini API key directly inside the Flutter application.

Conceptually, the service contains:

Firebase AI
      ↓
Gemini model
      ↓
Function declarations
      ↓
Tool call
      ↓
Database

We define the functions that the model is allowed to use.

Give the AI Tools

This is the key concept of the entire tutorial.

We provide the model with two functions.

add_expense

This function receives:

amount
category
description

and stores the expense in SQLite.

get_category_total

This function receives:

category

and returns the amount spent in that category.

The model doesn’t execute Dart code itself.

Instead, it produces a function call request.

Our application receives that request and executes the corresponding Dart function.

How add_expense Works

Suppose the user says:

I spent ₹500 on dinner.

The model can determine that the add_expense function should be called.

Conceptually, the request looks like:

{
  "amount": 500,
  "category": "Dinner",
  "description": "Dinner"
}

Our Flutter application receives these arguments.

The AiService creates an Expense object and sends it to:

database.addExpense(...)

The database then stores the expense.

The important part is that the AI requested the operation, but the application executed it.

How get_category_total Works

Now imagine the user asks:

How much have I spent on food?

The model can decide that it needs the get_category_total function.

The function receives the category:

food

Our database then calculates the total.

For example:

Food = ₹1,300 

The result is returned to the AI.

The AI can then convert that result into a natural-language response such as:

You've spent ₹1,300 on food.

This creates a very natural interaction between the user and the application.

The Function-Calling Flow

The complete process looks like this:

Step 1 — User sends a message

I spent ₹500 on dinner.

Step 2 — AI analyzes the request

The model determines that an expense needs to be created.

Step 3 — AI requests a function

add_expense(
    amount = 500,
    category = "Dinner",
    description = "Dinner"
)

Step 4 — Flutter executes the function

Our AiService calls the database.

AppDatabase
    ↓
SQLite

Step 5 — Database returns the result

The application sends the function result back to the model.

Step 6 — AI generates the response

The user sees a normal conversational response.

Got it! I've added ₹500 to your dinner expenses.

This is much more powerful than simply sending a prompt to an AI model and displaying its text response.

Why Use Tools Instead of Letting AI Modify the Database?

This is one of the most important design decisions in the application.

We don’t want the AI to have unrestricted access to our database.

Instead, we expose specific operations.

For example:

AI
 │
 ├── add_expense()
 │
 └── get_category_total()

The AI can request these operations, but the actual implementation remains inside our application.

This gives us much more control over what the AI is allowed to do.

We could later add more functions such as:

delete_expense()
get_recent_expenses()
update_expense()
get_monthly_total()

without changing the fundamental architecture.

The Flutter UI

The HomeScreen provides the user interface.

The user can type a natural-language request instead of manually filling out an expense form.

For example:

I spent ₹250 on coffee

or:

Show me my grocery spending

The screen sends the message to AiService.

The AI service handles the communication with Firebase AI and the function-calling flow.

This keeps the UI layer relatively simple.

Project Structure

The project is organized around separate responsibilities:

lib/
│
├── main.dart
├── firebase_options.dart
│
├── screens/
│   └── home_screen.dart
│
├── services/
│   └── ai_service.dart
│
├── data/
│   └── database.dart
│
└── models/
    └── expense.dart

The important idea is separation of responsibilities:

UI
 ↓
AI Service
 ↓
Database
 ↓
SQLite

This makes the project easier to maintain and also makes it easier to extend later.

Try the Application

After configuring Firebase, run:

flutter pub get
flutter run

Then try messages such as:

I spent ₹500 on dinner
I spent ₹800 on groceries
I spent ₹250 on coffee

And finally:

How much have I spent on food?

The interesting part is that you don’t explicitly tell the application which function to call.

The AI determines which available tool is relevant.

What Makes This Different From a Chatbot?

A traditional AI chatbot mainly does this:

User → AI → Text

Our application does this:

User
  ↓
AI
  ↓
Tool
  ↓
Application Logic
  ↓
Database
  ↓
AI
  ↓
User

That’s a significant difference.

We’re not simply adding an AI chat screen to an existing application.

We’re allowing the AI to interact with controlled capabilities of the application.

This pattern can be used far beyond expense trackers.

For example:

AI + Banking App
AI + Shopping App
AI + Fitness App
AI + Productivity App
AI + Travel App
AI + CRM

The same concept applies:

Give the AI tools, execute those tools inside your application, and return the results to the model.

Video Tutorial :

Source Code

You can find the complete project on GitHub:

GitHub: will post soon…

The repository contains the complete Flutter project, including the Firebase AI integration, database layer, expense model, and UI.

Conclusion

In this project, we built an AI-powered expense tracker using Flutter and Firebase AI.

The most important concept wasn’t the UI or even the database.

It was AI function calling.

Instead of allowing the AI to directly manipulate application data, we gave it controlled functions:

add_expense()
get_category_total()

The AI decides which function is required.

Flutter executes the function.

SQLite stores or retrieves the data.

The result is sent back to the AI.

And the AI converts that result into a natural conversation with the user.

This architecture opens up many possibilities for building AI-powered mobile applications where the AI doesn’t just talk to the user — it can actually operate the application through controlled tools.

If you’re interested in building more AI-powered Flutter applications, this is a pattern worth understanding.

More Flutter Tutorials

Find more interesting flutter tutorials here

Leave a Comment