Sparround

Live coding and code review scenarios

In the live coding round the goal is not working code but thinking out loud. A structure that works:

1. Clarify the requirement (30 seconds): "Should this screen work offline? Should previous data stay visible while loading? Is there pagination?" 2. Write the model first: the state class and its possible states. That signals you know how to model state. 3. The UI second: map state to UI with switch/pattern matching. 4. Don't forget error and empty states — most candidates write only the happy path; it is the fastest visible difference. 5. At least verbalise the test: "My first test for this notifier would be the empty-list case." 6. Say the trade-off out loud: "I chose an optimistic update here because the action is reversible."

dart
// 1) Model: mümkün olmayan vəziyyətlər ifadə edilə bilməz.
// AsyncValue loading/error/data-nı özü daşıyır, ona görə əlavə model lazım deyil.

// 2) State qatı.
class Articles extends AsyncNotifier<List<Article>> {
  @override
  Future<List<Article>> build() {
    return ref.watch(articleRepositoryProvider).fetchAll();
  }

  Future<void> refresh() async {
    state = const AsyncLoading<List<Article>>().copyWithPrevious(state);
    state = await AsyncValue.guard(
      () => ref.read(articleRepositoryProvider).fetchAll(),
    );
  }
}

final articlesProvider =
    AsyncNotifierProvider<Articles, List<Article>>(Articles.new);

// 3) UI: dörd hal — data, boş, xəta, ilk yükləmə.
class ArticlesPage extends ConsumerWidget {
  const ArticlesPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final articles = ref.watch(articlesProvider);

    return Scaffold(
      body: RefreshIndicator(
        onRefresh: () => ref.refresh(articlesProvider.future),
        child: switch (articles) {
          AsyncValue(:final value?) when value.isEmpty =>
            const EmptyView(message: 'Hələ məqalə yoxdur'),
          AsyncValue(:final value?) => ArticleListView(items: value),
          AsyncValue(:final error?) => ErrorView(
              message: '$error',
              onRetry: () => ref.invalidate(articlesProvider),
            ),
          _ => const ArticleSkeleton(),
        },
      ),
    );
  }
}

A typical task: a list with loading, error and retry. The Riverpod version.

dart
enum ArticlesStatus { initial, loading, success, failure }

final class ArticlesState extends Equatable {
  const ArticlesState({
    this.status = ArticlesStatus.initial,
    this.items = const [],
    this.error,
  });

  final ArticlesStatus status;
  final List<Article> items;
  final String? error;

  ArticlesState copyWith({
    ArticlesStatus? status,
    List<Article>? items,
    String? error,
  }) => ArticlesState(
        status: status ?? this.status,
        items: items ?? this.items,
        error: error,
      );

  @override
  List<Object?> get props => [status, items, error];
}

sealed class ArticlesEvent {}
final class ArticlesStarted extends ArticlesEvent {}
final class ArticlesRefreshRequested extends ArticlesEvent {}

class ArticlesBloc extends Bloc<ArticlesEvent, ArticlesState> {
  ArticlesBloc(this._repository) : super(const ArticlesState()) {
    on<ArticlesStarted>(_onLoad);
    // Təkrar sorğuların qarşısını alır.
    on<ArticlesRefreshRequested>(_onLoad, transformer: droppable());
  }

  final ArticleRepository _repository;

  Future<void> _onLoad(ArticlesEvent event, Emitter<ArticlesState> emit) async {
    emit(state.copyWith(status: ArticlesStatus.loading, error: null));
    try {
      final items = await _repository.fetchAll();
      emit(state.copyWith(status: ArticlesStatus.success, items: items));
    } catch (error) {
      // Köhnə items saxlanılır: ekran boşalmır.
      emit(state.copyWith(status: ArticlesStatus.failure, error: '$error'));
    }
  }
}

The same task in BLoC — with a status enum.

Code review red flagWhy it is a problem
`Navigator`, `showDialog` or `showSnackBar` inside `build`When the rebuild repeats, so does the effect
Reading a value in `build` with `read`/`context.read<T>().state`No subscription — the UI keeps a stale value (the docs call it error prone)
Mutating a state collection in place (`items.add(...)`)`==` sees no difference: bloc swallows the change and `select` never fires
A new field missing from `Equatable`'s `props`Two different states compare equal — a silent bug
Creating a notifier/provider inside `build`A new object per rebuild: state is lost and the old one leaks
Using `context` after an `await` without a `mounted` checkA crash on a disposed widget; the lint rule catches it
A global provider for ephemeral state (a form, scroll)Wide rebuilds and state leaking across routes; the Riverpod docs warn against it
No transformer specified in a critical flowTwo taps → two orders/payments

The most commonly lost points in live coding come from not writing the error and empty states. The difference between a candidate who writes only the happy path and one who covers four cases (first load, empty, error, refresh) is judged less as code quality than as product thinking. One sentence is enough: "We also need an empty list and an error screen — I'm adding those."

📚 Sources and documentation