
If you’re working with Dart or Flutter, understanding getter setter in Dart is essential for writing clean, maintainable code. Getters and setters are fundamental concepts that help you control how properties of a class are accessed and modified. In this comprehensive guide, we’ll explore everything you need to know about getter setter in Dart, from basic concepts to advanced patterns.
What is Getter Setter in Dart?
Getter setter in Dart are special methods that provide read and write access to an object’s properties. A getter retrieves the value of a property, while a setter assigns a new value to it. Unlike many other programming languages, Dart provides a clean, intuitive syntax for implementing getter setter in Dart that makes your code more readable and maintainable.
In Dart, you can define getters and setters without explicitly using parentheses when calling them, making them look like regular property access. This syntactic sugar is one of the reasons why getter setter in Dart is so popular among developers.
Why Use Getter Setter in Dart?
Before diving into the implementation details, let’s understand why getter setter in Dart is important:
- Encapsulation: Getters and setters help you hide the internal representation of a property while exposing a public interface.
- Validation: With setters, you can validate data before assigning it to a property, ensuring data integrity.
- Computed Properties: Getters allow you to compute values on-the-fly without storing them explicitly.
- Flexibility: You can change the internal implementation without affecting the code that uses your class.
- Read-only or Write-only Properties: You can create properties that are only readable or only writable by defining just a getter or just a setter.
Basic Syntax of Getter Setter in Dart
Let’s start with the basic syntax of getter setter in Dart:
class Person {
String _name; // Private property
// Getter
String get name {
return _name;
}
// Setter
set name(String value) {
_name = value;
}
Person(this._name);
}
In this example, we’re using getter setter in Dart to control access to the _name property. The underscore prefix makes the property private to the library.
Simple Getter Setter in Dart Examples
Example 1: Basic Implementation
class Rectangle {
double _width;
double _height;
Rectangle(this._width, this._height);
// Getter for width
double get width => _width;
// Setter for width with validation
set width(double value) {
if (value > 0) {
_width = value;
} else {
throw ArgumentError('Width must be positive');
}
}
// Getter for height
double get height => _height;
// Setter for height with validation
set height(double value) {
if (value > 0) {
_height = value;
} else {
throw ArgumentError('Height must be positive');
}
}
// Computed property using getter
double get area => _width * _height;
}
void main() {
var rect = Rectangle(10, 5);
print('Area: ${rect.area}'); // Using getter
rect.width = 20; // Using setter
print('New area: ${rect.area}');
}
Example 2: Read-only Properties
One powerful use of getter setter in Dart is creating read-only properties:
class Circle {
final double radius;
Circle(this.radius);
// Read-only property (only getter, no setter)
double get diameter => radius * 2;
double get circumference => 2 * 3.14159 * radius;
double get area => 3.14159 * radius * radius;
}
void main() {
var circle = Circle(5);
print('Diameter: ${circle.diameter}');
print('Circumference: ${circle.circumference}');
print('Area: ${circle.area}');
// circle.diameter = 20; // This would cause an error
}
Advanced Getter Setter in Dart Patterns
Pattern 1: Lazy Initialization
class DataProcessor {
List<int>? _data;
List<int>? _processedData;
DataProcessor(this._data);
// Lazy getter - computes only when needed
List<int> get processedData {
_processedData ??= _data?.map((e) => e * 2).toList();
return _processedData!;
}
}
Pattern 2: Dependent Properties
class Temperature {
double _celsius;
Temperature(this._celsius);
// Getter for celsius
double get celsius => _celsius;
// Setter for celsius
set celsius(double value) {
_celsius = value;
}
// Fahrenheit as computed property
double get fahrenheit => (_celsius * 9 / 5) + 32;
set fahrenheit(double value) {
_celsius = (value - 32) * 5 / 9;
}
// Kelvin as computed property
double get kelvin => _celsius + 273.15;
set kelvin(double value) {
_celsius = value - 273.15;
}
}
void main() {
var temp = Temperature(25);
print('Celsius: ${temp.celsius}');
print('Fahrenheit: ${temp.fahrenheit}');
print('Kelvin: ${temp.kelvin}');
temp.fahrenheit = 98.6;
print('New Celsius: ${temp.celsius}');
}
Pattern 3: Validation and Transformation
class User {
String _email;
String _username;
int _age;
User(this._email, this._username, this._age);
// Email getter and setter with validation
String get email => _email;
set email(String value) {
if (value.contains('@') && value.contains('.')) {
_email = value.toLowerCase();
} else {
throw FormatException('Invalid email format');
}
}
// Username getter and setter with transformation
String get username => _username;
set username(String value) {
if (value.length >= 3) {
_username = value.trim().toLowerCase();
} else {
throw ArgumentError('Username must be at least 3 characters');
}
}
// Age getter and setter with validation
int get age => _age;
set age(int value) {
if (value >= 0 && value <= 150) {
_age = value;
} else {
throw RangeError('Age must be between 0 and 150');
}
}
// Computed property
bool get isAdult => _age >= 18;
}
Getter Setter in Dart vs Other Languages
Understanding getter setter in Dart becomes easier when you compare it to other languages:

