No sections found
We couldn't find anything matching your search query. Try adjusting your keywords.
Learn Modern Dart for Python and Javascript Developers
In 2026, JavaScript/TypeScript remains ubiquitous, and Python dominates AI. However, developers often struggle with JS's lack of true multi-threading and Python's GIL. Enter Dart.
Dart is the secret weapon behind Flutter, but it has evolved into a powerhouse general-purpose language. It provides the ergonomics of TypeScript, the object-oriented structure of Java, and the compilation targets of Go. With Sound Null Safety, Isolates for true concurrency, and ultra-fast AOT compilation, Dart is designed for the modern full-stack developer.
2. The Rosetta Stone: Types Grid
Unlike JS and Python, Dart is strongly, statically typed with robust type inference. You can use var or final to let the compiler figure it out, but the types are strictly enforced at compile time.
| Concept | Dart (Statically Typed) | Python 3.12+ (Type Hints) | JS (ES2026) |
|---|---|---|---|
| Integer | int x = 42; // or: final x = 42; |
x: int = 42 | let x = 42; |
| Float | double pi = 3.14; | pi: float = 3.14 | let pi = 3.14; |
| String | String name = "Hi"; | name: str = "Hi" | let name = "Hi"; |
| List/Array | List<int> nums = [1, 2, 3]; | nums: list[int] = [1, 2, 3] | const nums = [1, 2, 3]; |
| Dictionary | Map<String, int> map = {"k": 1}; | map: dict[str, int] = {"k": 1} | const map = { "k": 1 }; |
| Null/None | int? val = null; | val: int | None = None | let val = null; |
| Exceptions | try { ... } catch (e) { ... } | try: ... except Exception: ... | try { ... } catch (e) { ... } |
int can never be null. If you want a variable to possibly be null, you must declare it as int?. The compiler will then force you to check for null before using it. Goodbye, "Cannot read properties of undefined".
3. Guess the Number Game
Dart Features Introduced: Standard library imports, Type inference (final), String interpolation, and basic I/O.
import 'dart:io';
import 'dart:math';
void main() {
// `final` infers the type, but guarantees the variable cannot be reassigned.
// Using `final` is best practice whenever a variable won't change.
final secret = Random().nextInt(100) + 1;
print("Guess the number between 1 and 100!");
while (true) {
stdout.write("> "); // stdout.write does not append a newline
final input = stdin.readLineSync();
// Because input can be null (EOF), Dart forces us to handle it.
if (input == null) break;
// int.tryParse returns an int?, meaning it will be null if parsing fails.
final guess = int.tryParse(input.trim());
if (guess == null) {
print("Please type a valid number!");
continue;
}
if (guess < secret) {
print("Higher!");
} else if (guess > secret) {
print("Lower!");
} else {
print("You win!");
break;
}
}
}
import random
def main():
secret_number = random.randint(1, 100)
print("Guess the number between 1 and 100!")
while True:
try:
guess = int(input("> ").strip())
except ValueError:
print("Please type a valid number!")
continue
if guess < secret_number:
print("Higher!")
elif guess > secret_number:
print("Lower!")
else:
print("You win!")
break
if __name__ == "__main__":
main()
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
async function main() {
const rl = readline.createInterface({ input, output });
const secret = Math.floor(Math.random() * 100) + 1;
console.log("Guess the number between 1 and 100!");
while (true) {
const guess = parseInt((await rl.question('> ')).trim(), 10);
if (isNaN(guess)) {
console.log("Please type a valid number!");
continue;
}
if (guess < secret) console.log("Higher!");
else if (guess > secret) console.log("Lower!");
else {
console.log("You win!");
break;
}
}
rl.close();
}
main();
4. Arithmetic Command Line Game
Dart Features Introduced: Elegant string interpolation ($var and ${expr}).
import 'dart:io';
import 'dart:math';
void main() {
final rng = Random();
print("Solve the addition problems! Type 'quit' to exit.");
while (true) {
final a = rng.nextInt(10) + 1;
final b = rng.nextInt(10) + 1;
// Dart's string interpolation uses $ for variables and ${} for expressions
stdout.write("What is $a + $b? ");
final input = stdin.readLineSync()?.trim();
if (input == "quit") {
print("Thanks for playing!");
break;
}
final answer = int.tryParse(input ?? "");
if (answer == null) {
print("Please enter a number or 'quit'.");
} else if (answer == a + b) {
print("Correct!");
} else {
print("Wrong! It was ${a + b}.");
}
}
}
import random
def main():
print("Solve the addition problems! Type 'quit' to exit.")
while True:
a, b = random.randint(1, 10), random.randint(1, 10)
user_input = input(f"What is {a} + {b}? ").strip()
if user_input == "quit":
print("Thanks for playing!")
break
try:
if int(user_input) == a + b:
print("Correct!")
else:
print(f"Wrong! It was {a + b}.")
except ValueError:
print("Please enter a number or 'quit'.")
if __name__ == "__main__": main()
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
async function main() {
const rl = readline.createInterface({ input, output });
console.log("Solve addition! Type 'quit' to exit.");
while (true) {
const a = Math.floor(Math.random() * 10) + 1;
const b = Math.floor(Math.random() * 10) + 1;
const userInput = (await rl.question(`What is ${a} + ${b}? `)).trim();
if (userInput === 'quit') break;
const answer = parseInt(userInput, 10);
if (isNaN(answer)) console.log("Enter a number or 'quit'.");
else if (answer === a + b) console.log("Correct!");
else console.log(`Wrong! It was ${a + b}.`);
}
rl.close();
}
main();
5. State Machine & Settings
Dart Features Introduced: sealed classes, Enum types, and Switch Expressions (Dart 3's flagship feature).
import 'dart:io';
import 'dart:math';
enum Operation { add, multiply }
// `sealed` means all subclasses MUST be defined in this same file.
// This allows the compiler to know every possible state, enabling exhaustive pattern matching!
sealed class AppState {}
class Menu extends AppState {}
class Playing extends AppState {}
class Quit extends AppState {}
class Settings {
int min = 1;
int max = 10;
Operation op = Operation.add;
}
void main() {
AppState state = Menu();
final settings = Settings();
final rng = Random();
while (state is! Quit) {
// Dart 3 Switch Statement. It guarantees we handle every single subclass of AppState.
switch (state) {
case Menu():
stdout.write("1. Play 2. Set Multiply 3. Quit\n> ");
final input = stdin.readLineSync()?.trim();
switch (input) {
case "1": state = Playing();
case "2":
settings.op = Operation.multiply;
print("Operation set to Multiplication.");
case "3": state = Quit();
default: print("Invalid option.");
}
case Playing():
final a = rng.nextInt(settings.max) + settings.min;
final b = rng.nextInt(settings.max) + settings.min;
// Dart 3 Switch Expression! Returns a value directly, acting like a super-powered ternary.
final (symbol, correct) = switch (settings.op) {
Operation.add => ('+', a + b),
Operation.multiply => ('*', a * b),
};
stdout.write("What is $a $symbol $b? ('menu' to go back) ");
final input = stdin.readLineSync()?.trim();
if (input == 'menu') {
state = Menu();
continue;
}
final ans = int.tryParse(input ?? "");
if (ans != null) {
if (ans == correct) print("Correct!");
else print("Wrong, it was $correct");
} else {
print("Not a number.");
}
}
}
}
import random
from enum import Enum, auto
from dataclasses import dataclass
class Operation(Enum): ADD = auto(); MULTIPLY = auto()
class AppState(Enum): MENU = auto(); PLAYING = auto(); QUIT = auto()
@dataclass
class Settings:
min: int = 1
max: int = 10
op: Operation = Operation.ADD
def main():
state, settings = AppState.MENU, Settings()
while True:
match state:
case AppState.MENU:
user_in = input("1. Play 2. Multiply 3. Quit\n> ").strip()
match user_in:
case "1": state = AppState.PLAYING
case "2": settings.op = Operation.MULTIPLY
case "3": state = AppState.QUIT
case _: print("Invalid option.")
case AppState.PLAYING:
a = random.randint(settings.min, settings.max)
b = random.randint(settings.min, settings.max)
sym, cor = ("+", a+b) if settings.op == Operation.ADD else ("*", a*b)
ans = input(f"What is {a} {sym} {b}? ('menu' to exit) ").strip()
if ans == "menu":
state = AppState.MENU
continue
try:
if int(ans) == cor: print("Correct!")
else: print(f"Wrong, it was {cor}")
except ValueError: pass
case AppState.QUIT: break
if __name__ == "__main__": main()
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
const AppState = { MENU: 'MENU', PLAYING: 'PLAYING', QUIT: 'QUIT' };
const Operation = { ADD: 'ADD', MULTIPLY: 'MULTIPLY' };
async function main() {
const rl = readline.createInterface({ input, output });
let state = AppState.MENU;
let settings = { min: 1, max: 10, op: Operation.ADD };
while (true) {
switch (state) {
case AppState.MENU:
const menuIn = (await rl.question("1. Play 2. Set Multiply 3. Quit\n> ")).trim();
if (menuIn === "1") state = AppState.PLAYING;
else if (menuIn === "2") settings.op = Operation.MULTIPLY;
else if (menuIn === "3") state = AppState.QUIT;
break;
case AppState.PLAYING:
const a = Math.floor(Math.random() * 10) + 1;
const b = Math.floor(Math.random() * 10) + 1;
const symbol = settings.op === Operation.ADD ? '+' : '*';
const correct = settings.op === Operation.ADD ? a + b : a * b;
const ansStr = (await rl.question(`What is ${a} ${symbol} ${b}? `)).trim();
if (ansStr === 'menu') { state = AppState.MENU; continue; }
const ans = parseInt(ansStr, 10);
if (!isNaN(ans)) {
if (ans === correct) console.log("Correct!");
else console.log(`Wrong, it was ${correct}`);
}
break;
case AppState.QUIT:
rl.close();
return;
}
}
}
main();
6. Flashcards Quizzer: Records
Dart Features Introduced: Records! Instead of creating an entire class for a simple pair of values, Dart 3 allows you to group data anonymously and safely.
import 'dart:io';
void main() {
// This is a List of Named Records. It has the ergonomic feel of JS objects or Python dicts,
// but it is entirely statically typed! (Type: List<({String question, String answer})>)
final deck = [
(question: "Capital of France?", answer: "Paris"),
(question: "2 ** 8?", answer: "256"),
(question: "Dart package manager?", answer: "pub"),
];
// Dart's standard list includes built-in shuffling
deck.shuffle();
for (final card in deck) {
stdout.write("Q: ${card.question} (Press Enter)");
stdin.readLineSync();
print("A: ${card.answer}\n");
}
print("Deck completed!");
}
7. OS Downloads Folder Sorter
Dart Features Introduced: dart:io for native system interactions. Dart is a fantastic scripting language, compiling instantly.
import 'dart:io';
void main() {
final downloadsDir = Directory('./downloads_test');
if (!downloadsDir.existsSync()) {
downloadsDir.createSync(recursive: true);
}
// listSync returns all files and subdirectories
for (final entity in downloadsDir.listSync()) {
// Check if it's actually a File (not a nested Directory)
if (entity is File) {
final fileName = entity.uri.pathSegments.last;
final parts = fileName.split('.');
final ext = parts.length > 1 ? parts.last.toLowerCase() : '';
final folderName = switch (ext) {
'pdf' || 'docx' || 'txt' => 'Documents',
'jpg' || 'png' || 'mp4' => 'Media',
'zip' || 'tar' || 'gz' => 'Compressed',
_ => 'Others',
};
final targetDir = Directory('${downloadsDir.path}/$folderName');
if (!targetDir.existsSync()) {
targetDir.createSync();
}
final newPath = '${targetDir.path}/$fileName';
entity.renameSync(newPath);
print('Moved $fileName to ${targetDir.path}');
}
}
}
8. The Dart Superpowers: Isolates & Sound Null Safety
Python struggles with the GIL. JavaScript is strictly single-threaded, forcing you to fake concurrency with async/await or heavy Web Workers. Dart fixes these structural issues cleanly.
1. Sound Null Safety
In Dart, "Soundness" means the compiler guarantees that your program will never encounter a null reference exception at runtime. If code compiles, nulls are safe.
void printLength(String? text) {
// ❌ print(text.length); // COMPILE ERROR
if (text != null) {
// ✅ Compiler promotes `text` to non-null String here!
print(text.length);
}
// Or use the safe navigation operator:
print(text?.length ?? 0);
}
2. Isolates (True Concurrency)
Dart runs on an Event Loop (like JS), but it also supports Isolates. An Isolate is a separate execution thread with its own memory heap. Because they share no memory, there are no locks or data races.
import 'dart:isolate';
// This function runs entirely in parallel on a separate CPU core
int heavyComputation(int target) {
return target * 2; // Imagine 5 seconds of work here
}
void main() async {
print("Main UI thread remains completely responsive.");
// Isolate.run spawns a background thread, executes the work,
// passes the result back, and kills the thread safely.
final result = await Isolate.run(() => heavyComputation(5000));
print("Result: $result");
}
The Dart Concurrency Model
How Isolates communicate safely
Handles I/O, UI rendering (Flutter), and lightweight async/await tasks seamlessly.
Spin up independent memory heaps for heavy JSON parsing or math. No GIL blocking!
"Fearless Concurrency" without the complexity of manual locks.
9. Dart Specific Gotchas & Tips
If you are coming from JS or Python, Dart has a few syntax choices that make development much faster once you know them.
The Cascade Operator ..
Instead of returning `this` from every method to allow chaining, Dart provides the cascade operator. It allows you to perform a sequence of operations on the same object.
final user = User()
..name = "Alice"
..age = 30
..save();
Named and Required Parameters
You can wrap function parameters in `{ }` to make them named. If you want them to be mandatory, use the required keyword.
void createBtn({required String label, String? color}) { ... }
createBtn(label: "Submit", color: "Blue");
const
final vs const
Both mean "cannot be reassigned".
final: Evaluated at runtime. (e.g., final time = DateTime.now();).
const: Evaluated at compile-time. If you use const for UI widgets in Flutter, Dart entirely skips rebuilding them, leading to massive performance gains!
10. Standard Library & Ecosystem Essentials
Dart's standard library is batteries-included. You rarely need external packages for basic networking or JSON handling.
Dart includes JSON decoding out of the box. For complex production apps, developers typically use json_serializable to generate safe typed mappings, but basic dynamic parsing is trivial.
import 'dart:convert';
void main() {
final jsonString = '{"name": "Alice", "score": 42}';
// Decodes to Map
final Map data = jsonDecode(jsonString);
print("User ${data['name']} scored ${data['score']}");
// Encode back to string
final outJson = jsonEncode({"status": "success"});
}
Add http: ^1.2.0 to your `pubspec.yaml`. Dart handles async/await seamlessly just like Javascript.
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() async {
final url = Uri.parse('https://api.github.com/repos/dart-lang/sdk');
final response = await http.get(url);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
print('Stars: ${data['stargazers_count']}');
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
A Future is a single async value. A Stream is a sequence of async values over time (like WebSockets or UI events). Dart has first-class language support for them with await for.
Stream generateNumbers() async* {
for (int i = 1; i <= 3; i++) {
await Future.delayed(Duration(seconds: 1));
yield i; // Emit value to the stream
}
}
void main() async {
// Wait for and consume values as they arrive
await for (final num in generateNumbers()) {
print("Received: $num");
}
print("Stream closed.");
}
11. Software Engineering Best Practices (Tooling)
The Dart SDK includes everything you need. You don't need a separate package manager (like pip/npm), formatter (like Black/Prettier), or linter (like ESLint). It's all baked into the dart command.
dart pub
The package manager. Use dart pub add <package> to install dependencies. Packages are hosted at pub.dev.
dart analyze
The Dart analyzer powers the world-class VSCode and IntelliJ plugins, providing immediate squiggly lines for errors and stylistic recommendations. It enforces sound null safety natively.
dart format
An uncompromising code formatter. Just run it. It ends all debates about indentation and spacing forever.
dart compile exe
While you use JIT compilation during development for instant hot-reload, this command AOT (Ahead-Of-Time) compiles your script into a blazing fast, standalone native machine code binary.