← Назад к вопросам

Что позволяет получить доступ к глобальному key?

2.0 Middle🔥 151 комментариев
#Flutter виджеты

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Глобальный GlobalKey в Flutter

GlobalKey — один из самых мощных и одновременно опасных инструментов в Flutter. Он позволяет получить доступ к состоянию виджета из-за пределов его дерева виджетов.

Что такое GlobalKey?

GlobalKey уникально идентифицирует виджет во всём приложении и позволяет:

  • Получить State объект виджета
  • Вызвать методы из этого State
  • Получить BuildContext виджета
  • Получить RenderObject для измерений

Практические примеры

1. Получение доступа к State

class MyForm extends StatefulWidget {
  const MyForm({Key? key}) : super(key: key);

  @override
  State<MyForm> createState() => MyFormState();
}

class MyFormState extends State<MyForm> {
  final _textController = TextEditingController();

  void resetForm() {
    _textController.clear();
    setState(() {});
  }

  void submitForm() {
    print('Form submitted: ${_textController.text}');
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(controller: _textController),
      ],
    );
  }
}

class MainScreen extends StatelessWidget {
  final _formKey = GlobalKey<MyFormState>();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        MyForm(key: _formKey),
        ElevatedButton(
          onPressed: () => _formKey.currentState?.resetForm(),
          child: Text('Reset Form'),
        ),
        ElevatedButton(
          onPressed: () => _formKey.currentState?.submitForm(),
          child: Text('Submit Form'),
        ),
      ],
    );
  }
}

2. Работа с FormState

class LoginPage extends StatelessWidget {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            validator: (value) {
              if (value == null || value.isEmpty) {
                return 'Email is required';
              }
              return null;
            },
          ),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                print('Form is valid');
              }
            },
            child: Text('Login'),
          ),
        ],
      ),
    );
  }
}

3. Получение RenderObject и размеров

class MeasurementExample extends StatefulWidget {
  @override
  State<MeasurementExample> createState() => _MeasurementExampleState();
}

class _MeasurementExampleState extends State<MeasurementExample> {
  final _containerKey = GlobalKey();
  late Size _containerSize;
  late Offset _containerPosition;

  void _getMeasurements() {
    final RenderBox renderBox = _containerKey.currentContext!.findRenderObject() as RenderBox;
    final size = renderBox.size;
    final position = renderBox.localToGlobal(Offset.zero);

    setState(() {
      _containerSize = size;
      _containerPosition = position;
    });

    print('Size: ${size.width} x ${size.height}');
    print('Position: ${position.dx}, ${position.dy}');
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          key: _containerKey,
          width: 200,
          height: 100,
          color: Colors.blue,
        ),
        ElevatedButton(
          onPressed: _getMeasurements,
          child: Text('Get Size and Position'),
        ),
      ],
    );
  }
}

4. Доступ к BuildContext

class CustomWidget extends StatelessWidget {
  final _widgetKey = GlobalKey();

  void _showSnackBar() {
    final BuildContext? context = _widgetKey.currentContext;
    if (context != null) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Hello from GlobalKey!')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      key: _widgetKey,
      child: ElevatedButton(
        onPressed: _showSnackBar,
        child: Text('Show SnackBar'),
      ),
    );
  }
}

Типы GlobalKey

// 1. GlobalKey<WidgetState>
GlobalKey<MyWidgetState> key = GlobalKey<MyWidgetState>();

// 2. GlobalKey<FormState>
GlobalKey<FormState> formKey = GlobalKey<FormState>();

// 3. GlobalKey<ScaffoldState>
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

// 4. Просто GlobalKey без типа
GlobalKey key = GlobalKey();

Когда использовать GlobalKey

Правильные сценарии:

  • Валидация форм (GlobalKey<FormState>)
  • Открытие Drawer или BottomSheet (GlobalKey<ScaffoldState>)
  • Получение measurements виджета (размеры, позиция)
  • Focus management (FocusNode с GlobalKey)
  • Навигация и анимации

Когда НЕ использовать:

  • Вместо state management (используй Provider, Riverpod, GetX)
  • Для передачи данных между виджетами (используй параметры конструктора)
  • Если можно решить через callback functions
  • Для часто обновляемых данных (это убивает performance)

Performance и проблемы

// ❌ ПЛОХО: GlobalKey для каждого элемента в списке
ListView.builder(
  itemBuilder: (context, index) {
    final key = GlobalKey(); // Создаёт новый key на каждый rebuild!
    return Container(key: key); // Performance nightmare
  },
)

// ✅ ХОРОШО: Используй обычный Key или IndexedStack
ListView.builder(
  itemBuilder: (context, index) {
    return Container(key: ValueKey(index));
  },
)

Альтернативы GlobalKey

В большинстве случаев есть лучшие решения:

1. State Management вместо GlobalKey

// ❌ Плохо
final _formKey = GlobalKey<MyFormState>();
_formKey.currentState?.submit();

// ✅ Хорошо
final _controller = TextEditingController();
text.Controller.notifyListeners();

2. Callbacks вместо GlobalKey

// ❌ Плохо
final _childKey = GlobalKey<_ChildState>();
_childKey.currentState?.doSomething();

// ✅ Хорошо
Child(onAction: () => print('Action triggered'))

3. Provider/Riverpod вместо GlobalKey

// Используй реактивный state management вместо прямого доступа
final dataProvider = StateNotifierProvider((ref) => DataNotifier());

Заключение

GlobalKey — это мощный инструмент, но его нужно использовать осторожно. За 10+ лет опыта я вижу, что он часто используется как костыль вместо правильной архитектуры. Используй GlobalKey только когда это действительно необходимо и нет лучшей альтернативы.