【问题标题】:Flutter: How to close SnackBar when navigating away?Flutter:导航离开时如何关闭 SnackBar?
【发布时间】:2021-12-03 15:14:15
【问题描述】:

我有一个问题。我有一个“详细信息”页面,可以通过ScaffoldMessenger 显示SnackBar。 此小吃栏不会隐藏,它的持续时间设置为很长时间,因为它应该保持可见很长时间,或者直到它被用户或导航离开视图。

最后一部分是我遇到的问题。在我的dispose 方法中,我尝试调用ScaffoldMessenger.of(_scaffoldKey.currentContext!).hideCurrentSnackBar(),但这不起作用并抛出无法访问上下文的错误。 我怀疑这是因为与键关联的上下文也是从小部件树中删除的相同上下文,因为我正在离开它(通过使用后退按钮)。

我不想处理在其他视图中删除快餐栏的问题。我知道每次导航到它们时我都可以致电ScaffoldMessenger.of(context).clearAllSnackBars(),但出于架构原因我不喜欢它:

  1. “详细”视图拥有快餐栏,因为它负责创建它。它还应负责处理它。
  2. 将来我可能会重新组织我的观点,然后我必须记住 清理各处的小吃店。我给你的例子是受限的 示例,但想象有可访问的侧边栏导致许多 不同的看法。这意味着将这段代码添加到所有这些视图中。

所以我真的很想在处理 DetailPage 视图时以某种方式删除该快餐栏。我怎样才能做到这一点?

链接到dartpad.dev

import 'package:flutter/material.dart';

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

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: const HomePage(), routes: {
      '/detail': (_) => const DetailPage(),
    });
  }
}

class HomePage extends StatelessWidget {
  const HomePage({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: SizedBox.expand(
        child: TextButton(
          child: const Text('See detail'),
          onPressed: () {
            Navigator.pushNamed(context, '/detail');
          },
        ),
      ),
    );
  }
}

class DetailPage extends StatefulWidget {
  const DetailPage({
    Key? key,
  }) : super(key: key);

  @override
  _DetailPageState createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar(SnackBar(
        content: const Text('Entered detail page'),
        duration: const Duration(days: 1),
        action: SnackBarAction(
            label: 'Close',
            onPressed: () {
              ScaffoldMessenger.of(_scaffoldKey.currentContext!)
                  .hideCurrentSnackBar();
            }),
      ));
    });
  }

  @override
  void dispose() {
    ScaffoldMessenger.of(_scaffoldKey.currentContext!).hideCurrentSnackBar();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(title: const Text('Detail')),
      body: const SizedBox.expand(
        child: Center(child: Text('Detail page')),
      ),
    );
  }
}

【问题讨论】:

    标签: flutter dart navigation snackbar


    【解决方案1】:

    使用WillPopScope 小部件删除snackbar。

    此小部件允许异步代码在视图弹出导航堆栈之前运行,并且此时上下文仍然存在于小部件树中。您可以通过这种方式摆脱覆盖的 dispose 方法。

    您可以在 this dartpad 中看到它的工作原理,或者只需注意以下代码:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(App());
    }
    
    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(home: const HomePage(), routes: {
          '/detail': (_) => const DetailPage(),
        });
      }
    }
    
    class HomePage extends StatelessWidget {
      const HomePage({
        Key? key,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Home')),
          body: SizedBox.expand(
            child: TextButton(
              child: const Text('See detail'),
              onPressed: () {
                Navigator.pushNamed(context, '/detail');
              },
            ),
          ),
        );
      }
    }
    
    class DetailPage extends StatefulWidget {
      const DetailPage({
        Key? key,
      }) : super(key: key);
    
      @override
      _DetailPageState createState() => _DetailPageState();
    }
    
    class _DetailPageState extends State<DetailPage> {
      final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
    
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance!.addPostFrameCallback((_) {
          ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar(SnackBar(
            content: const Text('Entered detail page'),
            duration: const Duration(days: 1),
            action: SnackBarAction(
                label: 'Close',
                onPressed: () {
                  ScaffoldMessenger.of(_scaffoldKey.currentContext!)
                      .hideCurrentSnackBar();
                }),
          ));
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return WillPopScope(
            child: Scaffold(
              key: _scaffoldKey,
              appBar: AppBar(title: const Text('Detail')),
              body: const SizedBox.expand(
                child: Center(child: Text('Detail page')),
              ),
            ),
            onWillPop: () async {
              ScaffoldMessenger.of(_scaffoldKey.currentContext!).hideCurrentSnackBar();
              return Future.value(true);
            });
      }
    }
    

    我认为唯一的缺点是hideCurrentSnackBar 没有与Future 一起完成,所以动画没有完成。也许有一种方法可以使用某种Completer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-20
      • 2022-12-31
      • 2015-07-23
      • 1970-01-01
      • 2020-10-18
      • 2021-01-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多