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:
- Numbers
int
: Integer valuesdouble
: Floating-point values
- Strings
String
: Text values
- Booleans
bool
: true or false values
- Collections
List
: Ordered collections (similar to arrays in JS or lists in Python)Set
: Unordered collections of unique itemsMap
: Key-value pairs (like dictionaries in Python or objects in JS)
- Others
Runes
: For expressing Unicode charactersSymbol
: For representing operators or identifiersNull
: Represented bynull
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
.add(6); // [1, 2, 3, 4, 5, 6]
numbers.remove(3); // [1, 2, 4, 5, 6]
numbersbool 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
.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}
uniqueNumbers
// 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
'Charlie'] = 35; // Add new entry
ages['Bob'] = 41; // Update existing entry
ages[
// 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
.forEach((name, age) {
ages'$name is $age years old');
print(});
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) {
'Value is an integer');
print(}
// Type casting
Object someObject = 'Hello, Dart!';
if (someObject is String) {
String stringValue = someObject as String;
.toUpperCase()); // "HELLO, DART!"
print(stringValue}
// Smart casting
if (someObject is String) {
// No need for explicit casting here
.toUpperCase()); // Dart knows someObject is a String in this scope
print(someObject}
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) {
.toUpperCase()); // Safe access
print(nullableName}
// 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 ...
= 'This is initialized later'; // Must be assigned before use description
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)
+= student['age'] as int? ?? 0;
totalAge
// Collect subjects
var subjects = student['subjects'] as List<String>? ?? [];
.addAll(subjects);
allSubjects}
// 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);
'Student count: ${stats['count']}');
print('Average age: ${stats['averageAge']}');
print('All subjects: ${(stats['subjects'] as Set<String>).join(', ')}');
print(}
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