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

Что такое BuildContext и как он используется?

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

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

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

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

Что такое BuildContext и как он используется

BuildContext — это объект, который представляет позицию виджета в дереве элементов (Element Tree). Это фундаментальное понятие в Flutter, которое позволяет виджетам взаимодействовать со своим окружением и получать данные от предков в дереве виджетов.

Что это такое?

// BuildContext это по сути ссылка на Element
Widget build(BuildContext context) {
  // context — это ссылка на Element этого виджета
  // через context мы можем:
  // 1. Получать данные от предков (Theme, MediaQuery, Inherited)
  // 2. Выполнять навигацию
  // 3. Показывать диалоги, снекбары
  // 4. Получать информацию о размере экрана
}

Архитектура

Widget (конфигурация) — неизменяемая
   ↓
Element (состояние, позиция в дереве) — это BuildContext
   ↓
RenderObject (отрисовка)

BuildContext — это объект Element, который связывает виджет с его позицией в дереве.

Основные способы использования

1. Получение Theme

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Получить Theme
    final theme = Theme.of(context);
    final primaryColor = theme.primaryColor;
    final textTheme = theme.textTheme;
    
    return Container(
      color: theme.colorScheme.surface,
      child: Text(
        'Hello',
        style: theme.textTheme.titleLarge,
      ),
    );
  }
}

2. Получение информации о размере экрана

class ResponsiveWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final mediaQuery = MediaQuery.of(context);
    final screenWidth = mediaQuery.size.width;
    final screenHeight = mediaQuery.size.height;
    final isPortrait = mediaQuery.orientation == Orientation.portrait;
    final devicePixelRatio = mediaQuery.devicePixelRatio;
    
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('Width: ${screenWidth.toStringAsFixed(0)}'),
          Text('Height: ${screenHeight.toStringAsFixed(0)}'),
          Text('Orientation: ${isPortrait ? "Portrait" : "Landscape"}'),
          if (screenWidth > 600)
            Text('Large screen')
          else
            Text('Small screen'),
        ],
      ),
    );
  }
}

3. Навигация между экранами

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Используем context для навигации
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => SecondScreen(),
              ),
            );
          },
          child: Text('Go to Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Назад
            Navigator.of(context).pop();
          },
          child: Text('Go Back'),
        ),
      ),
    );
  }
}

4. Показание диалогов и снекбаров

class DialogExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                // Показать AlertDialog
                showDialog(
                  context: context,
                  builder: (dialogContext) => AlertDialog(
                    title: Text('Confirm'),
                    content: Text('Are you sure?'),
                    actions: [
                      TextButton(
                        onPressed: () => Navigator.of(dialogContext).pop(),
                        child: Text('Cancel'),
                      ),
                      TextButton(
                        onPressed: () => Navigator.of(dialogContext).pop(true),
                        child: Text('OK'),
                      ),
                    ],
                  ),
                );
              },
              child: Text('Show Dialog'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                // Показать Snackbar
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: Text('Hello from Snackbar!'),
                    duration: Duration(seconds: 2),
                  ),
                );
              },
              child: Text('Show Snackbar'),
            ),
          ],
        ),
      ),
    );
  }
}

5. Работа с FocusScope

class FormExample extends StatefulWidget {
  @override
  State<FormExample> createState() => _FormExampleState();
}

class _FormExampleState extends State<FormExample> {
  late FocusNode emailFocus;
  late FocusNode passwordFocus;
  
  @override
  void initState() {
    super.initState();
    emailFocus = FocusNode();
    passwordFocus = FocusNode();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              focusNode: emailFocus,
              decoration: InputDecoration(labelText: 'Email'),
              onSubmitted: (_) {
                // Перейти на следующее поле
                FocusScope.of(context).requestFocus(passwordFocus);
              },
            ),
            TextField(
              focusNode: passwordFocus,
              decoration: InputDecoration(labelText: 'Password'),
              obscureText: true,
              onSubmitted: (_) {
                // Убрать фокус с клавиатуры
                FocusScope.of(context).unfocus();
              },
            ),
          ],
        ),
      ),
    );
  }
  
  @override
  void dispose() {
    emailFocus.dispose();
    passwordFocus.dispose();
    super.dispose();
  }
}

