Dart: Dart Variables and Data Types

Last updated: 2026-08-26

Variables are containers for data, and types are the shape of the container — choose the right container, and your code will be safe.

1. What You Will Learn


2. A Developer's True Story

(1) Pain Point: Runtime crashes caused by haphazard variable declarations

When Bob was developing the early version of DataPipeline, he declared all variables with var and often changed their types midway. During a refactor, he mistakenly changed int orderCount to String orderCount. The compilation didn't report an error (dynamic type), but at runtime, orderCount * price threw an exception, causing the batch processing of 500,000 orders to interrupt and delaying report delivery by 4 hours.

(2) The Type-Safe Solution

Dart offers a range of declaration methods from loose to strict: const (compile-time constant) → final (runtime constant) → var (inferred type) → late (lazy initialization) → dynamic (dynamic type). Using them appropriately allows the compiler to help you find bugs.

DART
// Best practice for DataPipeline configuration
const int maxRecords = 1000000;       // Compile-time constant
final String pipelineName;             // Runtime constant (set once)
var processedCount = 0;                // Inferred as int, mutable
late String outputPath;                // Initialized later
// dynamic should be avoided if possible
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

(3) Benefits


3. Five Declaration Methods

(1) Declaration Method Decision Tree

100%
graph LR
  subgraph Declarations
    A[var] --> B[Type inference]
    C[final] --> D[Runtime constant]
    E[const] --> F[Compile-time constant]
    G[late] --> H[Lazy initialization]
    I[dynamic] --> J[Dynamic type]
  end
  subgraph Built-in Types
    K[int] --- L[double]
    M[String] --- N[bool]
  end
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: var — Type Inference

