askesis

Flutter essentials: 50 cards on Dart, widgets, state and layout

Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.

ProgrammingBeginner50 cardsFree

Practice now

Study without an account

0 of 50 seen0 graduated
String vs String? in Dart

Scheduled with the default Askesis timings. Progress is saved in this browser.

All 50 cards

  1. 1
    String vs String? in Dart
    With sound null safety (required since Dart 3), a String can never be null; a String? can, and the compiler makes you handle that case before using it
  2. 2
    Dart’s null-aware operators
    dart
    final name = user?.name ?? 'Guest';
    cache ??= {};
    final len = maybeText!.length; // throws if null
    ?. short-circuits to null, ?? supplies a default, ??= assigns only if null, and ! asserts non-null.
  3. 3
    What does late do?
    Declares a non-nullable variable that is initialized after its declaration; reading it before it is assigned throws a LateInitializationError
    late final config = loadConfig(); also makes the initializer lazy: it runs on first access.
  4. 4
    final vs const
    final is set once, at runtime; const is a compile-time constant that is deeply immutable and canonicalized, so identical const values are the same instance
    dart
    final now = DateTime.now();    // fine
    const appName = 'Askesis';      // fine
    const later = DateTime.now();  // compile error
  5. 5
    var vs dynamic vs Object?
    var infers a fixed static type from the initializer; dynamic turns off static checking; Object? accepts anything but still requires a check or cast before you use it
  6. 6
    What happens here?
    dart
    var x = 'hi';
    x = 5;
    A compile error: var inferred x as a String, so it can’t be assigned an int
  7. 7
    String interpolation in Dart
    dart
    print('Hello, $name! You have ${items.length} items.');
    Use ${…} for anything beyond a simple identifier.
  8. 8
    Build a list with collection if, for and spread
    dart
    final menu = [
      ...baseItems,
      if (isAdmin) 'Admin panel',
      for (final id in recentIds) 'Item $id',
    ];
  9. 9
    What does the cascade operator .. do?
    Performs several operations on the same object and evaluates to that object
    dart
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 2;
  10. 10
    Named and required parameters
    dart
    void greet({required String name, String greeting = 'Hi'}) {
      print('$greeting, $name');
    }
    
    greet(name: 'Ada');
    Named parameters go in {}; optional positional parameters go in [].
  11. 11
    A class with a const constructor and initializing formals
    dart
    class Point {
      const Point(this.x, this.y);
    
      final int x;
      final int y;
    }
    this.x assigns the field straight from the parameter. A const constructor requires all fields to be final.
  12. 12
    Return two values from a function
    dart
    (String, int) nameAndAge() => ('Ada', 36);
    
    final (name, age) = nameAndAge();
    Records and destructuring patterns arrived in Dart 3.
  13. 13
    Switch exhaustively over a sealed class
    dart
    sealed class Shape {}
    class Square extends Shape { Square(this.side); final double side; }
    class Circle extends Shape { Circle(this.radius); final double radius; }
    
    double area(Shape s) => switch (s) {
      Square(side: var l) => l * l,
      Circle(radius: var r) => 3.14159 * r * r,
    };
    Sealed classes and switch expressions (Dart 3) let the compiler check that every subtype is handled.
  14. 14
    What is a Future?
    A value (or error) that will be available later. Mark a function async and await futures inside it as if they were synchronous
    dart
    Future<User> loadUser(int id) async {
      final res = await http.get(Uri.parse('$baseUrl/users/$id'));
      return User.fromJson(jsonDecode(res.body));
    }
  15. 15
    What does this print?
    dart
    void main() {
      print('A');
      Future(() => print('B'));
      print('C');
    }
    A, C, B: the future’s callback is queued on the event loop and runs only after the current synchronous code finishes
  16. 16
    Wait for several futures in parallel
    dart
    final results = await Future.wait([fetchUser(), fetchPosts()]);
    With records (Dart 3): final (user, posts) = await (fetchUser(), fetchPosts()).wait;
  17. 17
    Future vs Stream
    A Future delivers one value or error; a Stream delivers a sequence of them over time, consumed with await for or listen
    A single-subscription stream can be listened to only once; a broadcast stream allows many listeners.
  18. 18
    Write a function that produces a stream
    dart
    Stream<int> countdown(int from) async* {
      for (var i = from; i > 0; i--) {
        yield i;
        await Future.delayed(const Duration(seconds: 1));
      }
    }
  19. 19
    How do you keep heavy CPU work from freezing a Flutter UI?
    Isolates don’t share memory.
    Dart runs your code on a single thread per isolate, so a long computation blocks frames. Move it to another isolate
    dart
    final data = await Isolate.run(() => parseHugeJson(text));
  20. 20
    What is a widget?
    An immutable description of part of the UI. Flutter builds a tree of widgets and uses it to create and update the longer-lived elements and render objects that actually lay out and paint
  21. 21
    StatelessWidget vs StatefulWidget
    A stateless widget depends only on its constructor arguments and inherited data; a stateful widget has a separate State object that persists across rebuilds and can change over time
  22. 22
    A minimal StatelessWidget
    dart
    class Greeting extends StatelessWidget {
      const Greeting({super.key, required this.name});
    
      final String name;
    
      @override
      Widget build(BuildContext context) => Text('Hello, $name');
    }
  23. 23
    A counter with StatefulWidget
    dart
    class Counter extends StatefulWidget {
      const Counter({super.key});
    
      @override
      State<Counter> createState() => _CounterState();
    }
    
    class _CounterState extends State<Counter> {
      int _count = 0;
    
      @override
      Widget build(BuildContext context) => ElevatedButton(
            onPressed: () => setState(() => _count++),
            child: Text('Tapped $_count times'),
          );
    }
  24. 24
    What does setState do?
    Runs your callback, marks the State as dirty and schedules build to run again. Changing a field without setState updates the value but not the screen
  25. 25
    What should and shouldn’t happen in build?
    build may run very often, even every frame during an animation, so keep it fast and free of side effects: no network calls, subscriptions or heavy work
  26. 26
    Key State lifecycle methods
    initState runs once when the state is created (set up controllers and subscriptions); didUpdateWidget runs when the parent rebuilds with a new widget; dispose runs when it is removed for good (release them)
    Call super.initState() first and super.dispose() last.
  27. 27
    Why add const to widget constructors and instances?
    A const widget is created once at compile time and reused, so when its parent rebuilds, Flutter can skip rebuilding that subtree
    dart
    const Padding(padding: EdgeInsets.all(8), child: Text('Hi'))
  28. 28
    What is a BuildContext?
    A handle to a widget’s location in the tree, used to look up ancestors and inherited data: Theme.of(context), MediaQuery.sizeOf(context), Navigator.of(context)
  29. 29
    What should you check before using context after an await?
    That the widget is still in the tree: if (!context.mounted) return;
    Inside a State, check its mounted property. Using a stale context can throw or act on the wrong screen.
  30. 30
    Hot reload vs hot restart
    Hot reload injects changed code into the running app and rebuilds the widget tree, keeping state; hot restart reruns the app from main(), resetting state. Native code changes need a full rebuild
  31. 31
    Which changes doesn’t hot reload pick up?
    Code that already ran: edits to main() or initState(), and initializers of global variables and static fields. Some type changes, like turning an enum into a class, also need a hot restart
  32. 32
    What are keys for?
    They tell Flutter which widget is which when siblings are reordered, inserted or removed, so each State stays with the right item
    dart
    TodoTile(key: ValueKey(todo.id), todo: todo)
  33. 33
    What goes wrong if you reorder stateful list items without keys?
    Flutter matches widgets by type and position, so each State (a checkbox’s value, a text field’s contents) stays at its old position and ends up attached to a different item
  34. 34
    When do you need a GlobalKey?
    To reach a widget’s State from elsewhere, such as GlobalKey<FormState> to call validate() on a Form, or to keep state while moving a widget to a new parent. They are costly, so use them sparingly
  35. 35
    Flutter’s core layout rule
    “Constraints go down. Sizes go up. Parent sets position.” A parent tells each child its minimum and maximum size, the child picks a size within them, and the parent positions it
  36. 36
    Row and Column alignment
    mainAxisAlignment positions children along the main axis (horizontal in a Row, vertical in a Column); crossAxisAlignment aligns them on the other axis
  37. 37
    Make a child fill the remaining space in a Row
    dart
    Row(
      children: [
        const Icon(Icons.person),
        Expanded(child: Text(longName)),
      ],
    )
    Flexible lets the child be smaller than the space it gets; flex: shares space by ratio.
  38. 38
    What does a yellow-and-black striped bar on screen mean?
    An overflow: a child is bigger than the space its parent allows, e.g. long text in a Row. Fix it with Expanded or Flexible, by wrapping or truncating the text, or by making the area scrollable
  39. 39
    SizedBox vs Padding vs Container
    SizedBox gives a fixed size or a gap; Padding adds space around a child; Container combines padding, margin, size, color and decoration in one widget
  40. 40
    Overlay one widget on another
    dart
    Stack(
      children: [
        Image.asset('photo.jpg'),
        const Positioned(top: 8, right: 8, child: Icon(Icons.star)),
      ],
    )
  41. 41
    Show a long list efficiently
    dart
    ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, i) => ListTile(title: Text(items[i])),
    )
    The builder creates only the items that are on screen, instead of all of them up front.
  42. 42
    Why does a ListView inside a Column throw an error?
    The Column gives it unbounded height, but a ListView tries to fill all available space. Wrap it in Expanded so it gets the remaining space
    The error mentions “Vertical viewport was given unbounded height”.
  43. 43
    Adapt a layout to the space available
    dart
    LayoutBuilder(
      builder: (context, constraints) => constraints.maxWidth > 600
          ? const WideLayout()
          : const NarrowLayout(),
    )
    MediaQuery.sizeOf(context) gives the whole screen size instead of the parent’s constraints.
  44. 44
    What does a Flutter app’s entry point look like?
    dart
    void main() => runApp(const MyApp());
    MyApp usually returns a MaterialApp (theme, routes, localization), and each screen is a Scaffold with an appBar and a body.
  45. 45
    Open a new screen and get a result back
    dart
    final color = await Navigator.push<String>(
      context,
      MaterialPageRoute(builder: (_) => const PickerPage()),
    );
    
    // inside PickerPage:
    Navigator.pop(context, 'blue');
    Packages like go_router add URL-based, declarative routing on top.
  46. 46
    How should state be shared between widgets?
    Keep it local with setState when one widget needs it; lift it up to a common ancestor when several do; for app-wide state use InheritedWidget or a package built on the same idea, like provider, Riverpod or Bloc
  47. 47
    Rebuild part of the UI when a model changes, without packages
    dart
    class Cart extends ChangeNotifier {
      final items = <String>[];
    
      void add(String item) {
        items.add(item);
        notifyListeners();
      }
    }
    
    ListenableBuilder(
      listenable: cart,
      builder: (context, _) => Text('${cart.items.length} items'),
    )
  48. 48
    The FutureBuilder gotcha
    Creating the future inside build (future: fetchData()) starts a new request on every rebuild. Create it once, e.g. in initState, store it in a field, and pass that
  49. 49
    Add a package to a Flutter project
    bash
    flutter pub add http
    This adds it under dependencies in pubspec.yaml and fetches it; flutter pub get installs everything listed there.
  50. 50
    Create, check and run a Flutter project
    bash
    flutter create my_app
    flutter doctor
    flutter run
    flutter doctor reports missing SDKs, devices or tooling setup.