Inherited Widgets с BuildContext

// Создание собственного Inherited Widget
class ThemeProvider extends InheritedWidget {
  final String themeName;
  
  ThemeProvider({
    required this.themeName,
    required Widget child,
  }) : super(child: child);
  
  static ThemeProvider of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ThemeProvider>()!;
  }
  
  @override
  bool updateShouldNotify(ThemeProvider oldWidget) {
    return themeName != oldWidget.themeName;
  }
}

// Использование в дереве
class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ThemeProvider(
      themeName: 'dark',
      child: MyHomePage(),
    );
  }
}

// Получение значения
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final provider = ThemeProvider.of(context);
    return Text('Current theme: ${provider.themeName}');
  }
}

Практический пример: полная форма

class LoginForm extends StatefulWidget {
  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<FormState>();
  String email = '';
  String password = '';
  bool isLoading = false;
  
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;  // получить размер экрана
    final theme = Theme.of(context);           // получить тему
    
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: SingleChildScrollView(
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Form(
            key: _formKey,
            child: Column(
              children: [
                TextFormField(
                  decoration: InputDecoration(labelText: 'Email'),
                  validator: (value) => value?.isEmpty ?? true ? 'Email required' : null,
                  onChanged: (value) => email = value,
                ),
                SizedBox(height: 16),
                TextFormField(
                  decoration: InputDecoration(labelText: 'Password'),
                  obscureText: true,
                  validator: (value) => value?.isEmpty ?? true ? 'Password required' : null,
                  onChanged: (value) => password = value,
                ),
                SizedBox(height: 24),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton(
                    onPressed: isLoading ? null : () => _login(context),
                    child: isLoading
                        ? SizedBox(
                            height: 20,
                            width: 20,
                            child: CircularProgressIndicator(strokeWidth: 2),
                          )
                        : Text('Login'),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
  
  Future<void> _login(BuildContext context) async {
    if (!_formKey.currentState!.validate()) return;
    
    setState(() => isLoading = true);
    
    try {
      // API call
      await Future.delayed(Duration(seconds: 2));
      
      // Навигация
      Navigator.of(context).pushReplacementNamed('/home');
    } catch (e) {
      // Снекбар с ошибкой
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error: $e')),
      );
    } finally {
      setState(() => isLoading = false);
    }
  }
}

Важные моменты

1. BuildContext специфичен для каждого виджета

// context это Context ЭТОГО виджета
Widget build(BuildContext context) {
  return MyChild();  // MyChild имеет свой context
}

// Нельзя использовать context родителя для MyChild
class MyChild extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // это context MyChild, не родителя
  }
}

2. BuildContext не рекомендуется передавать в обработчики

// ❌ Плохо
ElevatedButton(
  onPressed: () => handleClick(context),
  child: Text('Click'),
)

// ✅ Хорошо
ElevatedButton(
  onPressed: () {
    Navigator.of(context).push(...);  // используй context сразу
  },
  child: Text('Click'),
)

3. Проверка наличия предка

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Проверить наличие предка
    final scaffold = ScaffoldMessenger.maybeOf(context);
    if (scaffold == null) {
      return Text('No Scaffold ancestor');
    }
    
    return ElevatedButton(
      onPressed: () {
        scaffold.showSnackBar(
          SnackBar(content: Text('Hello')),
        );
      },
      child: Text('Show Snackbar'),
    );
  }
}

Вывод: BuildContext — это ключ к взаимодействию виджета со своим окружением в дереве. Через него мы получаем темы, размеры экрана, выполняем навигацию, показываем диалоги и снекбары. Это одно из самых важных понятий в Flutter, которое используется повсеместно.

Что такое BuildContext и как он используется? | PrepBro