DART
void main() {
  var name = 'DataPipeline';   // Inferred as String
  var count = 1000;            // Inferred as int
  var rate = 0.08;             // Inferred as double

  // Can reassign same type
  name = 'Analytics';          // OK - still String
  count = 2000;                // OK - still int

  // Cannot change type
  // count = 'two thousand';   // Compile error!
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: final — Runtime Constant

DART
void main() {
  // Set once at runtime
  final DateTime startTime = DateTime.now();
  final String configPath = '/etc/datapipeline/config.yaml';

  // Cannot reassign
  // configPath = '/other/path';  // Compile error!

  // final with collection - contents can change
  final List<String> sources = ['orders.csv', 'products.csv'];
  sources.add('customers.csv');   // OK - modifying contents
  // sources = ['new_list'];       // Error - cannot reassign reference
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: const — Compile-time Constant

DART
void main() {
  // Must be known at compile time
  const int maxRecords = 1000000;
  const double taxRate = 0.08;
  const String version = '1.0.0';

  // const collections are deeply immutable
  const List<String> formats = ['json', 'csv', 'html'];
  // formats.add('xml');  // Error! Cannot modify const list

  // const constructor
  const config = PipelineConfig(batchSize: 10000);
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: late — Lazy Initialization

DART
class DataPipeline {
  late String outputPath;  // Will be set later

  void configure(String path) {
    outputPath = path;
  }

  late final int maxRecords = _loadMaxRecords();  // Computed on first access

  int _loadMaxRecords() {
    print('Loading max records config...');
    return 1000000;
  }
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: dynamic — Dynamic Type

DART
void main() {
  dynamic value = 'hello';   // String
  value = 42;                // OK - now int
  value = [1, 2, 3];        // OK - now List

  // No compile-time type checking - risky!
  // value.nonExistentMethod();  // No compile error, runtime crash

  // Prefer Object? over dynamic when you need flexibility
  Object? safeValue = 'test';
  // safeValue.nonExistentMethod();  // Compile error - safer
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

(2) Comparison of Five Declaration Methods

Declaration Method Type Mutable Initialization Time Safety Use Case
const Compile-time determined Immutable At declaration Highest Configuration constants, enum values
final Runtime determined Immutable At declaration or in constructor High Runtime configuration, injected values
var Inferred Mutable At declaration Medium Local variables, counters
late Explicitly annotated Mutable Deferred Medium Deferred initialization fields
dynamic Runtime Mutable Anytime Low JSON parsing, interoperability

4. Detailed Built-in Types

(1) Numeric Types

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: int and double

DART
void main() {
  int orderCount = 1500;
  double totalAmount = 52500.75;
  num genericNum = 42;      // num is supertype of int and double
  genericNum = 3.14;        // OK - num accepts both

  // Arithmetic operations
  double avgOrderValue = totalAmount / orderCount;
  int roundedDown = totalAmount.toInt();

  // Convenient methods
  print(totalAmount.toStringAsFixed(2));  // 52500.75
  print(orderCount.isEven);              // false (1500 is even, true)
  print(orderCount.clamp(0, 1000));      // 1000 (clamped to max)
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.
Type Range Purpose
int 64-bit (varies on JS) Counting, indexing
double 64-bit IEEE 754 Amounts, ratios
num Supertype of int + double General numeric values

(2) String Type

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: String Interpolation and Operations

DART
void main() {
  // String interpolation
  String product = 'DataPipeline';
  int users = 5000;
  print('$product has $users users');               // Simple interpolation
  print('Revenue: ${users * 99} USD');              // Expression interpolation
  print('Average: ${(125000 / users).toStringAsFixed(2)} USD');

  // Multi-line strings
  String description = '''
  DataPipeline - E-Commerce Analytics Tool
  Processes up to 1,000,000 orders per batch
  Supports JSON, CSV, and HTML output formats
  ''';

  // Raw strings (no escape processing)
  String path = r'C:\Users\Bob\data\orders.csv';
  String regex = r'\d{4}-\d{2}-\d{2}';  // Date pattern

  // Useful methods
  String raw = '  Hello, World!  ';
  print(raw.trim());                  // 'Hello, World!'
  print(raw.toUpperCase());           // '  HELLO, WORLD!  '
  print('USD 1,500.00'.replaceAll(',', ''));  // 'USD 1500.00'
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.
Feature Syntax Example
Simple interpolation $variable '$name'
Expression interpolation ${expr} '${a + b}'
Multi-line strings '''...''' Triple quotes
Raw strings r'...' r'\n' remains as is

(3) bool Type

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: Boolean Values and Conditions

DART
void main() {
  bool isProduction = true;
  bool isDebug = false;

  // Dart requires explicit bool in conditions
  int count = 0;
  // if (count) {}  // Error! Must be bool
  if (count > 0) {  // OK - explicit comparison
    print('Has records');
  }

  // Logical operators
  bool shouldProcess = isProduction && !isDebug;
  bool hasData = count > 0 || isProduction;
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

5. Type Inference and Explicit Annotation

(1) Best Practices

DART
// Good - let the compiler infer for local variables
var name = 'DataPipeline';     // Inferred String
var count = 100;               // Inferred int
var items = <String>[];        // Explicit generic, inferred List

// Good - explicit type for public API
String get displayName => name;
int get maxCapacity => 1000000;

// Good - final for values that don't change
final startTime = DateTime.now();

// Avoid - unnecessary explicit type on locals
// String name = 'DataPipeline';  // Redundant
// int count = 100;               // Redundant
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.
Scenario Recommendation Reason
Local variables var / final Reduces redundancy, inference is sufficient
Public API Explicit annotation Clear documentation, stable interface
Collection generics Explicit generic Avoids inference as List<dynamic>
Constructor parameters Explicit annotation Clear contract

6. Bob's Scenario: DataPipeline Configuration Variables

▶ Example

TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

: Configuration Variable Design

DART
// DataPipeline configuration constants
const String appName = 'DataPipeline';
const int defaultBatchSize = 10000;
const double defaultTaxRate = 0.08;

// Runtime configuration
class PipelineConfig {
  final String version;
  final int batchSize;
  final String outputFormat;

  PipelineConfig({
    this.version = '1.0.0',
    this.batchSize = defaultBatchSize,
    this.outputFormat = 'json',
  });

  @override
  String toString() =>
      '$appName v$version | Batch: $batchSize | Format: $outputFormat';
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

7. Complete Example: DataPipeline Variable Declaration in Practice

DART
// ============================================
// DataPipeline Variable Declaration Demo
// Shows proper use of all declaration types
// ============================================

// Compile-time constants - never change across runs
const String appName = 'DataPipeline';
const int maxRecords = 1000000;
const double taxRate = 0.08;
const List<String> supportedFormats = ['json', 'csv', 'html'];

class Order {
  final String id;          // Runtime constant after construction
  final double amount;      // Runtime constant after construction
  late String status;       // Will be set by processing logic

  Order({required this.id, required this.amount});

  double get tax => amount * taxRate;
  double get total => amount + tax;

  String get formattedAmount =>
      '\$${amount.toStringAsFixed(2)} USD';
}

class DataPipeline {
  final String name;
  var processedCount = 0;              // Mutable counter
  late final DateTime startTime;       // Set on first run

  DataPipeline({required this.name});

  void start() {
    startTime = DateTime.now();
    print('$appName started at $startTime');
  }

  void processOrder(Order order) {
    processedCount++;
    order.status = 'processed';
    print('Order ${order.id}: ${order.formattedAmount} '
        '(tax: \$${order.tax.toStringAsFixed(2)} USD)');
  }

  void printSummary() {
    print('\n=== Summary ===');
    print('Pipeline: $name');
    print('Processed: $processedCount orders');
    print('Capacity: $processedCount / $maxRecords');
  }
}

void main() {
  final pipeline = DataPipeline(name: 'E-Commerce Analytics');
  pipeline.start();

  final orders = [
    Order(id: 'ORD-001', amount: 1500.00),
    Order(id: 'ORD-002', amount: 3250.50),
    Order(id: 'ORD-003', amount: 890.25),
  ];

  for (final order in orders) {
    pipeline.processOrder(order);
  }

  pipeline.printSummary();
}
TEXT 📖 Display only
> **Output:** Run in your local DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x, and the results may vary slightly depending on the SDK version.

Output:

TEXT 📖 Display only
DataPipeline started at 2024-01-15 10:30:00.000
Order ORD-001: $1500.00 USD (tax: $120.00 USD)
Order ORD-002: $3250.50 USD (tax: $260.04 USD)
Order ORD-003: $890.25 USD (tax: $71.22 USD)

=== Summary ===
Pipeline: E-Commerce Analytics
Processed: 3 orders
Capacity: 3 / 1000000

❓ FAQ

Q: Which should I use, var or final? A: If the variable will not be reassigned, prefer final. If you need to modify it, use var. The official Dart lint rule prefer_final_locals recommends prioritizing final.

Q: What is the difference between const and final? A: const is a compile-time constant; its value must be determined at compile time. final is a runtime constant; it can only be assigned once, but its value can be computed at runtime. DateTime.now() cannot be const.

Q: When should I use late? A: Use late when a variable cannot be initialized at the point of declaration, but you can guarantee it will be initialized before use. Common scenarios include dependency injection and post-construction configuration. Misuse will lead to a LateInitializationError at runtime.

Q: What is the difference between dynamic and Object?? A: dynamic turns off all type checking; the compiler does not verify any method calls. Object? retains type checking; you can only call methods from Object. Prefer Object? when you need flexibility.

Q: Is Dart's int the same on Web and VM? A: Not exactly. On the Dart VM, int is a 64-bit integer. When compiled to JS, int is subject to JS Number limitations (53-bit precision). Be cautious with large number arithmetic.

Q: Can I use single quotes or double quotes for String? A: Both are fine, and they have the exact same effect. The official Dart style guide recommends single quotes, using double quotes only when the string itself contains a single quote.

Q: When should I use the num type? A: Use num when you need a parameter or variable to accept both int and double. num is the supertype of int and double and supports basic arithmetic operations.


📖 Summary


📝 Exercises

  1. Basic (Difficulty ⭐): Declare the following variables: a const application name, a final current time, and a var counter. Print their values and types respectively.
  2. Intermediate (Difficulty ⭐⭐): Write a function that accepts a num type parameter and returns a USD-formatted string of that number (e.g., formatUSD(1500.5) returns "$1,500.50 USD"). Handle both int and double inputs.
  3. Advanced (Difficulty ⭐⭐⭐): Design a Config class that uses late to lazily initialize a database connection string, uses const to define default values, and uses final to store configuration read from environment variables. Demonstrate the collaboration of these three declaration methods.

← Previous Lesson | Next Lesson →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