Frequently asked questions

Do I need to know Dart before Flutter?

No. The first part of the deck covers the Dart that Flutter code uses daily: null safety, async/await, streams, collection literals, records and sealed classes.

Which Flutter and Dart versions does it use?

Current Flutter 3 with Dart 3. Dart 3 features such as records, patterns and sealed classes are marked.

Which state management approach should I learn first?

Start with setState, lifting state up and ChangeNotifier, which this deck covers. Packages such as provider, Riverpod and Bloc build on the same ideas.

Intermediate

JavaScript

Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.

50 cards
Intermediate

Advanced JS

Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.

50 cards
Intermediate

TypeScript

Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.

50 cards
Beginner

Python

Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.

50 cards
Beginner

Git & terminal

The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.

50 cards
Beginner

Java

Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.

50 cards
Beginner

C#

C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.

50 cards
Beginner

Kotlin

Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.

50 cards
Beginner

Swift

Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.

50 cards
Beginner

PHP

Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.

50 cards
Beginner

Ruby

Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.

50 cards
Beginner

Go

Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.

50 cards
Intermediate

Rust

Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.

50 cards
Intermediate

C++

Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.

50 cards
Intermediate

Haskell

Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.

50 cards
Intermediate

Elixir

Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.

50 cards
Intermediate

x86-64 assembly

x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.

50 cards
Beginner

Regex

Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.

50 cards