Skip to main content
In Dart, a private field is a class member whose visibility is restricted to the library in which it is declared. Unlike many object-oriented languages that enforce class-level encapsulation, Dart enforces library-level encapsulation. A field is designated as private by prefixing its identifier with an underscore (_), which acts as a library-level visibility modifier rather than relying on access modifier keywords.

Syntax

To declare a private field, prepend the variable name with _.
class Configuration {
  // Private field
  String _apiKey; 
  
  // Public field
  int timeoutSeconds; 

  Configuration(this._apiKey, this.timeoutSeconds);
}

Scope and Visibility Mechanics

1. Library Boundaries In Dart, every .dart file is inherently its own library unless explicitly combined with other files using part and part of directives. Because privacy is library-scoped, any code residing within the exact same library can access the private field directly, regardless of whether that code is inside or outside the defining class. 2. Same-Library Access (Valid) If an instance of a class is created within the same library where the class is defined (either in the same file or a file linked via part of), the private fields are fully exposed to that local code.
// file: user_profile.dart

class UserProfile {
  int _age = 30; 
}

void main() {
  var profile = UserProfile();
  // Valid: main() is in the same library as UserProfile
  profile._age = 31; 
  print(profile._age); 
}
3. Cross-Library Access (Invalid) If an instance of the class is accessed from a completely different library (a separate .dart file not linked via part directives), the private field is strictly hidden. Attempting to access or mutate the _ prefixed field from an external library results in a compilation error.
// file: app.dart
import 'user_profile.dart';

void main() {
  var profile = UserProfile();
  
  // Compilation Error: The getter '_age' isn't defined for the type 'UserProfile'.
  print(profile._age); 
}

Constructor Initialization

Private fields can be initialized directly in constructors using this._fieldName for positional parameters. However, they cannot be used directly as named parameters, because named parameters form part of the public API of the class and cannot begin with an underscore. To initialize a private field using a named parameter, you must use a public parameter name and map it to the private field using an initializer list.
class NetworkClient {
  final String _endpoint;

  // Valid: Positional parameter initializing a private field
  // NetworkClient(this._endpoint); 
  
  // Invalid: Named parameters cannot be private
  // NetworkClient({required this._endpoint}); 

  // Valid Workaround: Using an initializer list
  NetworkClient({required String endpoint}) : _endpoint = endpoint;
}

Exposing and Mutating Private Fields

To expose or mutate the underlying value of a private field across library boundaries, you must explicitly define public interfaces. This can be achieved using Dart’s get and set keywords to create formal accessors, or by defining standard public methods (e.g., void updateEndpoint(String url) or String fetchEndpoint()) to handle the data manipulation.
Tired of Poor Dart Skills? Fix That With Deep Grasping!Learn More