2  Introduction to Dart Types

Welcome to the world of Dart and Flutter! As someone with experience in Python, JavaScript, and R, you’ll find some familiar concepts in Dart, but also some important differences. Let’s start with Dart’s type system, which is a fundamental aspect of the language.

2.1 Built-in Types in Dart

Dart is a statically typed language with type inference capabilities, making it both safe and flexible. Your background in Python and JavaScript will help you understand Dart’s approach to types, as it combines elements from both.

Here are the core built-in types in Dart:

  1. Numbers
    • int: Integer values
    • double: Floating-point values
  2. Strings
    • String: Text values
  3. Booleans
    • bool: true or false values
  4. Collections
    • List: Ordered collections (similar to arrays in JS or lists in Python)
    • Set: Unordered collections of unique items
    • Map: Key-value pairs (like dictionaries in Python or objects in JS)
  5. Others
    • Runes: For expressing Unicode characters
    • Symbol: For representing operators or identifiers
    • Null: Represented by null

Let’s explore each of these with examples that will highlight the differences and similarities with your existing knowledge.

2.2 Numbers: int and double

In Dart, unlike JavaScript but similar to Python, there’s a clear distinction between integers and floating-point values:

// Integer values
int age = 30;
int hexValue = 0xEADEBAEE; // Hexadecimal notation

// Double values
double height = 1.75;
double exponent = 1.42e5; // Scientific notation: 142000.0

// Type inference (Dart can determine the type)
var weight = 70; // Inferred as int
var pi = 3.14159; // Inferred as double

// Number conversions
int roundedValue = pi.toInt(); // 3
String ageAsString = age.toString(); // "30"
double ageAsDouble = age.toDouble(); // 30.0

Unlike Python, Dart doesn’t have a built-in decimal type for arbitrary precision. For financial calculations, you might want to use a dedicated package.

2.3 Strings

Strings in Dart will feel familiar, with some nice additions:

// String creation
String name = 'Kittipos'; // Single quotes
String greeting = "Hello"; // Double quotes are fine too

// String interpolation
String message = 'Hello, $name!'; // "Hello, Kittipos!"
String calculation = 'The sum is ${2 + 3}.'; // "The sum is 5."

// Multiline strings
String multiline = '''
This is a multiline string.
It can span multiple lines.
''';

// Raw strings (ignores escape sequences)
String rawString = r'This \n will not create a new line';

// String operations
String lowercase = name.toLowerCase(); // "kittipos"
bool containsK = name.contains('K'); // true
List<String> parts = name.split('i'); // ["K", "tt", "pos"]

The string interpolation ($variable and ${expression}) is similar to JavaScript template literals but with different syntax.

2.4 Booleans

Booleans are straightforward:

// Boolean values
bool isActive = true;
bool hasPermission = false;

// Boolean operations
bool result = isActive && hasPermission; // Logical AND: false
bool anotherResult = isActive || hasPermission; // Logical OR: true
bool negation = !isActive; // Logical NOT: false

// Conditional expressions
var status = isActive ? 'active' : 'inactive'; // Ternary operator: "active"

Unlike JavaScript but similar to Python, Dart is strict about boolean types. You cannot use non-boolean values in boolean contexts (e.g., if (1) won’t work).

2.5 Lists (Arrays)

Lists in Dart are similar to Python lists or JavaScript arrays:

// Creating lists
List<int> numbers = [1, 2, 3, 4, 5];
List<String> fruits = ['apple', 'banana', 'cherry'];

// Using var with type inference
var vegetables = ['carrot', 'broccoli', 'spinach']; // List<String>

// Empty list with type specification
List<double> emptyScores = [];

// Accessing elements (zero-based indexing)
int secondNumber = numbers[1]; // 2
String firstFruit = fruits[0]; // "apple"

// List properties and methods
int length = numbers.length; // 5
numbers.add(6); // [1, 2, 3, 4, 5, 6]
numbers.remove(3); // [1, 2, 4, 5, 6]
bool containsBanana = fruits.contains('banana'); // true

// Spread operator (similar to JavaScript)
var combinedFruits = [...fruits, 'mango', 'kiwi']; // Creates a new list with all elements

// List filtering and mapping (similar to Python/JavaScript)
var evenNumbers = numbers.where((number) => number % 2 == 0).toList(); // [2, 4, 6]
var doubledNumbers = numbers.map((number) => number * 2).toList(); // [2, 4, 8, 10, 12]

The angle bracket notation (List<int>) specifies the type of elements that the list can contain, which is part of Dart’s generic type system.

2.6 Sets

Sets are collections of unique elements:

// Creating sets
Set<int> uniqueNumbers = {1, 2, 3, 4, 5};
var uniqueFruits = <String>{'apple', 'banana', 'cherry'};

// Empty set with type specification
Set<String> emptySet = {};

// Set operations
uniqueNumbers.add(6); // {1, 2, 3, 4, 5, 6}
uniqueNumbers.add(1); // No change, 1 is already in the set
uniqueNumbers.remove(3); // {1, 2, 4, 5, 6}

// Set operations
var setA = {1, 2, 3, 4};
var setB = {3, 4, 5, 6};
var union = setA.union(setB); // {1, 2, 3, 4, 5, 6}
var intersection = setA.intersection(setB); // {3, 4}
var difference = setA.difference(setB); // {1, 2}

