openskills.info
Dart Fundamentals logo

Dart Fundamentals

itProgramming languages

Dart Fundamentals

Dart is a general-purpose programming language built for applications that run across native and web platforms. It is also the language behind Flutter. You can use Dart without Flutter for command-line tools, servers, libraries, and web applications.

Keep the first mental model small:

Dart source → analyzer and compiler → native or web output → Dart runtime and libraries → application behavior

The language defines syntax, types, and program behavior. The Dart SDK supplies the analyzer, compilers, runtime, package tools, formatter, and other developer tools. Flutter is a separate user-interface framework built on Dart.

Why Dart exists

Dart combines static type checking with type inference. You can state a type when it clarifies a contract, while the analyzer infers many local types from their initial values. Sound null safety makes non-nullable types the default and reports many possible null errors before execution.

Dart also targets different execution environments. Native development uses a virtual machine with just-in-time compilation. Production native applications can use ahead-of-time compilation. Web compilers produce JavaScript or WebAssembly.

These features support fast development and several deployment targets. They do not make every Dart library portable. Platform-specific libraries such as dart:io do not work on every target.

Values, variables, and types

Every Dart variable refers to an object. Numbers, functions, and collections are objects too. A type describes which values a variable can hold and which operations are valid.

var language = 'Dart';        // Inferred as String.
int releaseYear = 2011;       // Explicit type.
final stable = true;          // Assigned once at runtime.
const minutesPerHour = 60;    // Compile-time constant.

Use var when the initializer makes the local type clear. Use an explicit type when it communicates a public contract or removes ambiguity. Use final for a variable assigned once. Use const when the value and everything needed to create it are known at compile time.

Avoid dynamic unless you intentionally need runtime member lookup. Object? accepts any Dart object or null while retaining static checks. dynamic tells the analyzer to allow operations that might fail at runtime.

Null safety is part of the type

String does not allow null. String? does. A nullable value must be checked before you use members that require a non-null receiver.

int labelLength(String? label) {
  if (label == null) return 0;
  return label.length;
}

After the check, flow analysis promotes label to String inside the remaining path. The null assertion operator tells Dart that a nullable expression is non-null. It throws if that assertion is wrong, so a check or a better type is usually safer.

Functions package behavior

Functions are objects. You can store them in variables, pass them as arguments, and return them from other functions. Parameters may be positional or named. Named parameters are optional unless marked required.

String greeting(String name, {String prefix = 'Hello'}) {
  return '$prefix, $name';
}

The parameter types define the input contract. The return type defines the normal output contract. A short expression-bodied function can use arrow syntax, but a block is clearer when behavior has several steps.

Collections model groups of values

Dart has built-in literals for lists, sets, and maps.

  • List<T>: ordered values addressed by index.
  • Set<T>: unique values.
  • Map<K, V>: values addressed by keys.

Generics preserve element types. A List<String> accepts strings, and the analyzer can reject an attempted integer insertion. Collection literals also support conditional elements, spread elements, and loop elements for declarative construction.

Classes define objects

A class groups fields and methods. Constructors establish initial state. Dart supports single inheritance for classes, interfaces through class declarations, mixins for reusable behavior, and extension methods for adding statically resolved operations to existing types.

class Counter {
  Counter(this.value);

  int value;

  void increment() {
    value++;
  }
}

Start with the responsibility of the type. Keep fields private to a library when callers should not change them directly. In Dart, an identifier beginning with an underscore is private to its library.

Records and patterns help with structured data. A record is an immutable, fixed-size, typed group of values. A pattern checks or destructures the shape of another value. Use a class when data needs named behavior, identity, or a lasting abstraction.

Control flow makes decisions visible

Use if for conditions, switch for alternatives, and loops for repetition. Modern switch expressions and patterns can select by both value and shape. Exhaustiveness checking helps when every possible case must be handled.

Exceptions leave normal control flow. Dart exceptions are unchecked, so a function does not declare the exceptions it might throw. Catch an exception where you can recover, translate it, or add useful context. Use finally for cleanup that must run after either success or failure.

Asynchronous work follows the event loop

A Future<T> represents one eventual value or error. A Stream<T> represents a sequence of asynchronous events. An async function can await a future without blocking the isolate's event loop.

Asynchrony and parallelism are different. Awaiting input or output lets other queued work proceed in the same isolate. A separate isolate can run CPU work concurrently on another core. Isolates have separate memory and communicate with messages instead of shared mutable state.

Packages organize reusable code

A Dart package uses pubspec.yaml for metadata, SDK constraints, and dependencies. The dart pub commands resolve and manage packages. Public libraries normally live under lib, command-line entry points under bin, and tests under test.

The core dart: libraries cover common needs. Packages from pub.dev add more APIs. Check platform support, maintenance, version constraints, and API documentation before adopting a dependency.

A practical learning path

Begin with variables, built-in types, functions, control flow, and collections. Then practice classes, generics, error handling, records, and patterns. Add futures and streams before reaching for isolates. Learn the analyzer, formatter, tests, and package manager as part of the language rather than as cleanup work.

Choose a target after that foundation. Flutter adds a UI framework. Server and command-line work use native libraries and packages. Browser applications use web-compatible libraries and compilation targets. The official language guide and API reference provide exact details when this orientation ends.