如你所见,我的按钮在刑台的身体里。但是我得到了这个异常:

使用不包含脚手架的上下文调用Scaffold.of()。

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<ScaffoldState>,我们就可以像下面那样显示SnackBar,而不需要在Builder小部件中包装我们的主体。返回脚手架的小部件应该是一个有状态的小部件。

 _scaffoldKey.currentState.showSnackBar(snackbar); 

当前回答

一个更有效的解决方案是将构建功能拆分为几个小部件。 这将引入一个“新上下文”,从中您可以获得Scaffold

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Scaffold.of example.')),
        body: MyScaffoldBody(),
      ),
    );
  }
}

class MyScaffoldBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
          child: Text('Show a snackBar'),
          onPressed: () {
            Scaffold.of(context).showSnackBar(
              SnackBar(
                content: Text('Have a Snack'),
              ),
            );
          }),
    );
  }
}

其他回答

你可以用两种方法来解决这个问题:

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: (){
               final snackBar = SnackBar(content: Text('Profile saved'));
               globalKey.currentState.showSnackBar(snackBar);
          },
        ),
     );
   }
}

试试下面的代码:

Singleton.showInSnackBar(
    Scaffold.of(context).context, "Theme Changed Successfully");
// Just use Scaffold.of(context) before context!!

更新:Flutter的推荐方法(截至2022年12月20日)…

要显示一个小吃店,你应该使用:

ScaffoldMessenger

从我们读到的文件中

脚手架中的SnackBar API现在由Scaffold messenger处理,其中一个在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'),
  ),
);

现在,在文档中我们可以看到

When presenting a SnackBar during a transition, the SnackBar will complete a Hero animation, moving smoothly to the next page. The ScaffoldMessenger creates a scope in which all descendant Scaffolds register to receive SnackBars, which is how they persist across these transitions. When using the root ScaffoldMessenger provided by the MaterialApp, all descendant Scaffolds receive SnackBars, unless a new ScaffoldMessenger scope is created further down the tree. By instantiating your own ScaffoldMessenger, you can control which Scaffolds receive SnackBars, and which do not based on the context of your application.


原来的答案

在Flutter文档中,您遇到的这种行为甚至被称为“棘手的情况”。

如何修复

你可以从这里发布的其他答案中看到,这个问题以不同的方式修复。例如,我引用的这篇文档通过使用Builder来解决这个问题

一个内部的BuildContext,这样onPressed方法就可以用Scaffold.of()引用脚手架。

因此,从Scaffold调用showSnackBar的方法如下

@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.')
            ));
          }
        );
      }
    )
  );
}

现在给好奇的读者一些细节

我自己发现,通过简单地(Android Studio)将光标设置在一段代码上(Flutter类,方法等),并按ctrl+B,就可以显示该特定部分的文档,来探索Flutter文档是非常有指导意义的。

您所面临的特定问题在BuildContext的文档中提到,可以在其中阅读

每个小部件都有自己的BuildContext,它成为[…]返回的小部件的父部件。构建函数。

因此,这意味着在我们的案例中,当创建Scaffold小部件时,上下文将是它的父部件(!)。此外,脚手架的文件。Of表示它会返回

该类最近的[Scaffold]实例的状态,它包含给定的上下文。

但是在我们的例子中,上下文还没有包含脚手架(它还没有被构建)。这就是建造者开始行动的地方!

文档再一次为我们提供了启示。我们可以读到

一个柏拉图式的小部件,它调用一个闭包来获取它的子小部件。

嘿,等一下,什么!?好吧,我承认:这并没有多大帮助……但这足以说明(遵循另一个SO线程)

Builder类的目的只是构建和返回子部件。

现在一切都清楚了!通过在Scaffold中调用Builder,我们正在构建脚手架,以便能够获得它自己的上下文,并且武装了这个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.')
            ));
          }
        );
      }
    )
  );
}

在这里,我们使用一个构建器来封装另一个需要小吃店的小部件

Builder(builder: (context) => GestureDetector(
    onTap: () {
        Scaffold.of(context).showSnackBar(SnackBar(
            content: Text('Your Services have been successfully created Snackbar'),
        ));
        
    },
    child: Container(...)))
           Expanded(
              child: Container(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
                child: Builder(
                  builder: (context) => RaisedButton(
                    onPressed: () {
                        final snackBar = SnackBar(
                          content: Text("added to cart"),
                           action: SnackBarAction(
                            label: 'Undo',
                             onPressed: () {
                              // Some code to undo the change.
                            },
                          ),
                        );
                    },
                    textColor: Colors.white,
                    color: Colors.pinkAccent,
                    child: Text("Add To Cart",
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.w600)),
                  ),
                ),
              ),
            )