Отображение AlertDialog без Context в Flutter
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

Проблема: Отображение AlertDialog без контекста
Проблема: Отображение AlertDialog без контекста
В Flutter функция showDialog требует параметра BuildContext. Однако, если мы хотим показать AlertDialog из асинхронной функции (например, при ошибке HTTP-запроса внутри getNews()), у нас может не быть прямого доступа к context.
Распространённая ошибка: Попытка показать диалог без контекста
Распространённая ошибка среди новичков — попытка показать диалог непосредственно внутри асинхронной функции без передачи корректного context. Например:
void getNews() async {
try {
throw Exception("Failed to load news");
} catch (e) {
print("Error: \$e");
showDialog(
context: context, // ❌ Ошибка: контекст недоступен здесь
builder: (context) => AlertDialog(
title: Text("Error"),
content: Text("Something went wrong!"),
),
);
}
}Этот код не будет работать, потому что context недоступен в независимой функции.
Решение: Использование GlobalKey<NavigatorState>
Чтобы решить эту проблему, можно использовать глобальный navigatorKey. Это позволяет получить доступ к BuildContext глобально и показать диалог из любой части приложения, даже внутри асинхронной функции.
Пошаговое руководство
Шаг 1: Определение глобального ключа навигатора
Глобальный ключ навигатора позволяет управлять навигацией без необходимости в context. Объявите его в самом верху файла main.dart, вне любых классов:
import 'package:flutter/material.dart';
// ✅ Правильно: Объявите глобальный ключ навигатора вверху
final navigatorKey = GlobalKey<NavigatorState>();
Шаг 2: Назначение MaterialApp
При инициализации MaterialApp назначьте navigatorKey ему:
void main() => runApp(
MaterialApp(
home: HomePage(),
navigatorKey: navigatorKey, // ✅ Назначьте глобальный ключ навигатора
),
);
Шаг 3: Создание функции для показа AlertDialog
Теперь создайте функцию, которую можно вызывать из любой части приложения для отображения AlertDialog:
void showMyDialog() {
showDialog(
context: navigatorKey.currentContext!, // ✅ Использование глобального контекста
builder: (context) => AlertDialog(
title: Text("Error"),
content: Text("Something went wrong!"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("OK"),
),
],
),
);
}Шаг 4: Триггер диалога при ошибке HTTP
В вашей асинхронной функции вызовите showMyDialog() при возникновении ошибки:
Future<void> getNews() async {
try {
// Имитация HTTP-запроса
throw Exception("Failed to load news");
} catch (e) {
print("Error: \$e"); // ✅ Запись ошибки
showMyDialog(); // ✅ Показать AlertDialog
}
}Шаг 5: Полный рабочий пример кода
Вот полный пример, чтобы новички могли увидеть, как всё собирается вместе:
import 'package:flutter/material.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(MaterialApp(
home: HomePage(),
navigatorKey: navigatorKey,
));
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Home")),
body: Center(
child: ElevatedButton(
onPressed: getNews,
child: Text("Fetch News"),
),
),
);
}
}
void showMyDialog() {
showDialog(
context: navigatorKey.currentContext!,
builder: (context) => AlertDialog(
title: Text("Error"),
content: Text("Something went wrong!"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("OK"),
),
],
),
);
}
Future<void> getNews() async {
try {
throw Exception("Failed to load news");
} catch (e) {
print("Error: \$e");
showMyDialog();
}
}
Почему это работает
- ❌ Неправильный подход: Использование
contextвнутри асинхронной функции. - ✅ Правильный подход: Использование
navigatorKeyдля получения глобальногоcontext. - Функция
showMyDialog()может быть вызвана из любого места, включая асинхронные функции. - Этот подход помогает избежать необходимости явного передачи
context, делая код чище.