Расскажите о случае, когда особенности Dart повлияли на архитектурное решение, производительность или сопровождение проекта.
Null safety в Dart 2.12 потребовал полного аудита моделей данных и изменил архитектуру парсинга API-ответов: вместо nullable-полей везде появились factory-конструкторы с явной валидацией на границе сети.
Как null safety изменил архитектуру
В одном Flutter-проекте (агрегатор новостей) мы мигрировали на Dart null safety (2.12) через полгода после релиза. До миграции модели данных выглядели просто: все поля String или int, а null молча проглатывался логикой. После включения анализатора выяснилось: 40% полей API реально могут приходить как null, и UI просто рендерил пустые строки вместо падений.
Решение: граница на уровне fromJson
Мы ввели правило: всё, что приходит из сети, разбирается через factory-конструктор с явными проверками. Nullable поля DTO преобразуются в non-nullable доменные объекты через fallback-значения или Result-тип.
// DTO — отражает реальный JSON, включая nullable
class ArticleDto {
final String id;
final String? imageUrl; // API может не вернуть
final String? authorName;
final int viewCount;
const ArticleDto({
required this.id,
this.imageUrl,
this.authorName,
required this.viewCount,
});
factory ArticleDto.fromJson(Map<String, dynamic> json) {
return ArticleDto(
id: json['id'] as String,
imageUrl: json['image_url'] as String?,
authorName: json['author']?['name'] as String?,
viewCount: (json['views'] as num?)?.toInt() ?? 0,
);
}
}
// Domain model — non-nullable, UI не знает о null
class Article {
final String id;
final Uri? imageUrl; // Uri? — семантически nullable
final String author;
final int viewCount;
const Article({
required this.id,
this.imageUrl,
required this.author,
required this.viewCount,
});
factory Article.fromDto(ArticleDto dto) {
return Article(
id: dto.id,
imageUrl: dto.imageUrl != null
? Uri.tryParse(dto.imageUrl!)
: null,
author: dto.authorName ?? 'Unknown',
viewCount: dto.viewCount,
);
}
}
Влияние на производительность
После миграции мы запустили flutter analyze в CI как блокирующий шаг. Это выявило места с неоптимальным использованием late: переменные, объявленные late final, инициализировались в initState() дважды из-за горячей перезагрузки в debug-режиме. Дополнительно Dart DevTools → Performance показал: устранение лишних проверок != null внутри build-методов сократило количество перестроек виджетов на ~15%.
Сопровождение
Команда добавила кодогенерацию через json_serializable + freezed для всех DTO, чтобы не писать fromJson вручную:
# pubspec.yaml
dependencies:
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.6
freezed: ^2.4.5
json_serializable: ^6.7.1
Подводные камни
lateбез инициализации бросаетLateInitializationErrorв runtime — хуже, чем явный nullable, потому что статический анализ молчит.!-оператор (null assertion) снимает ответственность с компилятора: один лишний!— и снова NullPointerException, только уже осознанный.- Смешанный граф зависимостей: legacy-пакеты без null safety запускаются в unsound-режиме, что отключает часть гарантий анализатора для всего приложения.
json_serializableгенерируетfromJsonс жёсткими cast'ами — нестандартные типы в JSON (например,intвместоStringдля id) вызываютTypeErrorв runtime.freezed-классы сcopyWithне позволяют явно выставить поле вnullесли тип non-nullable — нужен паттернOptional.- Hot reload в Flutter сбрасывает
late finalтолько в debug — в release поведение другое, что приводит к трудновоспроизводимым багам.
What hurts your answer
- Выдумывать опыт или говорить слишком общими фразами
- Не объяснять свою личную роль в работе с Dart
- Не показывать результат, метрики или извлечённые уроки
What they're listening for
- Может подготовить честный пример использования Dart
- Показывает свою роль, решения и результат
- Умеет рефлексировать над trade-offs и уроками