Sets are particularly useful when you need to maintain a collection of unique items or perform set operations.

2.7 Maps (Dictionaries)

Maps in Dart are similar to Python dictionaries or JavaScript objects:

// Creating maps
Map<String, int> ages = {
  'John': 30,
  'Alice': 25,
  'Bob': 40,
};

var scores = {
  'math': 95,
  'science': 88,
  'history': 75,
};

// Empty map with type specification
Map<String, double> emptyMap = {};

// Accessing values
int aliceAge = ages['Alice']; // 25
int defaultAge = ages['Charlie'] ?? 0; // Using ?? to provide a default value when key doesn't exist

// Adding or updating entries
ages['Charlie'] = 35; // Add new entry
ages['Bob'] = 41; // Update existing entry

// Map properties and methods
int size = ages.length; // 3
bool containsJohn = ages.containsKey('John'); // true
List<String> names = ages.keys.toList(); // ['John', 'Alice', 'Bob', 'Charlie']
List<int> allAges = ages.values.toList(); // [30, 25, 41, 35]

// Iterating over a map
ages.forEach((name, age) {
  print('$name is $age years old');
});

Maps are useful for key-value associations and lookups.

2.8 Type Checking and Casting

Dart provides ways to check and convert between types:

// Type checking
var value = 42;
if (value is int) {
  print('Value is an integer');
}

// Type casting
Object someObject = 'Hello, Dart!';
if (someObject is String) {
  String stringValue = someObject as String;
  print(stringValue.toUpperCase()); // "HELLO, DART!"
}

// Smart casting
if (someObject is String) {
  // No need for explicit casting here
  print(someObject.toUpperCase()); // Dart knows someObject is a String in this scope
}

The is operator checks if a value is of a particular type, and the as operator performs an explicit cast.

2.9 Null Safety

One of Dart’s most important features is null safety, which helps prevent null reference errors:

// Non-nullable types (default in modern Dart)
String name = 'Kittipos'; // Cannot be null
int age = 30; // Cannot be null

// Nullable types (explicit declaration)
String? nullableName = null; // Can be null
int? nullableAge; // Initialized to null

// Working with nullable types
if (nullableName != null) {
  print(nullableName.toUpperCase()); // Safe access
}

// Null-aware operators
String displayName = nullableName ?? 'Guest'; // If nullableName is null, use 'Guest'
int length = nullableName?.length ?? 0; // Safe access to length, default to 0 if null

// The late keyword (for non-nullable variables initialized after declaration)
late String description;
// ... some code ...
description = 'This is initialized later'; // Must be assigned before use

This null safety system is one of Dart’s strongest features, helping to eliminate a whole class of common runtime errors.

2.10 Comparing Dart Types with Languages You Know

Feature Dart Python JavaScript R
Type System Static with inference Dynamic Dynamic with optional types (TypeScript) Dynamic
Numbers int, double int, float, complex Number numeric, integer, double
Strings String str String character
Lists/Arrays List<T> list Array vector, list
Maps/Dictionaries Map<K,V> dict Object, Map list, environment
Sets Set<T> set Set No built-in set
Null representation null None null, undefined NULL
Boolean values true, false True, False true, false TRUE, FALSE

2.11 A Practical Example

Let’s put it all together with a practical example - a simple function to calculate statistics for a group of students:

Map<String, dynamic> calculateStatistics(List<Map<String, dynamic>> students) {
  // Early return for empty list
  if (students.isEmpty) {
    return {'count': 0, 'averageAge': 0.0, 'subjects': <String>{}};
  }
  
  // Calculate total age and collect all subjects
  int totalAge = 0;
  Set<String> allSubjects = {};
  
  for (var student in students) {
    // Access age with null safety (default to 0 if null)
    totalAge += student['age'] as int? ?? 0;
    
    // Collect subjects
    var subjects = student['subjects'] as List<String>? ?? [];
    allSubjects.addAll(subjects);
  }
  
  // Calculate average
  double averageAge = students.isEmpty ? 0.0 : totalAge / students.length;
  
  // Return statistics
  return {
    'count': students.length,
    'averageAge': averageAge,
    'subjects': allSubjects,
  };
}

// Usage
void main() {
  var studentData = [
    {
      'name': 'Alice',
      'age': 20,
      'subjects': ['Math', 'Physics', 'English']
    },
    {
      'name': 'Bob',
      'age': 22,
      'subjects': ['Biology', 'Chemistry', 'English']
    },
    {
      'name': 'Charlie',
      'age': 19,
      'subjects': ['Computer Science', 'Statistics', 'Math']
    }
  ];
  
  var stats = calculateStatistics(studentData);
  print('Student count: ${stats['count']}');
  print('Average age: ${stats['averageAge']}');
  print('All subjects: ${(stats['subjects'] as Set<String>).join(', ')}');
}

This example demonstrates many of Dart’s type features:

  • Type annotations with generics (List<Map<String, dynamic>>)
  • Null safety handling with ?? operator
  • Type checking and casting
  • Collection operations
  • String interpolation
  • Maps and sets for data processing