Java/C#:
// Java style
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Dart:
// Dart style - much cleaner!
String get name => _name;
set name(String value) => _name = value;
The Dart syntax is more concise and doesn’t require explicit method calls with parentheses, making code more readable.
Best Practices for Getter Setter in Dart
When working with getter setter in Dart, follow these best practices:
- Use Private Variables: Always use underscore-prefixed variables (
_variable) when implementing custom getters and setters. - Keep Getters Pure: Getters should not have side effects. They should simply return values or compute them based on existing state.
- Validate in Setters: Use setters to validate input and throw appropriate exceptions for invalid data.
- Use Arrow Syntax for Simple Cases: For simple getters and setters, use the arrow syntax (
=>) for conciseness. - Avoid Heavy Computation in Getters: If a getter performs expensive operations, consider caching the result or using a regular method instead.
- Document Your Intent: Add comments explaining why you’re using a custom getter or setter, especially if there’s validation or transformation logic.
Read More: How to Create a Puns Generator in Flutter: A Complete Beginner’s Guide
Common Mistakes to Avoid
When implementing getter setter in Dart, watch out for these common pitfalls:
- Infinite Recursion: Don’t call the getter/setter from within itself without using the private variable.
// Wrong!
String get name => name; // Infinite recursion
// Correct
String get name => _name;
- Forgetting Validation: Always validate data in setters if there are constraints.
- Overusing Getters and Setters: Not every property needs custom getters and setters. Use them only when you need additional logic.
- Making Getters Impure: Avoid modifying state in getters.
Real-World Example: Shopping Cart
Here’s a practical example showing getter setter in Dart in action:
class Product {
final String name;
final double price;
Product(this.name, this.price);
}
class ShoppingCart {
final List<Product> _items = [];
double _discountPercentage = 0;
// Getter for items (returns unmodifiable list)
List<Product> get items => List.unmodifiable(_items);
// Getter for item count
int get itemCount => _items.length;
// Getter and setter for discount
double get discountPercentage => _discountPercentage;
set discountPercentage(double value) {
if (value >= 0 && value <= 100) {
_discountPercentage = value;
} else {
throw RangeError('Discount must be between 0 and 100');
}
}
// Computed properties
double get subtotal {
return _items.fold(0, (sum, item) => sum + item.price);
}
double get discountAmount {
return subtotal * (_discountPercentage / 100);
}
double get total {
return subtotal - discountAmount;
}
// Methods
void addItem(Product product) {
_items.add(product);
}
void removeItem(Product product) {
_items.remove(product);
}
void clear() {
_items.clear();
}
}
void main() {
var cart = ShoppingCart();
cart.addItem(Product('Laptop', 999.99));
cart.addItem(Product('Mouse', 29.99));
cart.addItem(Product('Keyboard', 79.99));
print('Item count: ${cart.itemCount}');
print('Subtotal: \$${cart.subtotal.toStringAsFixed(2)}');
cart.discountPercentage = 10;
print('Discount: \$${cart.discountAmount.toStringAsFixed(2)}');
print('Total: \$${cart.total.toStringAsFixed(2)}');
}
Conclusion
Understanding getter setter in Dart is crucial for writing professional, maintainable Dart and Flutter applications. They provide a clean way to encapsulate data, validate input, and create computed properties. By following the patterns and best practices outlined in this guide, you’ll be able to leverage the full power of getter setter in Dart in your projects.
Remember that while getters and setters are powerful tools, they should be used judiciously. Not every property needs custom getters and setters—use them when you need to add logic, validation, or computation. With practice, you’ll develop an intuition for when getter setter in Dart is the right choice for your code.
Whether you’re building a simple Flutter app or a complex enterprise application, mastering getter setter in Dart will help you write code that’s more robust, maintainable, and elegant. Start implementing these patterns in your projects today and see the difference they make in your code quality!
