Skip to main content
Private fields in Dart are class members (instance variables or static variables) encapsulated at the library level. A field is designated as private by prefixing its identifier with an underscore (_). These members are visible only to code defined within the same library (typically the same .dart file) and are inaccessible to external libraries.

Syntax and Declaration

To declare a private field, prepend an underscore to the variable name. The underscore is a mandatory part of the identifier. This syntax applies to both instance fields and static class fields.
class NetworkConfig {
  // Public field
  String url = 'https://api.example.com';

  // Private instance field (initialized to satisfy null safety)
  String _apiKey = ''; 

  // Private static field
  static const int _maxRetries = 5;
}

Visibility Rules

1. Library Scope

Dart privacy is scoped to the library, not the class. Consequently, multiple classes defined within the same file can access each other’s private members, including private static fields.
// file: system_internals.dart

class Engine {
  int _temperature = 0;
  static int _globalThreshold = 100;
}

class Monitor {
  void checkEngine(Engine engine) {
    // Valid: Monitor and Engine are in the same library.
    // Accessing private instance field:
    print(engine._temperature); 
    
    // Accessing private static field:
    print(Engine._globalThreshold);
  }
}

2. External Access

When a class is imported into a different library, private fields are hidden. Accessing them generates a compilation error.
// file: main.dart
import 'system_internals.dart';

void main() {
  var engine = Engine();
  
  // Compilation Error: The getter '_temperature' isn't defined for 'Engine'.
  // print(engine._temperature); 
}

Constructor Initialization

Private fields can be initialized using standard constructors, initializing formals, or initializer lists. The approach differs between positional and named parameters due to parameter naming rules.

Positional Parameters

Positional initializing formals (this._fieldName) allow direct assignment. Because positional arguments are passed by order rather than name, the private identifier is not exposed to the caller.
class User {
  String _id;
  
  // Valid: Assigns the first argument to _id
  User(this._id);
}

Named Parameters

Using the initializing formal syntax this._fieldName inside named parameters (curly braces) creates a named parameter with the identifier _fieldName. While syntactically valid, this results in a private named parameter. Callers outside the library cannot invoke the constructor because they cannot reference the private parameter name _fieldName. To initialize a private field via a named parameter accessible to external callers, declare a public parameter and assign it to the private field using an initializer list.
class Session {
  final String _token;

  // Recommended: Maps public 'token' argument to private '_token' field
  Session({required String token}) : _token = token;
  
  // Technically Valid but Inaccessible Externally:
  // Session({required this._token}); 
  // External code cannot call: Session(_token: '123');
}

Encapsulation via Accessors

To provide controlled access to private fields from external libraries, classes define public getters and setters.
class Counter {
  int _count = 0;

  // Public getter
  int get count => _count;

  // Public setter
  set count(int value) {
    if (value >= 0) {
      _count = value;
    }
  }
}
Master Dart with Deep Grasping Methodology!Learn More