Dart is the language behind Flutter. These are the core concepts you need before building widgets.
Dart is statically typed. You can write the type yourself (String name = "Daniel") or let Dart infer it with var. Common types are int, double, String, bool, List, and Map.
How you declare a variable controls whether it can change.
String name = "Daniel"; // explicit typevar count = 0; // inferred as int, can be reassignedfinal createdAt = DateTime.now(); // set once at runtimeconst pi = 3.14159; // compile-time constantlate String token; // assigned later, before first usetoken = "abc123";
Rules:
var → mutable, type is inferredfinal → assigned once, value can be computed at runtimeconst → compile-time constantlate → delayed initialization (useful for non-nullable fields)By default, variables cannot be null. Add ? to allow it.
String title = "Hello";String? subtitle; // can be nullprint(subtitle ?? "No subtitle");print(subtitle?.length);subtitle ??= "Default";
Important operators:
? → nullable type?? → fallback if null?. → call only if not null??= → assign only if currently null! → assert the value is not nullString greet(String name) {return "Hello $name";}int add(int a, int b) => a + b;void log(String message, {String level = "info"}) {print("[$level] $message");}log("App started");log("Crash", level: "error");
Mark a function async to use await. It returns a Future.
Future<String> fetchUser() async {await Future.delayed(Duration(seconds: 1));return "Daniel";}void main() async {final name = await fetchUser();print("Hello $name");}
Function styles:
greet("Daniel"){String level = "info"}{required String name}=> for one-line returnsasync / await → wait for a Future without blockingfinal fruits = ["apple", "banana", "orange"];print(fruits[0]);fruits.add("mango");
final tags = {"dart", "flutter", "dart"};print(tags.length); // 2 — duplicates are removed
final user = {"name": "Daniel","age": 25,};print(user["name"]);user["city"] = "Hanoi";
These methods work on List, Set, and other iterables. map and where return an Iterable — call .toList() when you need a list.
final numbers = [1, 2, 3, 4, 5];final fruits = ["apple", "banana", "orange"];
Transforms each item into something new.
final doubled = numbers.map((n) => n * 2).toList();// [2, 4, 6, 8, 10]
Keeps only items that match a condition.
final evens = numbers.where((n) => n.isEven).toList();// [2, 4]
Returns the first item that matches. Use orElse if nothing matches.
final firstEven = numbers.firstWhere((n) => n.isEven);// 2final big = numbers.firstWhere((n) => n > 10,orElse: () => -1,);
Returns true if at least one item matches.
numbers.any((n) => n > 4); // true
Returns true if every item matches.
numbers.every((n) => n > 0); // true
Checks whether a value exists in the collection.
fruits.contains("banana"); // true
Runs a function on each item.
fruits.forEach((fruit) => print(fruit));
class Dog {final String name;Dog(this.name);void bark() {print("Woof!");}}final dog = Dog("Buddy");dog.bark();
Useful class features:
Dog(this.name)Dog.stray() : name = "Unknown"this refers to the current instanceclass User {final String name;final int age;User({required this.name, required this.age});factory User.guest() {return User(name: "Guest", age: 0);}String get label => "$name ($age)";}