【问题标题】:Display SnackBar in Flutter在 Flutter 中显示 SnackBar
【发布时间】:2017-03-27 13:36:31
【问题描述】:

我想在 Flutter 的有状态小部件中显示一个简单的 SnackBar。我的应用程序使用名为 MyHomePage 的有状态小部件创建了新的 MaterialApp 实例。

我尝试在 showSnackBar() 方法中显示 SnackBar。但它失败了'方法'showSnackBar'在null上被调用'。

这段代码有什么问题?

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

  @override
  void initState() {
    super.initState();
    showInSnackBar("Some text");
  }

  @override
  Widget build(BuildContext context) {
    return new Padding(
            key: _scaffoldKey,
            padding: const EdgeInsets.all(16.0),
            child: new Text("Simple Text")
    );
  }

  void showInSnackBar(String value) {
    _scaffoldKey.currentState.showSnackBar(new SnackBar(
        content: new Text(value)
    ));
  }
}

解决方案:

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    showInSnackBar("Some text");
    return new Padding(
        padding: const EdgeInsets.all(16.0),
        child: new Scaffold(
          body: new Text("Simple Text")
        )
    );
  }

  void showInSnackBar(String value) {
    Scaffold.of(context).showSnackBar(new SnackBar(
        content: new Text(value)
    ));
  }
}

【问题讨论】:

  • 我宁愿建议所有 Flutter 用户使用 flushbar 插件。你可以看看这个答案为什么你应该使用flushbar插件而不是直接使用snackbar。去看看这个答案。 stackoverflow.com/a/51261864/7940500
  • 只需使用异步函数来调用你的snackbar,如果你把你的函数放在build而不是initstate中,每次有setstate时你都会弹出snackbar
  • 如果您很困惑,这里有一个关于如何使 SnackBar 小部件可用于任何页面的教程视频:youtu.be/u9KoFtu0HEY
  • Scaffold.of() 已被弃用以显示 SnackBars,请改用 ScaffoldMessenger

标签: dart android-snackbar snackbar flutter


【解决方案1】:

Scaffold.Of(context) 不推荐用于显示小吃吧。

使用新方法:

final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));

ScaffoldMessenger.of(context).showSnackBar(snackBar);

https://flutter.dev/docs/cookbook/design/snackbars

