On this page23 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What this Means
- 2. Usually You Can Leave this Out
- 3. When this Is Required
- 4. Returning this for Chaining
- 5. Computed Getters
- 6. A Getter with a Block Body
- 7. Setters
- 8. A Setter That Checks the Value
- 9. Read-Only Properties
- 10. Field, Getter, or Method?
- 11. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 56 — this, Getters & Setters
| Category | 9 — Object-Oriented Programming |
| Lecture | 056 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
So far a field was a plain variable that anyone could overwrite with anything. Getters and setters let a property calculate its value or check a new one — and
thislets an object refer to itself.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.
Prerequisites
Students should already understand, from Lectures 052–055:
- classes, objects, fields, and methods
- constructors, including the
Student(this.name)shorthand finalfields (Lecture 006) and arrow functions=>(Lecture 034)- shadowing (Lecture 037) and the cascade operator
..(Lecture 020)
Connection With Previous Lectures
Here is a class built the way Lectures 053–055 taught:
class Student {
String name;
int marks;
Student(this.name, this.marks);
}
void main() {
var ali = Student('Ali', 80);
ali.marks = -40;
print('${ali.name}: ${ali.marks}');
}Ali: -40Nothing stopped a mark of -40. A field accepts any value of its type, and the class has no
chance to say "that makes no sense". And some values — a grade, an area, a total — should not
be stored at all, but worked out from other fields every time you ask. This lecture gives
a class both powers.
Learning Objectives
By the end of this lecture you will be able to:
- Explain what
thisrefers to inside a class - Recognize when
thisis required and when it is unnecessary - Return
thisfrom a method to chain calls, and compare that with cascades - Write computed getters with
get - Write setters with
set, including setters that validate a value - Combine a getter and a setter into one property
- Create read-only properties with a getter only or a
finalfield - Recognize the errors Dart gives for common getter and setter mistakes
1. What this Means
Inside a class, this means "the object this code is running on".
Analogy — when Ali says "my name", he means Ali's name; when Sara says the same words, she means hers.
thisis the object's way of saying "my".
class Student {
String name;
Student(this.name);
void introduce() {
print('Hi, I am ${this.name}');
}
}
void main() {
Student('Ali').introduce();
Student('Sara').introduce();
}Hi, I am Ali
Hi, I am SaraThe same method prints two different names, because this is a different object each time.
2. Usually You Can Leave this Out
Inside a method, a plain name already means the object's field, so ${this.name} and
${name} do the same thing. In fact, dart analyze flags the version above:
info - Unnecessary 'this.' qualifier. Try removing 'this.'. - unnecessary_thisDart's style is to write this only when it is needed. Write print('Hi, I am $name');.
3. When this Is Required
this becomes necessary when a parameter has the same name as a field. The parameter
shadows the field, exactly like the shadowing in Lecture 037:
class Student {
String name;
Student(this.name);
void rename(String name) {
name = name;
}
}
void main() {
var student = Student('Ali');
student.rename('Sara');
print(student.name);
}AliNo error, no warning — and the name did not change. Inside rename, both names mean the
parameter, so the line copies the parameter into itself. this.name reaches past the
parameter to the field:
void rename(String name) {
this.name = name;
}SaraThis is also what Student(this.name) means in a constructor: "put this argument into the
field called name".
4. Returning this for Chaining
A method can return this — the object itself — so the next call continues on it:
class Counter {
int count = 0;
Counter add(int amount) {
count += amount;
return this;
}
}
void main() {
var counter = Counter().add(5).add(3).add(2);
print(counter.count);
}10You already know a tool that does this without changing the class: the cascade. With a plain
void add(int amount) method, the cascade still hands back the object:
class Counter {
int count = 0;
void add(int amount) {
count += amount;
}
}
void main() {
var counter = Counter()
..add(5)
..add(3)
..add(2);
print(counter.count);
}10Prefer cascades. They work on every class, and the method's return value stays free for a real result.
5. Computed Getters
A getter is a property whose value is calculated each time you read it. Write the type,
the keyword get, the name — and no parentheses:
class Rectangle {
double width;
double height;
Rectangle(this.width, this.height);
double get area => width * height;
}
void main() {
var room = Rectangle(4, 3);
print(room.area);
room.width = 5;
print(room.area);
}12.0
15.0You read room.area like a field, with no (). Because nothing is stored, the area can never
go out of date when the width changes.
6. A Getter with a Block Body
A getter that needs more than one expression uses { } and return, like any function:
class Student {
String name;
int marks;
Student(this.name, this.marks);
String get grade {
if (marks >= 80) return 'A';
if (marks >= 60) return 'B';
return 'C';
}
}
void main() {
var sara = Student('Sara', 85);
print('${sara.name}: ${sara.grade}');
}Sara: A7. Setters
A setter runs code when you assign to a property. Write set, the name, and exactly one
parameter. A getter and a setter with the same name form one property:
class Thermometer {
double celsius;
Thermometer(this.celsius);
double get fahrenheit => celsius * 9 / 5 + 32;
set fahrenheit(double value) {
celsius = (value - 32) * 5 / 9;
}
}
void main() {
var t = Thermometer(25);
print(t.fahrenheit);
t.fahrenheit = 212;
print(t.celsius);
}77.0
100.0t.fahrenheit = 212 looks like a normal assignment, but it calls the setter, which updates
celsius. Only one value is stored; fahrenheit is a view of it from another angle.
8. A Setter That Checks the Value
Now we can fix the -40 problem. The setter decides whether to accept a value. It needs a
field to store the accepted value in, and that field needs a different name from the
property:
class Student {
String name;
int storedMarks;
Student(this.name, this.storedMarks);
int get marks => storedMarks;
set marks(int value) {
if (value < 0 || value > 100) {
print('Rejected: $value is not between 0 and 100');
return;
}
storedMarks = value;
}
}
void main() {
var ali = Student('Ali', 80);
ali.marks = -40;
ali.marks = 95;
print('${ali.name}: ${ali.marks}');
}Rejected: -40 is not between 0 and 100
Ali: 95The code outside the class still writes ali.marks = ..., but now the class gets a say.
There is a gap, though: nothing stops someone from writing ali.storedMarks = -40 and skipping
the setter entirely. Closing that gap with private members is the topic of Lecture 057.
9. Read-Only Properties
A property with a getter and no setter can be read but not assigned:
class Rectangle {
final double width;
final double height;
Rectangle(this.width, this.height);
double get area => width * height;
}
void main() {
var room = Rectangle(4, 3);
room.area = 20;
}Error: The setter 'area' isn't defined for the type 'Rectangle'.
- 'Rectangle' is from 'bin/main.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'area'.
room.area = 20;
^^^^A final field is read-only too — writing room.width = 5; gives the same message for
'width'. A final field is set once by the constructor; a getter-only property is
calculated. Both are safe to share, because no one can overwrite them.
10. Field, Getter, or Method?
- Field — a value the object stores:
name,width. - Getter — a quick value worked out from fields, with no inputs:
area,grade. - Method — an action, or anything that needs arguments:
rename('Sara'),add(5).
If you would describe it with a noun and it needs no arguments, it is usually a getter.
11. A Real-World Example
class BankAccount {
final String owner;
double storedBalance = 0;
BankAccount(this.owner);
double get balance => storedBalance;
set balance(double value) {
if (value < 0) {
print('Rejected: balance cannot go below 0');
return;
}
storedBalance = value;
}
bool get isEmpty => storedBalance == 0;
void deposit(double amount) {
balance = balance + amount;
}
void withdraw(double amount) {
balance = balance - amount;
}
}
void main() {
var account = BankAccount('Sara')
..deposit(1000)
..withdraw(300)
..withdraw(900);
print('${account.owner}: ${account.balance}');
print('Empty: ${account.isEmpty}');
}Rejected: balance cannot go below 0
Sara: 700.0
Empty: falseowner is a read-only final field, isEmpty a computed getter, and balance a checked
getter/setter pair. Because deposit and withdraw also go through the setter, the rule is
written once and applies everywhere. The cascade calls three methods on the new account.
Code Examples
Example 1 — this Inside a Method
class Pet {
String name;
Pet(this.name);
void greet() {
print('$name says hello');
}
}
void main() {
Pet('Zayan\'s cat').greet();
}Zayan's cat says helloExample 2 — this with a Shadowing Parameter
class City {
String name;
City(this.name);
void update(String name) {
this.name = name;
}
}
void main() {
var city = City('Lahore');
city.update('Karachi');
print(city.name);
}KarachiExample 3 — Returning this
class Score {
int points = 0;
Score bonus(int amount) {
points += amount;
return this;
}
}
void main() {
print(Score().bonus(10).bonus(15).points);
}25Example 4 — A Cascade on Your Own Class
class Score {
int points = 0;
void bonus(int amount) {
points += amount;
}
}
void main() {
var score = Score()
..bonus(10)
..bonus(15);
print(score.points);
}25Example 5 — Computed Getters
class Square {
double side;
Square(this.side);
double get area => side * side;
double get perimeter => side * 4;
}
void main() {
var tile = Square(3);
print('Area: ${tile.area}');
print('Perimeter: ${tile.perimeter}');
}Area: 9.0
Perimeter: 12.0Example 6 — A Getter/Setter Pair with an Arrow Setter
class Timer {
int seconds;
Timer(this.seconds);
int get minutes => seconds ~/ 60;
set minutes(int value) => seconds = value * 60;
}
void main() {
var timer = Timer(150);
print(timer.minutes);
timer.minutes = 3;
print(timer.seconds);
}2
180A short setter can use => too. 150 ~/ 60 is 2 whole minutes.
Example 7 — Student Marks
class Student {
final String name;
int storedMarks = 0;
Student(this.name);
int get marks => storedMarks;
set marks(int value) {
if (value < 0 || value > 100) {
print('Invalid mark for $name: $value');
return;
}
storedMarks = value;
}
bool get passed => storedMarks >= 50;
}
void main() {
var zayan = Student('Zayan')
..marks = 120
..marks = 72;
print('${zayan.name}: ${zayan.marks}, passed: ${zayan.passed}');
}Invalid mark for Zayan: 120
Zayan: 72, passed: trueHow it works
- A cascade can assign through a setter:
..marks = 120callsset marks. 120is rejected, sostoredMarksstays0until72arrives.passedis worked out from the stored mark each time it is read.
Example 8 — Shopping Total
class CartItem {
final String product;
final int price;
int storedQuantity = 1;
CartItem(this.product, this.price);
int get quantity => storedQuantity;
set quantity(int value) {
if (value < 1) {
print('Quantity must be at least 1');
return;
}
storedQuantity = value;
}
int get total => price * storedQuantity;
}
void main() {
var item = CartItem('Notebook', 500);
item.quantity = 3;
item.quantity = 0;
print('${item.product} x ${item.quantity} = ${item.total}');
}Quantity must be at least 1
Notebook x 3 = 1500Example 9 — Bank Account Number
class Account {
final String number;
double balance;
Account(this.number, this.balance);
String get masked => '****${number.substring(number.length - 4)}';
}
void main() {
var account = Account('1234567890', 2500);
print('Account ${account.masked}: ${account.balance}');
}Account ****7890: 2500.0How it works
numberisfinal, so it can never be changed after the account is created.maskedis a read-only getter that shows only the last four digits.
Example 10 — Temperature
class Weather {
final String city;
double celsius;
Weather(this.city, this.celsius);
double get fahrenheit => celsius * 9 / 5 + 32;
set fahrenheit(double value) => celsius = (value - 32) * 5 / 9;
bool get isFreezing => celsius <= 0;
}
void main() {
var today = Weather('Murree', 5);
print('${today.city}: ${today.fahrenheit} F');
today.fahrenheit = 23;
print('${today.celsius} C, freezing: ${today.isFreezing}');
}Murree: 41.0 F
-5.0 C, freezing: trueCommon Beginner Mistakes
1. Forgetting this when a parameter shadows a field
// void rename(String name) {
// name = name; // ✗ assigns the parameter to itself
// }There is no error and no warning — the field simply never changes. Write this.name = name;.
2. Putting parentheses on a getter
// double get area() => width * height; // ✗Error: A getter can't have formal parameters.
Try removing '(...)'.3. Calling a getter like a method
// print(room.area()); // ✗Error: 'area' isn't a function or method and can't be invoked.Read a getter like a field: room.area.
4. A getter that returns itself
// double get balance => balance; // ✗ should return storedBalanceUnhandled exception:
Stack OverflowThe getter calls itself forever. dart analyze warns first: "The getter 'balance'
recursively returns itself." A setter that assigns to its own name loops the same way. Store
the value in a field with a different name.
5. Assigning to a getter-only property or a final field
// room.area = 20; // ✗ no setterError: The setter 'area' isn't defined for the type 'Rectangle'.Add a setter if the property really should be assignable.
6. Writing this. everywhere
this.name where name alone works earns the unnecessary_this hint. Keep this for the
shadowing case.
Best Practices
- Leave
thisout unless a parameter or local variable shadows the field. - Chain calls with cascades instead of making methods return
this. - Use a getter for a value calculated from fields, so it is never out of date.
- Keep getters quick and free of surprises — reading a property should not change anything.
- Put validation in a setter, and route your own methods through it so the rule lives in one place.
- Make a property read-only —
finalor getter-only — unless it truly needs to change.
Mini Challenge
Sara's room thermostat accepts target temperatures from 16 to 30 only. Write a
Thermostat class with a target getter/setter pair (start at 22) and a mode getter that
returns 'Heating' when the target is above 24, otherwise 'Normal'. Set 35, then 26.
Your program should print exactly:
Rejected: 35
Target: 26, mode: HeatingTry it yourself before opening the solution.
Show solution
class Thermostat {
int storedTarget = 22;
int get target => storedTarget;
set target(int value) {
if (value < 16 || value > 30) {
print('Rejected: $value');
return;
}
storedTarget = value;
}
String get mode => storedTarget > 24 ? 'Heating' : 'Normal';
}
void main() {
var thermostat = Thermostat()
..target = 35
..target = 26;
print('Target: ${thermostat.target}, mode: ${thermostat.mode}');
}Rejected: 35
Target: 26, mode: HeatingPractice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
-
Write a
Bookclass with atitlefield and a methodshow()that printsBook: <title>without usingthis. CreateBook('Dart Basics')and callshow(). -
Write a
Circleclass with aradiusfield and a getterdiameter. Print the diameter ofCircle(7). -
This method never changes the field. Fix it with one word, so the program prints
Sara:class Player { String name; Player(this.name); void rename(String name) { name = name; } } void main() { var player = Player('Ali'); player.rename('Sara'); print(player.name); }
Application
- Write a
Productclass withpriceandquantityfields and a gettertotal. Print the total for price250, quantity4. - Write a
Distanceclass that storesmetersand has akilometersgetter/setter pair. Start at1500meters, print the kilometers, then setkilometers = 2and print meters. - Write a
Studentclass with amarksgetter and a setter that rejects values above100by printingRejected. Set105, then88, and print the marks. - Write a
Glassclass with anamountfield and a methodpour(int ml)that adds to it. Use a cascade to pour100,50, and25into a new glass, then print the amount.
Challenge
- Write a
Walletclass with a read-onlyfinalfieldowner, a balance property that cannot go below0, and methodsaddandspendthat go through the setter. For Zayan, add500, spend200, spend400, then print the owner and balance. - Write a
Temperatureclass that storescelsiusand has a getterlabelreturning'Cold'below10,'Warm'from10to29, and'Hot'from30. Print the label for5,20, and35. - Write a
Basketclass whose methodadd(int price)returnsthis, plus anitemCountfield and atotalfield. Chain three calls —250,100,650— and printItems: 3, total: 1000. Then explain in one sentence why a cascade would letaddreturnvoid.
Practice Answers
Question 1 — Solution
class Book {
String title;
Book(this.title);
void show() {
print('Book: $title');
}
}
void main() {
Book('Dart Basics').show();
}Book: Dart BasicsQuestion 2 — Solution
class Circle {
double radius;
Circle(this.radius);
double get diameter => radius * 2;
}
void main() {
print(Circle(7).diameter);
}14.0Question 3 — Solution
Add this., so the left side means the field:
class Player {
String name;
Player(this.name);
void rename(String name) {
this.name = name;
}
}
void main() {
var player = Player('Ali');
player.rename('Sara');
print(player.name);
}SaraQuestion 4 — Solution
class Product {
int price;
int quantity;
Product(this.price, this.quantity);
int get total => price * quantity;
}
void main() {
print(Product(250, 4).total);
}1000Question 5 — Solution
class Distance {
double meters;
Distance(this.meters);
double get kilometers => meters / 1000;
set kilometers(double value) => meters = value * 1000;
}
void main() {
var walk = Distance(1500);
print(walk.kilometers);
walk.kilometers = 2;
print(walk.meters);
}1.5
2000.0Question 6 — Solution
class Student {
int storedMarks = 0;
int get marks => storedMarks;
set marks(int value) {
if (value > 100) {
print('Rejected');
return;
}
storedMarks = value;
}
}
void main() {
var student = Student();
student.marks = 105;
student.marks = 88;
print(student.marks);
}Rejected
88Question 7 — Solution
class Glass {
int amount = 0;
void pour(int ml) {
amount += ml;
}
}
void main() {
var glass = Glass()
..pour(100)
..pour(50)
..pour(25);
print(glass.amount);
}175Question 8 — Solution
class Wallet {
final String owner;
int storedBalance = 0;
Wallet(this.owner);
int get balance => storedBalance;
set balance(int value) {
if (value < 0) {
print('Not enough money');
return;
}
storedBalance = value;
}
void add(int amount) {
balance = balance + amount;
}
void spend(int amount) {
balance = balance - amount;
}
}
void main() {
var wallet = Wallet('Zayan')
..add(500)
..spend(200)
..spend(400);
print('${wallet.owner}: ${wallet.balance}');
}Not enough money
Zayan: 300Question 9 — Solution
class Temperature {
double celsius;
Temperature(this.celsius);
String get label {
if (celsius < 10) return 'Cold';
if (celsius < 30) return 'Warm';
return 'Hot';
}
}
void main() {
print(Temperature(5).label);
print(Temperature(20).label);
print(Temperature(35).label);
}Cold
Warm
HotQuestion 10 — Solution
class Basket {
int itemCount = 0;
int total = 0;
Basket add(int price) {
itemCount++;
total += price;
return this;
}
}
void main() {
var basket = Basket().add(250).add(100).add(650);
print('Items: ${basket.itemCount}, total: ${basket.total}');
}Items: 3, total: 1000A cascade always gives back the object it started on, whatever add returns — so add would
not need to return this at all.
Quick Revision
- What does
thisrefer to inside a method? - When is
this.required? - What does the
unnecessary_thishint tell you? - Why are cascades usually better than methods that return
this? - How do you read a getter — with or without parentheses?
- How many parameters does a setter take?
- Why does a validating setter need a field with a different name?
- Name two ways to make a property read-only.
Key Takeaways
thismeans "the current object"; inside a method it is usually optional.- Use
this.when a parameter shadows a field — without it,name = namesilently does nothing. - A method can
return thisfor chaining, but cascades do the same job on any class. - A getter (
double get area => ...) is calculated on every read and has no parentheses. - A setter (
set marks(int value) { ... }) runs on assignment and can reject bad values. - A getter and setter with the same name form one property, backed by a differently named field.
- A getter with no setter, or a
finalfield, is read-only. - A getter or setter that uses its own name loops forever.
What's Next?
Lecture 057 — Private Members & Encapsulation. Our validating setters have a back door:
ali.storedMarks = -40 skips the check entirely. Next we will learn how a leading _ makes a
member private, so the only way in is through the getters, setters, and methods you choose.