ОБНОВЛЕНИЕ 03.03.2021: Только что вышел Flutter 2.0 ...
(Что касается исторической ценности, я также приведу ниже свой исходный ответ ...)
Теперь ... Enter ...
Из документа мы читаем
SnackBar API в Scaffold теперь обрабатывается ScaffoldMessenger, один из которых доступен по умолчанию в контексте MaterialApp.
Итак, используя новый ScaffoldMessenger, теперь вы сможете писать такой код, как
Scaffold(
key: scaffoldKey,
body: GestureDetector(
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () { },
),
));
},
child: const Text('SHOW SNACK'),
),
);
Теперь снова в документе мы видим, что
При представлении SnackBar во время перехода SnackBar завершит анимацию Hero, плавно переходя на следующую страницу.
ScaffoldMessenger создает область, в которой все дочерние скаффолды регистрируются для получения SnackBars, и именно так они сохраняются при этих переходах. При использовании корневого ScaffoldMessenger, предоставляемого MaterialApp, все дочерние скаффолды получают SnackBars, если только дальше по дереву не создается новая область ScaffoldMessenger. Создавая собственный ScaffoldMessenger, вы можете контролировать, какие скаффолды получают SnackBars, а какие нет, в зависимости от контекста вашего приложения.
ОРИГИНАЛЬНЫЙ ОТВЕТ
Само поведение, с которым вы столкнулись, даже упоминается как сложный случай в Flutter документация.
Как исправить
Проблема устраняется по-разному, как вы можете видеть из других ответов, размещенных здесь. Например, часть документации, на которую я ссылаюсь, решает проблему с помощью Builder
, который создает
внутренний BuildContext
, так что onPressed
методы могут ссылаться на Scaffold
с помощью Scaffold.of()
.
Таким образом, вызов showSnackBar
из Scaffold будет
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext innerContext) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
Scaffold.of(innerContext).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
Теперь немного подробностей для любознательного читателя.
Я сам нашел весьма поучительным изучить документацию Flutter, просто установив (Android Studio) курсор на фрагмент кода (Flutter класс, метод и т. д.) и нажмите ctrl + B, чтобы отобразить документацию для этого конкретного элемента.
Конкретная проблема, с которой вы столкнулись, упоминается в документе для BuildContext, где можно прочитать
Каждый виджет имеет свой собственный BuildContext, который становится родительским для виджета, возвращаемого функцией [...]. Build.
Итак, это означает, что в нашем случае context будет родительским элементом нашего виджета Scaffold при его создании (!). Кроме того, в документе Scaffold.of говорится, что он возвращает
Состояние из ближайшего экземпляра [Scaffold] этого класса, который включает данный контекст.
Но в нашем случае context не включает (пока) Scaffold (он еще не построен). Здесь в дело вступает Builder!
И снова документ освещает нас. Там мы можем прочитать
[Класс Builder - это просто] Платонический виджет, который вызывает закрытие для получения своего дочернего виджета.
Эй, погоди, что ?! Хорошо, я признаю: это не очень помогает ... Но достаточно сказать (после другой поток SO), который
Назначение класса Builder - просто создавать и возвращать дочерние виджеты.
Итак, теперь все становится ясно! Вызывая Builder внутри Scaffold, мы создаем Scaffold, чтобы иметь возможность получить его собственный контекст, и, вооружившись этим innerContext, мы, наконец, можем вызовите Scaffold.of (innerContext)
Аннотированная версия приведенного выше кода следует
@override
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext innerContext) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
// here, Scaffold.of(innerContext) returns the locally created Scaffold
Scaffold.of(innerContext).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
person
Deczaloth
schedule
09.05.2020