Scaffold.of(), вызванный контекстом, который не содержит леса

Как видите, моя кнопка находится внутри тела Леса. Но я получаю это исключение:

Scaffold.of(), вызываемый контекстом, который не содержит Scaffold.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SnackBar Playground'),
      ),
      body: Center(
        child: RaisedButton(
          color: Colors.pink,
          textColor: Colors.white,
          onPressed: _displaySnackBar(context),
          child: Text('Display SnackBar'),
        ),
      ),
    );
  }
}

_displaySnackBar(BuildContext context) {
  final snackBar = SnackBar(content: Text('Are you talkin\' to me?'));
  Scaffold.of(context).showSnackBar(snackBar);
}

РЕДАКТИРОВАТЬ:

Я нашел другое решение этой проблемы. Если мы дадим Scaffold ключ, который является GlobalKey, мы можем отобразить SnackBar следующим образом, без необходимости обертывать наше тело виджлером Builder. Виджет, который возвращает Scaffold, должен быть видоизмененным.

 _scaffoldKey.currentState.showSnackBar(snackbar); 

Ответы

Ответ 1

Это исключение происходит потому, что вы используете context виджета, который создает экземпляр Scaffold. Не context ребенка Scaffold.

Вы можете решить эту проблему, просто используя другой контекст:

Scaffold(
    appBar: AppBar(
        title: Text('SnackBar Playground'),
    ),
    body: Builder(
        builder: (context) => 
            Center(
            child: RaisedButton(
            color: Colors.pink,
            textColor: Colors.white,
            onPressed: () => _displaySnackBar(context),
            child: Text('Display SnackBar'),
            ),
        ),
    ),
);

Обратите внимание, что хотя мы используем здесь Builder, это не единственный способ получить другой BuildContext.

Также возможно извлечь поддерево в другой Widget (обычно с использованием рефактора extract widget)

Ответ 2

Вы можете использовать GlobalKey. Единственным недостатком является то, что использование GlobalKey может быть не самым эффективным способом сделать это.

Хорошо, что вы также можете передать этот ключ другим классам пользовательских виджетов, которые не содержат никаких скаффолдов. Смотрите (здесь)

class HomePage extends StatelessWidget {
  final _scaffoldKey = GlobalKey<ScaffoldState>(); \\ new line
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,                           \\ new line
      appBar: AppBar(
        title: Text('SnackBar Playground'),
      ),
      body: Center(
        child: RaisedButton(
          color: Colors.pink,
          textColor: Colors.white,
          onPressed: _displaySnackBar(context),
          child: Text('Display SnackBar'),
        ),
      ),
    );
  }
}

_displaySnackBar(BuildContext context) {
  final snackBar = SnackBar(content: Text('Are you talkin\' to me?'));
  _scaffoldKey.currentState.showSnackBar(snackBar);   \\ edited line
}

Ответ 3

Проверьте это из документации метода:

Когда Scaffold действительно создается в той же функции сборки, контекстный аргумент функции сборки не может быть использован для поиска Эшафот (так как он "выше" возвращаемого виджета). В таком В следующих случаях для построения новая область с BuildContext, который находится "под" скаффолдом:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Demo')
    ),
    body: Builder(
      // Create an inner BuildContext so that the onPressed methods
      // can refer to the Scaffold with Scaffold.of().
      builder: (BuildContext context) {
        return Center(
          child: RaisedButton(
            child: Text('SHOW A SNACKBAR'),
            onPressed: () {
              Scaffold.of(context).showSnackBar(SnackBar(
                content: Text('Hello!'),
              ));
            },
          ),
        );
      },
    ),
  );
}

Вы можете проверить описание в методе документации.

Ответ 4

Простым способом решения этой проблемы будет создание ключа для вашего скаффолда, например, финального, со следующим кодом:

Первый: GlobalKey<ScaffoldState>() _scaffoldKey = GlobalKey<ScaffoldState> ();

Scecond: назначьте ключ на ваш эшафот key: _scaffoldKey

В-третьих: вызвать Снэк-бар, используя _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Welcome")));

Ответ 5

Хорошие новости!

Нам больше не нужно оборачивать наш виджет в Builder или использовать вещи GlobalKey, чтобы предоставить виджету context Scaffold. С приходом Flutter к стабильной версии 1.0 команда работала над этим. Теперь мы можем напрямую использовать в нашем классе Stateful State

Scaffold.of(context).showSnackBar(SnackBar(content: Text("Hi there")));

Ответ 6

Есть два способа решения этой проблемы

1) Использование виджета Builder

Scaffold(
    appBar: AppBar(
        title: Text('My Profile'),
    ),
    body: Builder(
        builder: (ctx) => RaisedButton(
            textColor: Colors.red,
            child: Text('Submit'),
            onPressed: () =>  Scaffold.of(ctx).showSnackBar(SnackBar(content: Text('Profile Save'),
            ),
        ),
    ),
);

2) Использование GlobalKey

class HomePage extends StatelessWidget {
  final globalKey = GlobalKey<ScaffoldState>(); 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: globalKey,       
      appBar: AppBar(
        title: Text('My Profile'),
      ),
      body:  RaisedButton(
          textColor: Colors.red,
          child: Text('Submit'),
          onPressed: showSnackbar(context),
        ),
      ),
    );
  }
}

void showSnackbar(BuildContext context) {
  final snackBar = SnackBar(content: Text('Profile saved'));
  globalKey.currentState.showSnackBar(snackBar);  
}