Dart Basics
Variables, types, functions, null safety, and core Dart syntax
- 01
What is null safety in Dart and what are nullable vs non-nullable types?
EasyNull safety means a variable cannot hold null unless its type is marked nullable with a trailing question mark.
// Non-nullable — must be initialized String name = 'Alice'; // Nullable String? email; print(email?.length); // null (safe access) … - 02
What are the main data types in Dart?
EasyDart's core types cover numbers, text, booleans, three collections and a few special types like
Never.int age = 30; double salary = 75000.50; num anything = 3; // can be int or double String name = 'Flutter'; String interpolated = 'Hello, $name! Age: ${age + 1}'; … - 03
Explain the difference between final and const in Dart.
EasyBoth
finalandconstblock reassignment; the difference is WHEN the value is fixed —finalat runtime,constat compile time.// final — runtime value OK final name = 'Alice'; final now = DateTime.now(); // ✅ runtime value // const — compile-time only const pi = 3.14159; … - 04
What are arrow functions, anonymous functions, and closures in Dart?
MediumFunctions in Dart are first-class objects — you can store them in variables, pass them as arguments and return them.
// Named function int add(int a, int b) => a + b; // Arrow — single expression only double circle(double r) => 3.14159 * r * r; … - 05
Explain Dart collections: List, Map, Set and their common operations.
MediumDart ships three core collections: List for ordered items, Map for key-value pairs, and Set for unique values.
// ── LIST ────────────────────────────────────────── var fruits = <String>['apple', 'banana', 'cherry']; fruits.add('date'); fruits.remove('banana'); fruits.sort(); … - 06
What are Dart enums and how do enhanced enums work?
MediumDart has plain enums, which are a named set of constants, and enhanced enums, which add fields, methods and constructors.
// Simple enum enum Status { pending, active, suspended, deleted } // Using simple enum Status userStatus = Status.active; print(userStatus.name); // 'active' … - 07
Explain the cascade operator (..) and spread operator (...) in Dart.
MediumThe cascade operator chains several calls on one object and returns the object itself, while spread unpacks one collection into another.
// ── Cascade operator (..) ────────────────── var paint = Paint() ..color = Colors.red ..strokeWidth = 2.0 ..style = PaintingStyle.stroke; … - 08
When would you choose a Set over a List, or a Map over either?
EasyPick by the access pattern: List when order and index matter, Set when you need uniqueness and fast membership tests, Map when you look values up by key.
// ── List ────────────────────────────────── var numbers = [1, 2, 3, 2]; // allows duplicates numbers.add(4); print(numbers[0]); // 1 print(numbers.length); // 5 … - 09
What are typedef and function types in Dart?
Mediumtypedef declares a type alias, most often a name for a function signature you use in more than one place.
// ── Function types ───────────────────────── // Function as variable int add(int a, int b) => a + b; var operation = add; print(operation(2, 3)); // 5 … - 10
How do named and optional parameters work in Dart?
EasyDart parameters are positional and required by default; optional ones are either named, in braces, or positional, in brackets.
// ── Named parameters ────────────────────── void greet({String name = 'Guest', int age = 0}) { print('Hello $name, age $age'); } greet(); // Hello Guest, age 0 … - 11
What is the difference between
var,dynamic, andObject?in Dart?EasyOf the three, only
dynamicactually switches type checking off.var name = 'Alice'; // inferred String, forever // name = 42; // ❌ compile error dynamic value = 'Alice'; value = 42; // ✅ allowed — no static type value.thisDoesNotExist(); // compiles; NoSuchMethodError at runtime … - 12
In Dart, what is the difference between an Exception and an Error, and which one should you catch?
MediumException marks a failure your code is expected to anticipate and recover from, while Error signals a bug in the program that you are supposed to fix rather than catch.
sealed class PaymentFailure implements Exception { const PaymentFailure(); } class CardDeclined extends PaymentFailure { final String code; … - 13
Your assert catches the bad value on every debug run, yet users still hit the crash in the store build — why?
EasyAsserts are compiled out of profile and release builds, so
assertis a debug-only check and never input validation.class Badge { final int count; // Checked at compile time for `const Badge(-1)`, and at runtime in debug. const Badge(this.count) : assert(count >= 0, 'count cannot be negative'); } … - 14
A helper assigns a new List to its parameter and the caller sees nothing, yet an add() inside the same helper did change the caller's list — why?
EasyDart passes every argument by value, but for an object that value is a reference — the callee gets its own copy of the handle, not a copy of the object.
void reassign(List<int> items) { items = [99]; // rebinds the LOCAL handle only } void mutate(List<int> items) { items.add(99); // reaches through the handle — caller sees it … - 15
Your code throws "Unsupported operation: Cannot add to a fixed-length list" on a list you built with List.filled — what should you have written?
EasyList.filledreturns a fixed-length list by default: elements can be replaced by index, but the length can never change.final fixed = List.filled(3, 0); fixed[0] = 7; // ✅ replacing an element is fine // fixed.add(1); // ❌ Unsupported operation: Cannot add to a fixed-length list final growable = List.filled(3, 0, growable: true); growable.add(1); // ✅ … - 16
A nickname field capped at 20 characters chops an emoji in half and renders a black diamond — what is String.length actually counting?
MediumString.lengthcounts UTF-16 code units, not the characters a person sees.import 'package:characters/characters.dart'; const name = 'Ana 👍🇰🇿'; print(name.length); // 10 — UTF-16 code units print(name.runes.length); // 7 — code points … - 17
A screen keeps rebuilding after dispose even though dispose() calls removeListener(() => _refresh()) — what is wrong with that line?
MediumEvery evaluation of
() => _refresh()creates a brand-new closure object, andremoveListenerfinds the listener by==, so it removes nothing and the notifier keeps a reference to your disposed State.class _FeedState extends State<Feed> { final controller = ScrollController(); @override void initState() { super.initState(); … - 18
A cart total renders as 19.999999999999996, and on Flutter web an order id from JSON comes back off by one digit — what do these two bugs share?
MediumBoth are IEEE-754 doubles:
doublecannot represent 0.1 exactly, and compiled to JavaScript every Dartintis a JS number, which is a double.print(0.1 + 0.2); // 0.30000000000000004 print(0.1 + 0.2 == 0.3); // false print((0.1 + 0.2).toStringAsFixed(2)); // 0.30 — display only, not a fix // ❌ accumulating money in doubles double totalBad = 0; … - 19
Users abroad see an event an hour off, and a saved DateTime is not equal to the loaded one although both point at the same instant — what does DateTime store?
MediumA
DateTimeis microseconds since the epoch plus one boolean saying whether it is UTC — it carries no timezone at all.final now = DateTime.now(); print(now.isUtc); // false — local, with the device's offset // ❌ no Z in the string → parsed as LOCAL, so the instant shifts per device final a = DateTime.parse('2026-03-29T10:00:00'); // ✅ explicit UTC → the same instant everywhere … - 20
The same endpoint parses fine for one product and throws "type 'int' is not a subtype of type 'double'" for another — where does that come from?
MediumjsonDecodegives you anintfor10and adoublefor10.0, andas doubleon an int is a failing runtime cast — the payload changed its literal, not its schema.final json = jsonDecode('{"price": 10, "tags": ["new"], "rating": 4.5}') as Map<String, dynamic>; // ❌ works while the server sends 10.0, throws the day it sends 10 // final price = json['price'] as double; …