← Back to blog
Dart Fundamentals · Part 2 of 10
June 22, 20268 min read

Getting Started with Dart: My Learning Notes

DartFlutter

Getting Started with Dart: My Learning Notes

These are my running notes as I learn Dart — pulled from Dart Apprentice: Fundamentals and the official Dart language docs. Dart feels friendly, especially if you've touched any C-style language before, and I'm writing all of this the way I wish someone had explained it to me when I started. I'll keep adding sections as I work through more topics. Hope it helps you too!


Everything in Dart is an Object

This was the first thing that surprised me. Unlike C or Java (where int, double etc. are primitive types), Dart has no primitives at all. Every single value — even a plain number like 10 — is an object.

That means you can call methods directly on numbers:

print(10.isEven);     // true
print(3.14.round());  // 3

Pretty cool, right? isEven is a property of the int class and .round() is a method of the double class. You'd never be able to do this in a language with primitive types.


Dart is Type-Safe (and That's a Good Thing)

Dart doesn't allow implicit type conversion. That means if you have a String and you try to use it as a number, Dart will refuse — loudly. You have to be explicit about converting types.

This might feel annoying at first, but trust me, it saves you from a whole class of bugs that are really hard to track down in loosely-typed languages.


var vs dynamic — They Look Similar but Are Very Different

This tripped me up initially.

var — type inferred, but fixed after assignment

var num3 = 10;   // Dart infers: this is an int
// num3 = "hello"; ❌ Can't do this — it's locked in as int

var is type-safe. The type is figured out at the time you assign a value, and after that it can't change. Use it when the type is obvious from the value you're assigning.

dynamic — anything goes (but be careful)

dynamic num2 = '10';
num2 = 42;       // ✅ totally fine
num2 = [1, 2];   // ✅ also fine

dynamic can hold any type and that type can change anytime. This sounds great but it turns off Dart's type checking, so you lose all the safety guarantees. Use it only when you absolutely have to — like when working with JSON data where the structure is truly unknown.


const vs final — Two Flavors of "Unchangeable"

Both const and final mean you can only assign a value once. The difference is when that value is determined.

final — set once, at runtime

The value can come from anywhere — a user input, an API call, a calculation. You just can't reassign it later.

final currentTime = DateTime.now();  // ✅ determined at runtime
final myList = [1, 2];
myList.add(3);   // ✅ the list itself can still be modified

Think of it as: "I don't know the value yet, but once I know it, it's locked in."

const — fixed at compile time, forever

The value must be known before the app even runs. Both the variable reference and the object itself become completely immutable.

const pi = 3.14159;       // ✅ known at compile time
const myList = [1, 2];
// myList.add(3);  ❌ Error! The list is frozen too

An extra bonus: Dart reuses the same memory location for identical const objects, which can improve performance.

Quick cheat sheet

| | var | final | const | | ------------------ | ------- | ------------- | ------------ | | Can be reassigned? | Yes | No | No | | Type inferred? | Yes | Yes | Yes | | When is value set? | Runtime | Runtime | Compile time | | Object mutable? | Yes | Yes (usually) | No |

Rule of thumb:

  1. Use const whenever possible
  2. Use final when the value comes from runtime data (user input, API, DateTime.now())
  3. In Flutter, prefer const widgets — it gives you a performance boost

Naming Conventions

Dart has pretty clear naming conventions and following them makes your code feel "Dart-like":

  • Variables and functions → lowerCamelCase
  • Classes and Enums → UpperCamelCase
int myAge = 25;           // variable — lowerCamelCase
void calculateTotal() {}  // function — lowerCamelCase
class UserProfile {}      // class — UpperCamelCase
enum OrderStatus {}       // enum — UpperCamelCase

Increment and Decrement Operators

These are pretty standard across languages, but worth noting the difference between pre and post forms:

int i = 5;

// Pre-increment: increments FIRST, then uses the value
print(++i);  // prints 6 (i is now 6)

// Post-increment: uses the value FIRST, then increments
print(i++);  // prints 6 (i becomes 7, but we see 6)
print(i);    // prints 7

// Same idea with decrement
print(--i);  // pre-decrement
print(i--);  // post-decrement

There are also compound assignment operators which are just shorthand:

i += 1;   // same as i = i + 1
i -= 1;   // same as i = i - 1
i *= 2;   // same as i = i * 2
i /= 2;   // same as i = i / 2

Comments — Talking to Future You

Here's a truth nobody tells you early on: most of the code you write, you write for other people — and the biggest "other person" is you, six months from now, staring at your own code wondering what on earth you were thinking. Comments are how you leave little notes for that confused future version of yourself.

Dart gives you three flavours of comments, and they each have a job.

Single-line comments — the quick note

Anything after // until the end of the line gets completely ignored by Dart. This is your everyday, jot-it-down comment.

void main() {
  // TODO: refactor into an AbstractLlamaGreetingFactory?
  print('Welcome to my Llama farm!');
}

I use these constantly — for // TODO: reminders, for explaining a why that isn't obvious from the code, or just for leaving myself breadcrumbs.

Multi-line comments — when one line isn't enough

Start with /*, end with */, and everything in between is ignored — across as many lines as you want. These are great for temporarily "switching off" a chunk of code while you debug.

void main() {
  /*
   * This is a lot of work. Consider raising chickens.

  Llama larry = Llama();
  larry.feed();
  larry.exercise();
  larry.clean();
   */
}

A neat little detail: multi-line comments can nest. So you can comment out a block that already contains a /* */ comment, and Dart won't get confused. Most C-style languages can't do this — small thing, but it'll save you a headache one day.

Documentation comments — the fancy ones

This is where Dart really shines. Documentation comments use /// (or /** */ for the multi-line version), and they're special: tools can read them and turn them into actual, browsable HTML documentation.

/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
///
/// Just like any other animal, llamas need to eat,
/// so don't forget to [feed] them some [Food].
class Llama {
  String? name;

  /// Feeds your llama [food].
  ///
  /// The typical llama eats one bale of hay per week.
  void feed(Food food) {
    // ...
  }

  /// Exercises your llama with an [activity] for
  /// [timeLimit] minutes.
  void exercise(Activity activity, int timeLimit) {
    // ...
  }
}

Two things to notice here:

  1. Using /// on several lines in a row behaves just like one big multi-line doc comment. So you don't need /** */ — most Dart code just stacks /// lines, and that's the convention you'll see everywhere.
  2. Those square brackets are magic. Inside a doc comment, the analyzer ignores everything except text wrapped in [brackets] — and it treats those as links. So [feed] becomes a clickable reference to the feed method, [Food] links to the Food class, [timeLimit] points to that parameter, and so on. Write [someThing] and Dart figures out what you meant and wires up the link for you.

When you're ready to generate real docs from all this, run the dart doc tool and it'll spit out a full HTML documentation site. This is exactly how the docs for packages on pub.dev get made.

My rule of thumb: use // for notes to yourself inside a function, and /// for anything public — classes, methods, parameters — that another developer (or future-you) might actually call. If someone could use it without reading the implementation, it deserves a /// comment.


Wrapping Up

Here's what's stuck with me most so far:

  • Everything in Dart is an object — no primitives
  • var locks in the type after assignment; dynamic doesn't (but avoid dynamic unless you need it)
  • Prefer const > final > var — in that order — for values that don't change
  • Dart is type-safe by design, and that's actually a superpower once you embrace it
  • Comments come in three flavours — //, /* */, and the documentation /// — and the [bracket] references inside doc comments auto-link to your code

Next up in the series, I dig into Dart's built-in types — numbers, strings, booleans and friends. I'll keep adding to these notes as I work through more topics. If you're also learning Dart — especially for Flutter — I'd recommend pairing the official Dart docs with Dart Apprentice. Both explain things in a really approachable way.


These notes draw from my personal reading of Dart Apprentice: Fundamentals and the official Dart language documentation. Written for my own future reference and anyone else who finds it useful.