Question #37EasyFlutter Basics

What is this in flutter ?

#flutter

Answer

Overview

The this keyword in Dart/Flutter refers to the current instance of a class. It's used to access instance variables, methods, and constructors within the class itself.


Basic Usage

Access Instance Variables

dart
class User {
  String name;
  int age;
  
  User(this.name, this.age);
  
  void greet() {
    print('Hello, my name is ${this.name}');
    // 'this' refers to current User instance
  }
}

void main() {
  final user = User('John', 30);
  user.greet();  // Hello, my name is John
}

Constructor Shorthand

Traditional Constructor

dart
class Point {
  int x;
  int y;
  
  Point(int x, int y) {
    this.x = x;  // this.x = instance variable
    this.y = y;  // x, y = parameters
  }
}

Shorthand (Common in Flutter)

dart
class Point {
  int x;
  int y;
  
  // Dart automatically assigns parameters to instance variables
  Point(this.x, this.y);
}

This is the most common pattern in Flutter:

dart
class MyWidget extends StatelessWidget {
  final String title;
  final Color color;
  
  const MyWidget({
    required this.title,
    required this.color,
  });
}

When to Use 'this'

1. Resolve Naming Conflicts

When parameter names match instance variable names.

dart
class Rectangle {
  double width;
  double height;
  
  Rectangle(double width, double height) {
    this.width = width;   // this.width = instance variable
    this.height = height; // width = parameter
  }
  
  double get area => this.width * this.height;
}

Without naming conflict:

dart
class Rectangle {
  double width;
  double height;
  
  // No need for 'this' when names differ
  void setDimensions(double w, double h) {
    width = w;
    height = h;
  }
}

2. Named Constructors

dart
class User {
  String name;
  int age;
  
  User(this.name, this.age);
  
  // Named constructor calling default constructor
  User.guest() : this('Guest', 0);
  
  User.fromJson(Map<String, dynamic> json)
      : this(json['name'], json['age']);
}

void main() {
  final guest = User.guest();
  final user = User.fromJson({'name': 'John', 'age': 30});
}

3. Factory Constructors

dart
class Database {
  static Database? _instance;
  
  Database._internal();
  
  factory Database() {
    return _instance ??= Database._internal();
  }
  
  void query() {
    print('Querying...');
  }
}

Flutter-Specific Usage

1. StatefulWidget

Access widget properties from State class.

dart
class Counter extends StatefulWidget {
  final String title;
  final int initialValue;
  
  const Counter({
    required this.title,
    required this.initialValue,
  });
  
  
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  late int _count;
  
  
  void initState() {
    super.initState();
    // Access widget property using 'widget'
    _count = widget.initialValue;
  }
  
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(widget.title),  // Access widget property
        Text('$_count'),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _count++;  // Access state variable
            });
          },
          child: Text('Increment'),
        ),
      ],
    );
  }
}

2. Callback Functions

dart
class SearchBar extends StatefulWidget {
  final Function(String) onSearch;
  
  const SearchBar({required this.onSearch});
  
  
  _SearchBarState createState() => _SearchBarState();
}

class _SearchBarState extends State<SearchBar> {
  final _controller = TextEditingController();
  
  void _handleSearch() {
    widget.onSearch(_controller.text);  // Call parent callback
  }
  
  
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      onSubmitted: (_) => _handleSearch(),
    );
  }
}

3. Animation Controllers

dart
class AnimatedBox extends StatefulWidget {
  
  _AnimatedBoxState createState() => _AnimatedBoxState();
}

class _AnimatedBoxState extends State<AnimatedBox>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  
  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,  // 'this' provides TickerProvider
      duration: Duration(seconds: 2),
    );
  }
  
  
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  
  
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Transform.rotate(
          angle: _controller.value * 2 * 3.14159,
          child: FlutterLogo(size: 100),
        );
      },
    );
  }
}

'this' vs No 'this'

Optional in Most Cases

dart
class Counter {
  int count = 0;
  
  void increment() {
    count++;        // ✅ Implicit 'this'
    this.count++;   // ✅ Explicit 'this' (same result)
  }
}

Required for Disambiguation

dart
class User {
  String name;
  
  User(String name) {
    this.name = name;  // Required - disambiguate
  }
  
  void setName(String name) {
    this.name = name;  // Required - parameter has same name
  }
}

Common Patterns

Pattern 1: Constructor with this

dart
class Product {
  final String id;
  final String name;
  final double price;
  
  const Product({
    required this.id,
    required this.name,
    required this.price,
  });
}

Pattern 2: Method Chaining

dart
class QueryBuilder {
  String _table = '';
  String _where = '';
  
  QueryBuilder table(String name) {
    _table = name;
    return this;  // Return current instance for chaining
  }
  
  QueryBuilder where(String condition) {
    _where = condition;
    return this;
  }
  
  String build() {
    return 'SELECT * FROM $_table WHERE $_where';
  }
}

// Usage
final query = QueryBuilder()
    .table('users')
    .where('age > 18')
    .build();

Pattern 3: Cascade Notation (Alternative to 'this')

dart
// Without cascade
final user = User('John', 30);
user.setAge(31);
user.setCity('NYC');
user.setEmail('john@example.com');

// With cascade (no need for 'this')
final user = User('John', 30)
  ..setAge(31)
  ..setCity('NYC')
  ..setEmail('john@example.com');

Complete Example

dart
class BankAccount {
  String owner;
  double balance;
  
  // Constructor with 'this'
  BankAccount(this.owner, this.balance);
  
  // Named constructor using 'this'
  BankAccount.newAccount(String owner) : this(owner, 0.0);
  
  // Method with 'this' for clarity
  void deposit(double amount) {
    this.balance += amount;
    print('${this.owner} deposited \$${amount}. New balance: \$${this.balance}');
  }
  
  void withdraw(double amount) {
    if (this.balance >= amount) {
      this.balance -= amount;
      print('${this.owner} withdrew \$${amount}. New balance: \$${this.balance}');
    } else {
      print('Insufficient funds');
    }
  }
  
  // Method chaining
  BankAccount setOwner(String owner) {
    this.owner = owner;
    return this;
  }
  
  BankAccount setBalance(double balance) {
    this.balance = balance;
    return this;
  }
}

void main() {
  // Using constructor
  final account1 = BankAccount('Alice', 1000.0);
  account1.deposit(500);
  
  // Using named constructor
  final account2 = BankAccount.newAccount('Bob');
  account2.deposit(200);
  
  // Method chaining
  final account3 = BankAccount('Charlie', 0)
      .setBalance(1500)
      .setOwner('Charles');
}

Best Practices

Tip: Use 'this' for clarity, omit when obvious

✅ Do

dart
// Good - Use in constructors
class User {
  final String name;
  User(this.name);
}

// Good - Use when ambiguous
void setName(String name) {
  this.name = name;  // Clear which is instance variable
}

// Good - Use in method chaining
return this;

❌ Don't

dart
// Bad - Unnecessary 'this'
class Counter {
  int count = 0;
  
  void increment() {
    this.count = this.count + 1;  // Verbose, 'this' not needed
  }
}

// Better
void increment() {
  count++;  // Clear without 'this'
}

Resources