I Thought This Was Private… Until Dart Surprised Me

Md. Al - AminMd. Al - Amin
4 min read

I was working on a simple budgeting feature for a finance app when something unexpected happened something that made me question everything I knew about encapsulation in Dart.

Let me walk you through the mistake that taught me something incredibly valuable.

The Real-World Setup: A Budgeting App

Imagine you’re building a personal finance app. Nothing too fancy just a way for users to track their account balances.

You decide to create a BankAccount class with a private _balance field, because hey, you don’t want other parts of your app messing with the internal state directly. So, you write this main.dart file:

void main() {
  BankAccount account = BankAccount();
  account.deposit(100);

  // Wait, this works?
  print("Balance is: ${account._balance}");
}

// BankAccount class
class BankAccount {
  int _balance = 0;

  void deposit(int amount) {
    _balance += amount;
  }

  int getBalance() {
    return _balance;
  }
}

And guess what? It prints:

Balance is: 100

Wait… I Thought That Was Private?

At first, I was confused. I clearly prefixed _balance with an underscore. In almost every language I’ve used Java, C++, even TypeScript that should restrict access to that variable from outside the class.

Yet Dart just… let me access it.

Had I misunderstood how encapsulation works in Dart?

The Investigation Begins

After a bit of digging (and some help from the Dart documentation), I stumbled on a key sentence:

In Dart, identifiers that start with an underscore (_) are private to the library, not the class.

Boom. There it was.

That single sentence flipped my understanding upside down. I wasn’t breaking any rules I was just dealing with a language that thinks about privacy in a different way.

What Does “Library-Private” Mean?

In Dart, privacy is not scoped to the class, but to the library and a library in Dart usually means a single file (unless explicitly split with part/part of).

So, in my example, both main() and BankAccount live in the same file. That means Dart treats them as part of the same library, so accessing _balance is allowed.

Let’s Split the Files

To test this, I moved the BankAccount class to a separate file.

// bank_account.dart
class BankAccount {
  int _balance = 0;

  void deposit(int amount) {
    _balance += amount;
  }

  int getBalance() {
    return _balance;
  }
}

Then in main.dart:

import 'bank_account.dart';

void main() {
  BankAccount account = BankAccount();
  account.deposit(100);

  print("Balance is: ${account._balance}"); // ❌ ERROR!
}

Boom! Dart now throws a compile-time error:

Error: The getter '_balance' isn't defined for the class 'BankAccount'.

The Takeaway: Dart’s Encapsulation Is Different

So, here’s what I learned and what you should take away from this:

  • In Dart, prefixing with _ makes a variable or method private to the file (library), not just the class.

  • If your classes and main logic are in the same file, Dart won’t enforce class-level encapsulation.

  • To achieve stronger encapsulation, structure your code in multiple files or use getters and setters to manage controlled access.

How Other Languages Do It (Quick Comparison)

  • Java: private, protected, public keywords control access at class/package level

  • C++: Class-level privacy via private: / protected: blocks

  • Python: Name mangling (__var) gives soft privacy

  • Dart: Underscore _ = library-level privacy

Dart’s choice is intentional. It promotes module-based design, encouraging developers to treat files as cohesive units of logic, similar to libraries in larger ecosystems.

Real-World Best Practices

If you’re building something real and want to keep internals safe:

  • Split your classes into separate Dart files.

  • Use public getters and setters to expose or modify state safely.

  • Stick to the Single Responsibility Principle don’t overload one file with too many concerns.

Wrapping Up

What started as a simple budgeting feature turned into a lesson in how Dart thinks differently about encapsulation. And honestly? I’m glad I stumbled onto this early.

Knowing how Dart’s privacy model works will help you avoid subtle bugs, write cleaner code, and structure your apps better especially in large Flutter projects.

What About You?

Have you ever been surprised by a language’s encapsulation model? Hit me up if you’ve faced this in Dart (or any other language). Would love to hear your story too.

0
Subscribe to my newsletter

Read articles from Md. Al - Amin directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Md. Al - Amin
Md. Al - Amin

Experienced Android Developer with a demonstrated history of working for the IT industry. Skilled in JAVA, Dart, Flutter, and Teamwork. Strong Application Development professional with a Bachelor's degree focused in Computer Science & Engineering from Daffodil International University-DIU.