【讨论】:

    【解决方案2】:

    在 Flutter 中显示 SnackBar

     ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                      content: Text('Please Enter Valid Data'),
                                      backgroundColor: Colors.red,
                                    ));
    

    例子:

     if(value.isNotEmpty && value != null){
     ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                      content: Text('Validated Successfully'),
                                      backgroundColor: Colors.green,
                                    ));
    }else{
     ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                      content: Text('Please Enter Valid Data'),
                                      backgroundColor: Colors.red,
                                    ));
    }
    

    【讨论】:

      【解决方案3】:

      在最新版本中--

      完整示例

      
      import 'package:flutter/material.dart';
      
      
      void test() {
        runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
        // This widget is the root of your application.
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            title: 'Test App',
            theme: ThemeData(
              primarySwatch: Colors.blue,
              visualDensity: VisualDensity.adaptivePlatformDensity,
            ),
            home: testPage(title: 'Flutter Demo Home Page'),
          );
        }
      }
      
      class testPage extends StatefulWidget {
        testPage({Key? key, required this.title}) : super(key: key);
      
        final String title;
      
        @override
        _testPageState createState() => _testPageState();
      }
      
      class _testPageState extends State<testPage> {
      
        GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
      
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
      
              home: SizedBox(
                  child: Scaffold(
      
                      key: _scaffoldKey,
      
                      body: Center(
                          child: RaisedButton(
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(10.0),
                            ),
                            onPressed: () {
      
                              final snackBar = SnackBar(content: Text('Your Text'));
      
                              _scaffoldKey.currentState!.showSnackBar(snackBar);
                            },
      
                            child: Text("Show SB"),
                          )
                      )
                  )
              )
          );
        }
      }
      

      【讨论】:

        【解决方案4】:

        ScaffoldState 现在已弃用。使用ScaffoldMessengerState

        使用ScaffoldMessenger 显示SnackBar 通常有两种方式。


        1. 直接方式:

          @override
          Widget build(BuildContext context) {
            return Scaffold(
              body: ElevatedButton(
                onPressed: () {
                  var snackBar = SnackBar(content: Text('Hello World'));
                  ScaffoldMessenger.of(context).showSnackBar(snackBar);
                },
                child: Text('Show SnackBar'),
              ),
            );
          }
          

        1. 使用GlobalKey

          final _globalKey = GlobalKey<ScaffoldMessengerState>();
          
          @override
          Widget build(BuildContext context) {
            return ScaffoldMessenger(
              key: _globalKey,
              child: Scaffold(
                body: Center(
                  child: ElevatedButton(
                    onPressed: () {
                      var snackBar = SnackBar(content: Text('Hello World'));
                      _globalKey.currentState.showSnackBar(snackBar);
                    },
                    child: Text('Show SnackBar'),
                  ),
                ),
              ),
            );
          }
          

        【讨论】:

          【解决方案5】:

          ScaffoldState 现已弃用。使用 ScaffoldMessengerState。

          上述所有解决方案都很棒,但现在已弃用,您应该以这种方式使用它。

              var snackBar = SnackBar(content: Text('Hello World'));
          
              ScaffoldMessenger.of(_scaffoldKey.currentContext)
              .showSnackBar(snackBar );
          

          【讨论】:

          • 嗨,我已经在my answer 中写了这个。两次添加相同的东西没有任何好处。
          • 如果我在写这篇文章的时候看到了你的答案,我不会浪费时间写一个重复的答案。祝你有美好的一天
          • 我只是从我的编辑答案中删除了Builder。其余的东西完全一样,不是吗?
          【解决方案6】:

          如果您在一个不直接具有return Scaffold(...) 小部件的小部件内,您可以这样做:

          Scaffold.of(context).showSnackBar(SnackBar(content: Text("Hello from snackbar")));
          

          不需要创建额外的 Scaffold 小部件,因为 showSnackBar() 方法需要一个,您可以重复使用。

          【讨论】:

            【解决方案7】:

            最好的方法是创建一个 PageWrapper,您可以将所有页面包装起来以获取 Scaffold 小部件的上下文并避免再次遇到这些错误。

            这是页面包装器的示例:

            import 'package:flutter/material.dart';
            
            class PageWrapper extends StatelessWidget {
            
              Widget page;
            
              WPage(this.page);
            
              @override
              Widget build(BuildContext context) {
                return Scaffold(
                  backgroundColor: Colors.white,
                  body: page
                );
              }
            }
            

            然后您可以像这样将所有页面包装在 main.dart 中:

            import 'package:flutter/material.dart';
            void main() => runApp(MaterialApp(
              initialRoute: '/login',
              routes: {
                '/login': (context) => PageWrapper(Login()),
                '/home': (context) => PageWrapper(Home())
              }
            ));
            

            现在您可以在任何页面的任何位置拨打Scaffold.of(context) 就好了。

            参考:https://noobieprogrammer.blogspot.com/2020/06/how-to-create-error-alert-or-popup.html

            或观看视频版本(5 分钟长):https://youtu.be/u9KoFtu0HEY

            【讨论】:

              【解决方案8】:

              打开snackbar的方法有很多,但我要说的是,有一种方法可以在没有任何上下文的情况下打开snackbar,并且可以完全自定义它,有颜色、模糊、形状、渐变、图标,字面上可以做任何事情通过这个包:

              https://pub.dev/packages/get

              您可以简单地调用:Get.snackbar("Hi", "your message"); 并准备好,而不是传递上下文,而不是创建一个 globalKey 并将其附加到您的 Scaffold 上! 最有趣的是,您可以从任何地方调用snackbar,例如,每当出现API 故障时,您都可以从控制器返回错误消息。 这是非常值得了解的。

              【讨论】:

                【解决方案9】:

                您可以通过三个简单的步骤完成此操作。

                1. 确保你有一个脚手架的钥匙。您可以通过编写以下代码来创建它:

                  final _scaffoldKey = GlobalKey<ScaffoldState>();
                  

                2.现在你必须在你的脚手架中提到这个键,通过在脚手架内写下下面的行:

                key:_scaffoldKey,
                
                1. 现在您可以通过以下方式显示小吃栏:

                  final snackBar=SnackBar(
                                        content: Text('Your password has been changed successfully'),
                                      );
                                      _scaffoldKey.currentState.showSnackBar(snackBar);
                  

                【讨论】:

                  【解决方案10】:

                  initStatebuild之前被调用我猜_scaffoldKey.currentState在调用的时候还没有被初始化。

                  我不知道您是否可以从initState 获得ScaffoldState。如果您更改代码,您可以使用build 方法显示小吃栏:

                  Scaffold.of(context).showSnackBar(SnackBar(Text(value)));
                  

                  【讨论】:

                  • 简单地将method showInSnackBar() 移动到 build() 没有帮助。一方面你是对的 - initState() 在 build() 方法之前被调用,但即使在 initState() 方法之后:Scaffold.of(context) 以及字段_scaffoldKey.currentState 都是空的。
                  【解决方案11】:

                  你可以用这个:

                  final _scaffoldKey = GlobalKey<ScaffoldState>();
                  
                    @override
                    Widget build(BuildContext context) {
                      return Scaffold(
                        key: _scaffoldKey,
                        appBar: AppBar(
                  
                  
                      ),
                    }
                  }
                  

                  然后 在onPressed()可以加这个代码

                  _scaffoldKey.currentState.showSnackBar(
                         new SnackBar(
                            content: new Text('Hello this is snackbar!')
                         )
                  );
                  

                  【讨论】:

                    【解决方案12】:
                    Scaffold.of(context).showSnackBar(
                         SnackBar(content: Text("Thanks for using snackbar",
                         textAlign: TextAlign.center, style: TextStyle(fontSize: 16.0, fontWeight: 
                         FontWeight.bold),), duration: Duration(seconds: 2), backgroundColor: Colors.red,)
                    );
                    

                    【讨论】:

                      【解决方案13】:

                      要在 initState() 上初始化 Snackbar,您可以在构建布局后执行一个函数。

                      void initState() {
                      super.initState();
                      WidgetsBinding.instance
                          .addPostFrameCallback((_) => _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Your message here..")));}
                      

                      【讨论】:

                        【解决方案14】:

                        如果有人希望在initState() 上初始化Snackbar(换句话说,当页面加载到这里时是我的解决方案)。另外,我假设您已经有了_scaffoldKey

                        void initState() {
                          super.initState();
                          Future.delayed(Duration(seconds: 1)).then(
                            (_) => _displaySnackbar
                          );
                        }
                        
                        // Display Snackbar
                        void get _displaySnackbar {
                          _scaffoldKey.currentState.showSnackBar(SnackBar(
                            duration: Duration(minutes: 1),
                            content: Text('Your snackbar message')
                          ));
                        }
                        

                        【讨论】:

                          【解决方案15】:

                          有一种更好更简洁的方式来显示 Snackbar。我发现它很难并分享,所以也许它对其他人有帮助。

                          无需更改您的主要应用部分

                          class MyApp extends StatelessWidget {
                          @override
                          Widget build(BuildContext context) {
                            return new MaterialApp(
                              title: 'MyApp',
                              theme: new ThemeData(
                              primarySwatch: Colors.orange),
                              home: new MainPage());
                            }
                          }
                          

                          页面状态代码是变化的地方。

                          我们知道 Flutter 提供了Scaffold.of(context).showSnackBar。但是,上下文应该是 Scaffold 的后代的上下文,而不是包含 Scaffold 的上下文。为了避免出错,我们需要为 Scaffold 的主体使用 BuildContext,并将其存储在一个变量中,如下所示。

                          class MainPageState extends State<MainPage> {                                                                         
                          BuildContext scaffoldContext;                                                                                                                                                                                                
                          
                          @override                                                                                                           
                          Widget build(BuildContext context) {                                                                                
                          
                          return new Scaffold(                                                                                              
                              backgroundColor: Colors.grey,                                                                                 
                              appBar: new AppBar(                                                                                           
                                title: const Text(APP_TITLE),                                                                               
                              ),                                                                                                             
                              body: new Builder(builder: (BuildContext context) {                                                           
                                scaffoldContext = context;                                                                                  
                                return new Center(
                                     child: new Text('Hello World', style: new TextStyle(fontSize: 32.0)),
                                   );                                                                                
                              }));                                                                                                           
                          }                                                                                                                   
                          
                          
                          void createSnackBar(String message) {                                                                               
                            final snackBar = new SnackBar(content: new Text(message),                                                         
                            backgroundColor: Colors.red);                                                                                      
                          
                            // Find the Scaffold in the Widget tree and use it to show a SnackBar!                                            
                            Scaffold.of(scaffoldContext).showSnackBar(snackBar);                                                              
                            }                                                                                                                   
                          }     
                          

                          现在,您可以从任何地方调用此函数,它会显示 Snackbar。例如,我使用它来显示 Internet 连接消息。

                          【讨论】:

                          【解决方案16】:

                          就我而言,我有这样的代码(在课堂状态下)

                          final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
                          
                          void showInSnackBar(String value) {
                              _scaffoldKey.currentState.showSnackBar(new SnackBar(content: new Text(value)));
                          }
                          

                          但我没有设置脚手架的密钥。 所以当我添加 key: _scaffoldKey

                           @override
                           Widget build(BuildContext context) {
                            return new Scaffold(
                              key: _scaffoldKey,
                              body: new SafeArea(
                          

                          小吃店开始工作:)

                          【讨论】:

                          【解决方案17】:

                          存在三个问题。首先是您在任何地方都没有 Scaffold,而 Scaffold 小部件知道如何显示小吃店。第二个是你有一把抓住脚手架的钥匙,但是你把它放在了一个 Padding 上(而且 Paddings 没有任何小吃店知识)。第三个是您在与其关联的小部件有机会初始化之前使用了密钥,因为 initState 在构建之前被调用。

                          最简单的解决方案是将 MyApp 小部件中的 home 行更改为:

                          home: new Scaffold(body: new MyHomePage()),

                          ...然后删除对_scaffoldKey 的所有提及,而在您当前拥有_scaffoldKey.currentState 的地方使用Scaffold.of(context)

                          【讨论】:

                          • 用新的 Scaffold 包装我的 StatefulWidget 并使用 Scaffold.of(context) 而不是 _scaffoldKey 参考有帮助。谢谢!
                          • 嵌套Scaffolds 是强烈不鼓励的,所以在这里要小心!
                          • @Westy92,很有趣的 cmets,你能告诉我们为什么吗?我看到有人鼓励使用脚手架包装每个小部件,但我不相信。
                          猜你喜欢
                          • 1970-01-01
                          • 2020-09-24
                          • 1970-01-01
                          • 2019-12-15
                          • 2019-08-28
                          • 2019-05-24
                          • 2020-04-23
                          • 2019-07-27
                          • 2022-01-06
                          相关资源
                          最近更新 更多