This is the sixth part of Expenses Manager app Flutter tutorial series where we are building an Expenses Manager app.
Introduction
In the previous post, we completed all the functionalities of creating Category. In this part of our Flutter tutorial, we will add features for working with expenses. We will design the home page and also create a category selector using ChoiceChip widget.
Designing Dashboard Page
The home tab of our Expense Manager app will be a dashboard page. In this page, we will have:
- Link to add new expense.
- A Date Switcher.
- List of expenses for a selected date.
Designing A Date Switcher
We will start by adding a date switcher for our app. Basically, we want to show current date and add buttons to change current date. The buttons should allow user to go back and forth by a day.
With the date switcher, the dashboard can start to look like this:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class DashboardPage extends StatelessWidget {
String getStringDate(DateTime dt) {
return "${dt.year}/${dt.month}/${dt.day}";
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[
Container(
padding: EdgeInsets.all(12.0),
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
onPressed: () {},
icon: Icon(Icons.arrow_back),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 12.0),
child: Text(
getStringDate(DateTime.now()),
style: Theme.of(context).textTheme.title,
),
),
IconButton(
onPressed: () {},
icon: Icon(Icons.arrow_forward),
)
],
))
]),
);
}
}
We have yet to implement onPressed functionality for icon buttons.
Add FloatingActionButton To Add Expenses
Next, we will add a button to add expenses. A FloatingActionButton would be the right choice for this purpose since it will be distinctly visible.
...
floatingActionButton: FloatingActionButton(
onPressed: (){},
child: Icon(Icons.add)
),
...
Add ListView To Show Expenses
Finally, we will have a list of expenses for the selected date shown. For this, we will use a ListView with sample expenses for now.
Widget _getExpenses() {
var expense1 = ExpenseModel().rebuild((b) => b
..id = 1
..title = "Coffee"
..notes = "Coffee at peepalbot"
..amount = 129.00);
var expense2 = ExpenseModel().rebuild((b) => b
..id = 2
..title = "Lunch"
..notes = "Momos at dilli bazar"
..amount = 150.00);
var expense3 = ExpenseModel().rebuild((b) => b
..id = 3
..title = "Pants"
..notes = "Bought a pair of pants from Dbmg"
..amount = 2500.00);
var ls = [expense1, expense2, expense3];
return ListView.builder(
itemCount: ls.length,
itemBuilder: (context, index) {
var expense = ls[index];
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.0),
border: new Border.all(
width: 1.0, style: BorderStyle.solid, color: Colors.white)),
margin: EdgeInsets.all(12.0),
child: ListTile(
onTap: () {},
trailing: IconButton(
icon: Icon(Icons.delete),
color: Theme.of(context).primaryColorLight,
onPressed: () {},
),
title: Text(
expense.title + " - Rs." + expense.amount.toString(),
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Theme.of(context).accentColor),
),
subtitle: Text(
expense.notes,
),
),
);
},
);
}
Our design so far should look like this:

