Live Coding Round in Dart
Irbisa · cheatsheetSeptember 13, 2026

Live Coding Round in Dart

Debounce, Result types, LRU cache, backoff, concurrency limits, tries, stream transformers

Middle Developer16 itemscompressed for a skim
  1. 01

    A search field fires a request per keystroke and you cannot add rxdart — how do you write debounce and throttle, and which one loses the last keystroke?

    Easy

    Debounce waits for the burst to end and runs only the last call; a leading-edge throttle runs the first call and silently drops everything after it, including the last one.

    import 'dart:async';
    
    class Debouncer {
      Debouncer(this.delay);
      final Duration delay;
      Timer? _timer;
    …
  2. 02

    The API returns a flat list of transactions and the design wants them under date headers — how do you group them, and what makes the DateTime key betray you?

    Easy

    Fold the flat list into a Map<DateTime, List<Txn>> keyed by the date with the time stripped, then sort the keys — the grouping is one pass and about five lines.

    import 'package:collection/collection.dart';   // groupBy
    
    typedef Section = ({DateTime day, List<Txn> items});
    
    DateTime _dayOf(DateTime t) {
      final local = t.toLocal();                    // the server sends UTC
    …
  3. 03

    Every caller of your repository wraps it in try/catch and half of them forget — how would you write a Result<T, E> with map, mapError and fold instead?

    Medium

    A sealed class Result<T, E> with Ok and Err subtypes moves failure into the return type, so the compiler — not code review — is what forces every caller to deal with it.

    sealed class Result<T, E> {
      const Result();
    
      R fold<R>(R Function(T value) onOk, R Function(E error) onErr) => switch (this) {
            Ok(:final value) => onOk(value),
            Err(:final error) => onErr(error),
    …
  4. 04

    Your cache holds messages sorted by time and the server returns another sorted page — how do you merge them and drop duplicate ids in one pass?

    Medium

    Walk both lists with two indices, always taking the smaller head, and keep a Set<String> of ids already emitted: O(n + m) time, one pass, no second sort.

    List<Message> mergeSorted(List<Message> cached, List<Message> fresh) {
      final out = <Message>[];
      final seen = <String>{};
      var i = 0, j = 0;
    
      void take(Message m) {
    …
  5. 05

    A comment thread arrives as a nested tree and ListView.builder needs a flat list with indentation — how do you flatten it without recursion?

    Medium

    Pre-order DFS with an explicit stack: push the roots reversed, then pop a node, emit (comment, depth), and push its children reversed so they come back out in reading order.

    import 'dart:math';
    import 'package:collection/collection.dart';
    
    class Comment {
      Comment(this.id, this.text, {this.children = const []});
      final String id;
    …
  6. 06

    A pricing call takes 40 ms and the same two arguments come back on every rebuild — how do you memoize it, and what makes a two-argument cache key actually hit?

    Medium

    Wrap the function in a closure over a Map and use a Dart record (a, b) as the key — a List key never hits, because List equality is identity.

    R Function(A) memo1<A, R>(R Function(A) f) {
      final cache = <A, R>{};
      return (a) => cache.putIfAbsent(a, () => f(a));   // one call per missing key
    }
    
    R Function(A, B) memo2<A, B, R>(R Function(A, B) f) {
    …
  7. 07

    Your image-metadata cache grows until the OS kills the app — how do you write an LRU bounded at 100 entries, and what do get, put and eviction cost?

    Medium

    A Dart Map is a LinkedHashMap that preserves insertion order, so an LRU is that map plus two moves: on a hit remove the key and re-insert it, and on an insert past capacity remove keys.first.

    class LruCache<K, V> {
      LruCache(this.capacity) : assert(capacity > 0);
    
      final int capacity;
      final _entries = <K, V>{};        // LinkedHashMap: insertion order == recency order
    …
  8. 08

    Three widgets ask for the same profile in the same frame and you fire three identical requests — how do you collapse that into one call?

    Medium

    Keep a Map<String, Future<T>> of the requests currently in flight and hand every later caller the same Future — a Dart Future is a value you can await any number of times, and the work behind it runs once.

    class RequestCoalescer {
      final _inFlight = <String, Future<dynamic>>{};
    
      Future<T> run<T>(String key, Future<T> Function() start) {
        final running = _inFlight[key];
        if (running != null) return running.then((value) => value as T);
    …
  9. 09

    You need to fetch 50 product details with at most four requests in flight and the results in the original order — how do you write that scheduler?

    Medium

    Order is the free half — Future.wait returns results in argument order no matter who finishes first; the work is bounding how many run at once, and that needs a pool fed with functions, not with futures that have already started.

    import 'package:collection/collection.dart';
    import 'package:pool/pool.dart';
    
    /// Runs [task] over [items] with at most [limit] in flight, results in order.
    Future<List<R>> mapPooled<T, R>(
      List<T> items,
    …
  10. 10

    Write a typed event bus with subscribe and unsubscribe — what keeps a listener alive after the screen that registered it is gone?

    Medium

    Type the bus by event class and hand the caller back a StreamSubscription it has to cancel — a bus keyed by strings that stores void Function(Object) callbacks has no way of knowing a screen is gone.

    import 'dart:async';
    
    class EventBus {
      final _ctrl = StreamController<Object>.broadcast();
    
      void emit(Object event) {
    …
  11. 11

    Write a paging controller — cursor, in-flight guard, end-of-list flag, retry. What happens if the user pulls to refresh while page three is still loading?

    Hard

    The four fields are the easy part; the bug is that page three's response can land after the refresh has already reset the list, so the controller needs a generation counter that turns every in-flight response into a no-op.

    import 'package:flutter/foundation.dart';
    
    class Page<T> {
      Page(this.items, this.nextCursor);
      final List<T> items;
      final String? nextCursor; // null means: that was the last page
    …
  12. 12

    Implement "at most five uploads per rolling minute" — and how do you test it without a suite that sleeps for a minute?

    Hard

    Keep a queue of the timestamps you have already granted: a call is admitted immediately while fewer than N of them fall inside the window, otherwise it is scheduled for the moment the oldest one ages out.

    import 'dart:async';
    import 'dart:collection';
    
    import 'package:clock/clock.dart';
    import 'package:fake_async/fake_async.dart';
    import 'package:test/test.dart';
    …
  13. 13

    How would you write a custom Iterable — a sliding window over a list — and why is sync* usually the better answer?

    Hard

    An Iterable is a factory for Iterators: you implement get iterator, and the whole contract is that every call returns a fresh iterator positioned before the first element.

    import 'dart:collection';
    
    // ── Hand-rolled: the state machine written out ──
    class Windows<E> extends IterableBase<List<E>> {
      Windows(this._source, this.size);
      final List<E> _source;
    …
  14. 14

    A GPS stream fires ten fixes a second — how do you write a transformer that emits at most one every five seconds and drops anything within ten metres of the last one kept?

    Hard

    Build it as a StreamTransformer.fromBind over an async* function, so the filter state — the last fix you kept — is created fresh on every bind, and gate on the fix's own timestamp rather than on the device clock.

    import 'dart:async';
    import 'dart:math' as math;
    
    class Fix {
      const Fix(this.lat, this.lon, this.at);
      final double lat;
    …
  15. 15

    A test comparing two decoded JSON maps fails with 400 lines of output — how do you write a deep compare that reports the first differing path instead of a bool?

    Hard

    Walk both structures together carrying a path, and return the first mismatch as a string like $.user.tags[1] instead of a bool — a plain == on two maps compares identity, which is the only reason this function has to exist at all.

    import 'package:collection/collection.dart';
    import 'package:test/test.dart';
    
    /// Returns null when the two structures match, else the first differing path.
    String? firstDiff(Object? expected, Object? actual, [String path = r'$']) {
      if (expected is Map && actual is Map) {
    …
  16. 16

    How would you build offline autocomplete over 50 000 product names with a trie, and how do you justify it against a plain startsWith scan?

    Hard

    A trie makes the prefix lookup O(len(prefix)) plus the cost of collecting matches, but for 50 000 short names a sorted list with two binary searches beats it on memory and on build time — the trie earns its place when you need payloads in the nodes, ranked completions or fuzzy walks.

    import 'package:collection/collection.dart';
    
    class TrieNode {
      final Map<int, TrieNode> children = {}; // keyed by code unit
      final List<int> ids = []; // product ids whose name ends here
    }
    …