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:
Expenseclass → 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 {
classkeyword tells Dart we're creating a new class
Expenseis 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.descriptionmeans "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
returnsends 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).inDaysto calculate days - Use
date.monthanddate.yearto 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 likeBudget,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! 💪