Ответ
Да, в Dart можно объявить локальную final переменную без немедленной инициализации, но с важным условием: значение должно быть присвоено ровно один раз до первого использования. Компилятор проводит статический анализ потока управления (flow analysis), чтобы это гарантировать.
Корректные примеры:
// 1. Присваивание в условии
void processUser(bool isPremium) {
final String accessLevel;
if (isPremium) {
accessLevel = 'premium';
} else {
accessLevel = 'basic';
}
print('Access: $accessLevel'); // OK
}
// 2. Присваивание в try-catch
void loadConfig() {
final String config;
try {
config = _readConfigFile();
} catch (e) {
config = 'default';
}
print('Config: $config'); // OK
}
// 3. Позднее присваивание в цикле (сложный случай)
void findFirstEven(List<int> numbers) {
final int firstEven;
bool found = false;
for (final number in numbers) {
if (number % 2 == 0) {
firstEven = number;
found = true;
break;
}
}
if (!found) {
firstEven = -1; // Обязательно присвоить в ветке else
}
print('First even: $firstEven'); // OK
}
Ошибки, которые вызовет компилятор:
// ОШИБКА: использование до присваивания
void errorExample1() {
final int x;
print(x); // Error: Non-nullable variable 'x' must be assigned
x = 10;
}
// ОШИБКА: не все пути выполнения присваивают значение
void errorExample2(bool condition) {
final String message;
if (condition) {
message = 'true';
}
// Нет else-ветки - ошибка если condition == false
print(message); // Error
}
// ОШИБКА: повторное присваивание
void errorExample3() {
final int value = 10;
value = 20; // Error: 'value' can only be set once
}
Отличия от полей класса:
class User {
// Для полей класса final требует немедленной инициализации:
final String name = 'John'; // OK
final int id; // Error если не инициализировано
// Или инициализация в конструкторе:
User(this.id); // OK
// Для late final можно отложить инициализацию:
late final String token;
void setToken(String t) {
token = t; // Можно присвоить один раз
}
}
Практическое применение в Flutter:
Widget buildUserCard(User user) {
// Позднее связывание final переменных полезно для вычислений
final Color cardColor;
final IconData icon;
if (user.isAdmin) {
cardColor = Colors.red[100]!;
icon = Icons.admin_panel_settings;
} else if (user.isPremium) {
cardColor = Colors.blue[100]!;
icon = Icons.star;
} else {
cardColor = Colors.grey[100]!;
icon = Icons.person;
}
return Card(
color: cardColor,
child: ListTile(
leading: Icon(icon),
title: Text(user.name),
),
);
}
Это мощная особенность Dart, которая повышает безопасность кода, гарантируя, что final переменные будут инициализированы перед использованием, но давая гибкость в выборе момента инициализации.