Adding New Expense
Now let’s add functionality to the dashboard page. First we will add functionality to add new expense. Similar to what we have done for category, we will create a separate route for adding expenses.
A product business logic component will handle UI interactions as well as expenses data stream. Also the CategoryBloc
shall be used to fetch list of categories as well.
Since, most of the functionalities for creating new expense is going to be similar to that of creating category, we will not repeat everything here. You can refer to the previous posts regarding:
- BLoC pattern implementation and
- Working with StreamBuilder and data stream.
Rather, here we will build a Category selector.
Using ChoiceChip Widget In Flutter
Since, every expense will fall under a category, when creating a new expense we will have to select a category. For this selection we can make use of the ChoiceChip widget. This widget can enable to select one item from a list of chip widgets.
Get Category List By Listening To Category Stream
Use the StreamBuilder like before to listed to category list stream.
StreamBuilder(
stream: categoryBloc.categoryListStream,
builder: (_, AsyncSnapshot<BuiltList<CategoryModel>> snap) {
You can access individual category model using the list index.
var categoryModel = snap.data[index];
Create ChoiceChip Widget
And in your ChoiceChip
widget, you can use this model to show text as well as to set selected property.
ChoiceChip(
selectedColor: Theme.of(context).accentColor,
selected: categoryModel.id == selectedCategoryId,
label: Text(categoryModel.title),
onSelected: (selected) {
setState(() {
selectedCategoryId = categoryModel.id;
});
},
)
Complete Implementation Using ChoiceChip Widget
The complete implementation could look like this:
import 'package:built_collection/built_collection.dart';
import 'package:expense_manager/blocs/category_bloc.dart';
import 'package:expense_manager/db/services/category_service.dart';
import 'package:expense_manager/models/category_model.dart';
import 'package:flutter/material.dart';
class AddExpense extends StatefulWidget {
@override
_AddExpenseState createState() => _AddExpenseState();
}
class _AddExpenseState extends State<AddExpense> {
CategoryBloc categoryBloc;
@override
void initState() {
super.initState();
categoryBloc = CategoryBloc(CategoryService());
}
int selectedCategoryId = 0;
@overrideW
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add New Expense"),
),
body: Container(
padding: EdgeInsets.all(12.0),
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 12.0),
child: Text("Pick Category", style: Theme.of(context).textTheme.title,)),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.white,
),
child: StreamBuilder(
stream: categoryBloc.categoryListStream,
builder: (_, AsyncSnapshot<BuiltList<CategoryModel>> snap) {
if (!snap.hasData)
return Center(
child: CircularProgressIndicator(),
);
return Wrap(
children: List.generate(snap.data.length, (int index) {
var categoryModel = snap.data[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 2.0,),
child: ChoiceChip(
selectedColor: Theme.of(context).accentColor,
selected: categoryModel.id == selectedCategoryId,
label: Text(categoryModel.title),
onSelected: (selected) {
setState(() {
selectedCategoryId = categoryModel.id;
});
},
),
);
}));
},
),
)
],
),
));
}
}
Here, we are using the setState function to rebuild the entire page on each category selection. You can also avoid the entire rebuild by putting the value of selected categoryId inside a stream and using a StreamBuilder
.

Link To Previous Posts
GitHub Link
Checkout the entire project from GitHub.
Similar Tutorial Series:
This was really looking like the best tutorial ever,
but got a bit lost on the last page, it would be good if you had a link to the code or could have shown full code and maybe gone into things a bit more in part six. everything was working up to part six, but now i am stuck, i will try to solve some more, but would be good if you could link to code or update this page a bit.
Thanks.
Paul.
Thank you Paul, I am glad this was helpful to you. I have updated the post with a link to the complete project in github.
Good luck,
Sovit
Thank you Sovit
This is the best tutorial I found in the subject, thank you very much. Please make more parts, I would love to see how you will do the complete app.
Best regards, Ana
Here I am on the last part. Can’t tell you how grateful I am. This is one of the most thorough tutorial I have come across since started learning Flutter. Big THANK YOU to you!!!
Hello Sovit, on the create expense page, there would be two stream builders, one for the category choicechip implementation, another one for the expense create/update. What do I do to bring selectedCategoryId into the expense stream builder? Wouldn’t a future builder be better for the category choiceship?
Should I just update the expense snapshot data with the selectedCategoryId before the expense entry creation?
Hi Dan,
It has been a while that this post was published. Can you take a look at the complete code repo itself from the github for reference?
https://github.com/stacksecrets/expense_manager
Thank you. I have figured that one out. 🙂
Hello Sovit, on this dashboard page, I would display the icon within the listtile. On calling CategoryBloc(CategoryService()).getCategories(), it returns null. What would be an effective method to fetch the iconCodePoint information from Category?
Hello Sovit, problem solved by using a futurebuilder. I am still wondering if it’s possible to use the categoryBloc created earlier.
Yes Dan, you should be able to subscribe to the `categoryListStream` from the CategoryBloc. You can use it with a StreamBuilder or a FutureBuilder.
Thank you Sovit.
I have been trying to use ‘where’ inside a _expenseBloc.expenseListStream StreamBuilder to filter the expenses when the date switcher pressed. No success yet. Not sure what to do with the returned lazy iterable with ‘where’ applied to the snapshot data. Any suggestion?