Lesson 2: Creating Your First Class
Duration: 30 minutes
App Feature: π° Building the Expense Class
What You'll Build: Create an Expense class to represent a single expense
Prerequisites: Complete Lesson 1
What We'll Learn Today
By the end of this lesson, you'll be able to:
- β Write your first Dart class
- β Understand properties (instance variables)
- β Create objects from your class
- β Use constructors to initialize objects
- β Add methods to make objects do things
- β Work with multiple expense objects
Review: What is a Class?
Remember from Lesson 1:
- A class is a blueprint/template
- An object is an actual instance created from that class
- Example:
Expense
class β your coffee purchase object
Today we're going to actually write this code ourselves!
Part 1: Your First Class (The Basic Structure)
Let's start with the simplest possible Expense
class:
class Expense {
// Properties (also called instance variables or fields)
String description;
double amount;
String category;
DateTime date;
}
Breaking It Down:
class Expense {
-
class
keyword tells Dart we're creating a new class -
Expense
is the name (always start with capital letter!) -
{
opens the class body
String description;
double amount;
String category;
DateTime date;
- These are properties - data that each expense will store
-
String
= text (description, category) -
double
= decimal number (amount like 4.50) -
DateTime
= date and time
}
- Closes the class body
π― Try It Yourself:
Create a file called expense.dart
and type this class. Pay attention to:
- Capital E in
Expense
- Semicolons
;
after each property - Proper indentation (2 or 4 spaces)
Part 2: Creating Objects (Instances)
Now that we have our class, let's create actual expense objects!
void main() {
// Create an object using the class
var coffee = Expense();
// Set the properties
coffee.description = 'Morning coffee';
coffee.amount = 4.50;
coffee.category = 'Food';
coffee.date = DateTime.now();
// Print the details
print('I spent \$${coffee.amount} on ${coffee.description}');
}
Understanding the Code:
var coffee = Expense();
-
var
= variable (Dart figures out the type) -
coffee
= variable name (our choice) -
Expense()
= calling the class to create a new object - The
()
is important - it creates a new instance
coffee.description = 'Morning coffee';
- Use dot notation (
.
) to access properties - Set each property one by one
DateTime.now()
- Gets the current date and time
- Perfect for tracking when you made the expense
π― Try It Yourself:
Add this code below your class and run it. You should see:
I spent $4.50 on Morning coffee
Part 3: The Problem with Our Current Code
Our code works, but there's a problem. Try running this:
void main() {
var expense = Expense();
print('Amount: ${expense.amount}');
}
What happens? You'll get an error!
Error: Non-nullable instance field 'amount' must be initialized.
This is because we created an expense but didn't set its properties. Dart doesn't know what values to use!
We need a better way to create objects...
Part 4: Introducing Constructors
A constructor is a special method that runs when you create an object. It initializes (sets up) the object with data.
Basic Constructor:
class Expense {
String description;
double amount;
String category;
DateTime date;
// Constructor - same name as the class
Expense(String desc, double amt, String cat, DateTime dt) {
description = desc;
amount = amt;
category = cat;
date = dt;
}
}
void main() {
// Now we MUST provide values when creating an object
var coffee = Expense('Coffee', 4.50, 'Food', DateTime.now());
print('${coffee.description}: \$${coffee.amount}');
}
Understanding Constructors:
Expense(String desc, double amt, String cat, DateTime dt) {
- Constructor has the same name as the class
- Takes parameters (the values we need)
- No return type (not even
void
)
description = desc;
amount = amt;
- Inside the constructor, we assign parameter values to properties
var coffee = Expense('Coffee', 4.50, 'Food', DateTime.now());
- Now we pass values when creating the object
- Order matters! (description, amount, category, date)
Part 5: Dart's Shortcut Constructor (The Better Way!)
Dart has a shortcut that makes constructors much cleaner:
class Expense {
String description;
double amount;
String category;
DateTime date;
// Shortcut constructor using 'this'
Expense(this.description, this.amount, this.category, this.date);
}
This does the same thing as before, but in one line!
-
this.description
means "set the description property to the value passed in" - Much cleaner and easier to read
- This is the style most Dart developers use
π― Try It Yourself:
Update your Expense
class to use the shortcut constructor and create 3 different expenses:
void main() {
var coffee = Expense('Coffee', 4.50, 'Food', DateTime.now());
var lunch = Expense('Lunch at cafe', 12.75, 'Food', DateTime.now());
var netflix = Expense('Netflix subscription', 15.99, 'Entertainment', DateTime.now());
print('${coffee.description}: \$${coffee.amount}');
print('${lunch.description}: \$${lunch.amount}');
print('${netflix.description}: \$${netflix.amount}');
}
Part 6: Adding Methods (Making Objects DO Things)
So far our objects just hold data. Let's make them do something useful!
A method is a function inside a class that can work with the object's data.
class Expense {
String description;
double amount;
String category;
DateTime date;
Expense(this.description, this.amount, this.category, this.date);
// Method to print expense details
void printDetails() {
print('ββββββββββββββββββββ');
print('π Description: $description');
print('π΅ Amount: \$$amount');
print('π Category: $category');
print('π
Date: ${date.toString().split(' ')[0]}');
print('ββββββββββββββββββββ');
}
}
void main() {
var coffee = Expense('Morning coffee', 4.50, 'Food', DateTime.now());
// Call the method
coffee.printDetails();
}
Understanding Methods:
void printDetails() {
-
void
= doesn't return anything -
printDetails
= method name (use camelCase) -
()
= no parameters needed
print('π΅ Amount: \$$amount');
- Inside methods, we can directly access properties
- No need for
this.amount
, justamount
-
\$
escapes the dollar sign so it prints literally
coffee.printDetails();
- Call methods using dot notation
- Don't forget the
()
!
π― Try It Yourself:
Add the printDetails()
method and create 3 expenses. Call printDetails()
on each:
void main() {
var coffee = Expense('Coffee', 4.50, 'Food', DateTime.now());
var uber = Expense('Uber to work', 8.20, 'Transport', DateTime.now());
var movie = Expense('Movie ticket', 12.00, 'Entertainment', DateTime.now());
coffee.printDetails();
uber.printDetails();
movie.printDetails();
}
Part 7: Methods That Return Values
Methods can also calculate and return values:
class Expense {
String description;
double amount;
String category;
DateTime date;
Expense(this.description, this.amount, this.category, this.date);
void printDetails() {
print('ββββββββββββββββββββ');
print('π $description');
print('π΅ \$$amount');
print('π $category');
print('π
${date.toString().split(' ')[0]}');
print('ββββββββββββββββββββ');
}
// Method that returns a boolean
bool isMajorExpense() {
return amount > 100;
}
// Method that returns a String
String getSummary() {
return '$description: \$$amount [$category]';
}
// Method that returns a formatted amount
String getFormattedAmount() {
return '\$${amount.toStringAsFixed(2)}';
}
}
void main() {
var laptop = Expense('New laptop', 899.99, 'Electronics', DateTime.now());
var coffee = Expense('Coffee', 4.50, 'Food', DateTime.now());
// Using methods that return values
print(laptop.getSummary());
print('Is major expense? ${laptop.isMajorExpense()}');
print('Formatted: ${laptop.getFormattedAmount()}');
print('');
print(coffee.getSummary());
print('Is major expense? ${coffee.isMajorExpense()}');
}
Output:
New laptop: $899.99 [Electronics]
Is major expense? true
Formatted: $899.99
Coffee: $4.50 [Food]
Is major expense? false
Understanding Return Values:
bool isMajorExpense() {
return amount > 100;
}
-
bool
= this method returns true or false -
return
sends the value back - Can use the returned value in conditions or print it
String getSummary() {
return '$description: \$$amount [$category]';
}
-
String
= returns text - Builds a nice formatted string
- Return value can be stored in a variable or printed directly
Part 8: Complete Example
Here's our complete Expense
class with everything we've learned:
class Expense {
// Properties
String description;
double amount;
String category;
DateTime date;
// Constructor
Expense(this.description, this.amount, this.category, this.date);
// Method: Print detailed information
void printDetails() {
print('ββββββββββββββββββββ');
print('π Description: $description');
print('π΅ Amount: ${getFormattedAmount()}');
print('π Category: $category');
print('π
Date: ${getFormattedDate()}');
if (isMajorExpense()) {
print('β οΈ This is a major expense!');
}
print('ββββββββββββββββββββ');
}
// Method: Check if major expense
bool isMajorExpense() {
return amount > 100;
}
// Method: Get one-line summary
String getSummary() {
String emoji = isMajorExpense() ? 'π΄' : 'π’';
return '$emoji $description: ${getFormattedAmount()} [$category]';
}
// Method: Get formatted amount with 2 decimals
String getFormattedAmount() {
return '\$${amount.toStringAsFixed(2)}';
}
// Method: Get formatted date (without time)
String getFormattedDate() {
return date.toString().split(' ')[0];
}
}
void main() {
print('π¦ MY EXPENSES\n');
// Create multiple expenses
var expenses = [
Expense('Monthly rent', 1200.00, 'Bills', DateTime.now()),
Expense('Groceries', 67.50, 'Food', DateTime.now()),
Expense('Coffee', 4.50, 'Food', DateTime.now()),
Expense('New phone', 799.99, 'Electronics', DateTime.now()),
Expense('Gas', 45.00, 'Transport', DateTime.now()),
];
// Print summary for each
print('SUMMARY:');
for (var expense in expenses) {
print(expense.getSummary());
}
print('\n');
// Print details for major expenses
print('MAJOR EXPENSES (>$100):');
for (var expense in expenses) {
if (expense.isMajorExpense()) {
expense.printDetails();
print('');
}
}
// Calculate total
double total = 0;
for (var expense in expenses) {
total += expense.amount;
}
print('π° TOTAL SPENT: \$${total.toStringAsFixed(2)}');
}
π― Practice Exercises
Now it's your turn to practice! Complete these exercises to solidify your understanding:
Exercise 1: Basic Class Creation (Easy)
Create a Category
class with:
- Properties:
name
(String),icon
(String),color
(String) - Constructor that takes all three properties
- Method:
getLabel()
that returns"icon name"
(e.g., "π Food")
Solution:
class Category {
String name;
String icon;
String color;
Category(this.name, this.icon, this.color);
String getLabel() {
return '$icon $name';
}
}
void main() {
var food = Category('Food', 'π', 'green');
var transport = Category('Transport', 'π', 'blue');
print(food.getLabel());
print(transport.getLabel());
}
Exercise 2: Adding Methods (Medium)
Add these methods to your Expense
class:
-
isThisMonth()
- returns true if expense is from current month -
isFood()
- returns true if category is "Food" -
getDaysAgo()
- returns how many days ago this expense was made
Hints:
- Use
DateTime.now()
to get current date - Use
date.difference(otherDate).inDays
to calculate days - Use
date.month
anddate.year
to check if same month
Solution:
bool isThisMonth() {
DateTime now = DateTime.now();
return date.year == now.year && date.month == now.month;
}
bool isFood() {
return category == 'Food';
}
int getDaysAgo() {
DateTime now = DateTime.now();
return now.difference(date).inDays;
}
Exercise 3: Real-World Scenario (Hard)
Create expenses for your last week and:
- Print all expenses from this month
- Calculate total food expenses
- Find the largest expense
- Count how many major expenses (>$100)
Challenge yourself before looking at the solution!
Solution:
void main() {
var expenses = [
Expense('Rent', 1200.00, 'Bills', DateTime(2025, 10, 1)),
Expense('Lunch', 15.50, 'Food', DateTime(2025, 10, 5)),
Expense('Coffee', 4.50, 'Food', DateTime(2025, 10, 6)),
Expense('Groceries', 89.30, 'Food', DateTime(2025, 10, 7)),
Expense('Uber', 12.00, 'Transport', DateTime(2025, 10, 8)),
];
// 1. Print this month's expenses
print('THIS MONTH\'S EXPENSES:');
for (var expense in expenses) {
if (expense.isThisMonth()) {
print(expense.getSummary());
}
}
// 2. Total food expenses
double foodTotal = 0;
for (var expense in expenses) {
if (expense.category == 'Food') {
foodTotal += expense.amount;
}
}
print('\nTotal food: \${foodTotal.toStringAsFixed(2)}');
// 3. Find largest expense
Expense? largest;
for (var expense in expenses) {
if (largest == null || expense.amount > largest.amount) {
largest = expense;
}
}
print('Largest: ${largest?.getSummary()}');
// 4. Count major expenses
int majorCount = 0;
for (var expense in expenses) {
if (expense.isMajorExpense()) {
majorCount++;
}
}
print('Major expenses: $majorCount');
}
Common Mistakes & How to Fix Them
Mistake 1: Forgetting this
in Constructor
// β Wrong
Expense(description, amount, category, date);
// β
Correct
Expense(this.description, this.amount, this.category, this.date);
Mistake 2: Wrong Method Call
// β Wrong - no parentheses
coffee.printDetails;
// β
Correct
coffee.printDetails();
Mistake 3: Using Class Name Instead of Object
// β Wrong - Expense is the class, not an object
print(Expense.amount);
// β
Correct - coffee is the object
var coffee = Expense('Coffee', 4.50, 'Food', DateTime.now());
print(coffee.amount);
Mistake 4: Forgetting Return Statement
// β Wrong - no return
String getSummary() {
'$description: \$$amount';
}
// β
Correct
String getSummary() {
return '$description: \$$amount';
}
Key Concepts Review
Before moving to Lesson 3, make sure you understand:
β
Classes are blueprints - define structure with properties and methods
β
Objects are instances - created using the class
β
Constructors initialize objects - use this.property
shortcut
β
Properties hold data - accessed with dot notation
β
Methods define behavior - can be void or return values
β
Use proper naming - classes PascalCase, methods camelCase
Self-Check Questions
Test your understanding:
1. What's the difference between a property and a method?
Answer:
- Property = data (like description, amount)
- Method = behavior/action (like printDetails(), getSummary())
2. Why do we use constructors?
Answer:
To initialize objects with data when we create them, ensuring all required values are set.
3. What does this
mean in a constructor?
Answer:
this
refers to the current object being created. this.amount
means "the amount property of this specific expense object".
What's Next?
In Lesson 3: Constructors Deep Dive, we'll learn:
- Named constructors for easier object creation
- Optional parameters
- Default values
- Factory constructors
- Making our Expense class even more flexible!
Example preview:
// Instead of this:
var expense = Expense('Coffee', 4.50, 'Food', DateTime.now());
// We'll be able to do this:
var expense = Expense.quick('Coffee', 4.50, 'Food');
var bill = Expense.monthly('Rent', 1200.00);
Much cleaner, right? See you in Lesson 3! π
Additional Resources
- Dart Language Tour: https://dart.dev/guides/language/language-tour#classes
-
Practice: Try creating other classes like
Budget
,User
, orTransaction
- Next Step: Move on to Lesson 3 when you're comfortable with this material
Remember: Don't rush! Make sure you can complete the exercises before moving forward. Practice is key! πͺ
Top comments (0)