Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Flutter 颤振:修改脚手架以跟踪当前显示的脚手架';应用程序范围SnackBar方法的构建上下文_Flutter_Dart_Snackbar - Fatal编程技术网

Flutter 颤振:修改脚手架以跟踪当前显示的脚手架';应用程序范围SnackBar方法的构建上下文

Flutter 颤振:修改脚手架以跟踪当前显示的脚手架';应用程序范围SnackBar方法的构建上下文,flutter,dart,snackbar,Flutter,Dart,Snackbar,我试图跟踪当前的Scaffolds(它们的BuildContexts),以便创建应用程序范围的SnackBar函数。目前,我正在创建一个类,该类显示一个脚手架,并将其上下文添加到另一个类,该类管理当前运行的脚手架。然而,我没有成功,因为我目前的尝试有两个问题: 它无法正确存储当前的脚手架s 显然,dispose方法对于从当前Scaffold的列表中删除Scaffold的BuildContext来说太晚了,因此这给我带来了异常,“查找停用小部件的祖先是不安全的。” 以下是我目前的尝试: 实现(ma

我试图跟踪当前的
Scaffold
s(它们的
BuildContext
s),以便创建应用程序范围的
SnackBar
函数。目前,我正在创建一个
,该类显示一个
脚手架
,并将其
上下文
添加到另一个
,该类管理当前运行的
脚手架
。然而,我没有成功,因为我目前的尝试有两个问题:

  • 它无法正确存储当前的
    脚手架
    s
  • 显然,
    dispose
    方法对于从当前
    Scaffold
    列表中删除
    Scaffold
    BuildContext
    来说太晚了,因此这给我带来了
    异常,“
    查找停用小部件的祖先是不安全的。
  • 以下是我目前的尝试:

  • 实现(
    main.dart
    ):

  • 承认它不能像现在这样工作,它看起来也有点冗长和混乱。我想做的是简单地制作一个
    Scaffold
    ,它的功能类似于
    Scaffold
    ,但与一个管理器
    ,该管理器跟踪所有
    Scaffold
    上下文
    以便我可以轻松地显示
    SnackBar
    消息,无论用户在哪个页面上。

    我自己还没有尝试过这段代码,但是您如何创建一个
    ChangeNotifier
    ,与
    提供者一起使用,并将其附加到
    材质PP
    上方:

    class MyErrorChangeNotifier extends ChangeNotifier {
      String error;
    
      setError(String error) {
        this.error = error;
    
        notifyListeners();
      }
    }
    
    然后创建一个自定义的
    Scaffold
    ,当您调用
    setError
    时,如果安装了
    Scaffold
    ,它将显示一个
    SnackBar

    class MyScaffold extends Scaffold {
      // TODO constructor
    
      @override
      ScaffoldState createState() => MyScaffoldState();
    }
    
    class MyScaffoldState extends ScaffoldState {
      MyErrorChangeNotifier _myErrorCN;
      Function() _listener;
    
      @override
      void initState() {
        super.initState();
    
        _listener = () {
          if (mounted) {
            showSnackBar(SnackBar(content: Text(_myErrorCN.error)));
          }
        };
    
        Future.microtask(() {
          _myErrorCN = Provider.of<MyErrorChangeNotifier>(context, listen: false)..addListener(_listener);
        });
      }
    
      @override
      void dispose() {
        _myErrorCN?.removeListener(_listener);
    
        super.dispose();
      }
    }
    
    类MyScaffold扩展了Scaffold{ //TODO构造函数 @凌驾 ScaffoldState createState()=>MyScaffoldState(); } 类MyScaffoldState扩展了ScaffoldState{ MyErrorChangeNotifier\u myErrorCN; 函数()\u侦听器; @凌驾 void initState(){ super.initState(); _侦听器=(){ 如果(已安装){ showSnackBar(SnackBar(内容:Text(_myErrorCN.error)); } }; Future.microtask(){ _myErrorCN=Provider.of(context,listen:false)…addListener(_listener); }); } @凌驾 无效处置(){ _myErrorCN?.RemovelListener(_listener); super.dispose(); } }

    每当您遇到这样的用例时,请尝试使用提供程序来考虑解决方案—数据将沿上下文层次结构发送到ChangeNotifier,然后ChangeNotifier将数据沿上下文层次结构发送回所有侦听它的小部件。

    多亏了Ovidiu的帮助,我找到了一个很好的答案。我决定放弃
    提供程序
    ,转而使用静态类。我认为这样的实现更简单。此外,我还让它在新页面上显示一个
    SnackBar
    (如果打开了新的
    Scaffold

    要实现它,只需导入
    mscapfold.dart
    并将
    Scaffold
    更改为
    mscapfold
    s即可

    我发现这种方法非常方便,非常有用,它也可以帮助其他一些人,他们也在寻找一种简单的方法来显示
    SnackBar
    ,无论当前显示的是哪个
    Scaffold

    以下是演示页面:

    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    
    import 'MScaffold.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Snackbar manager',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Snackbar manager'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return MScaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(),
          floatingActionButton: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              FloatingActionButton(
                heroTag: 0,
                child: Icon(Icons.add_circle_outline),
                onPressed: () {
                  ShowSnackBar().showText("You were on page 1");
                },
              ),
              FloatingActionButton(
                heroTag: 1,
                child: Icon(Icons.remove_circle_outline),
                onPressed: () {
                  ShowSnackBar().hide();
                },
              ),
              FloatingActionButton(
                heroTag: 2,
                child: Icon(Icons.add),
                onPressed: () {
                  Navigator.of(context).push(MaterialPageRoute(builder: (context) {
                    return SecondScaffold();
                  }));
                },
              ),
            ],
          ),
        );
      }
    }
    
    class SecondScaffold extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MScaffold(
          appBar: AppBar(
            title: Text("Page 2"),
          ),
          body: Center(),
          floatingActionButton: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              FloatingActionButton(
                heroTag: 0,
                child: Icon(Icons.add_circle_outline),
                onPressed: () {
                  ShowSnackBar().show(
                    SnackBar(
                      content: Text("You were on page 2"),
                    ),
                  );
                },
              ),
              FloatingActionButton(
                heroTag: 1,
                child: Icon(Icons.remove_circle_outline),
                onPressed: () {
                  ShowSnackBar().hide();
                },
              ),
              FloatingActionButton(
                heroTag: 2,
                child: Icon(Icons.remove),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          ),
        );
      }
    }
    

    这似乎将大大简化应用程序的设计。仅仅使用
    Scaffold.of
    有什么不对?“为了创建应用程序范围的SnackBar功能”为什么?什么会触发显示快餐店的需要?对于与连接相关的服务器消息,
    SnackBar
    应该显示,而不管当前显示的是什么页面。以正常的方式使用它需要为每个
    Scaffold
    提供者创建不同的方法,这对我来说仍然有点困惑。我尝试了您的代码,并将
    MaterialApp
    指定为
    Provider
    子级,即
    Provider(create:(\u)=>MyErrorChangeNotifier(),child:MaterialApp(…
    ),我现在收到错误,“
    未处理的异常:尝试将Provider与Listenable/Stream(MyErrorChangeNotifier)的子类型一起使用。”.
    ,“建议我使用
    ListenableProvider
    ChangeNotifierProvider
    ValueListenableProvider
    ,或
    StreamProvider
    我通过更改
    提供程序(创建:(\u)=>MyErrorChangeNotifier(),子项:MaterialApp)解决了上一个问题(…
    to
    ChangeNotifierProviderUsing
    ChangeNotifierProvider
    在您的情况下是正确的。当您说什么都没有发生时,您的意思是没有触发自定义支架连接的侦听器吗?尝试在构建
    MaterialApp
    ,并与之一起使用
    ChangeNotifierProvider.value
    构造函数,以确保您没有重建不同的提供程序。如果在
    notifyListeners()上放置断点
    ,你应该能够检查
    这个
    并查看附加了多少侦听器-如果你替换了所有的脚手架,应该有>=1个。我发现它正在工作,只是
    showcoldfold()
    没有做任何事情。使用
    Scaffold
    上下文
    ,通过在
    生成器中包装
    Scaffold
    获得(…
    并将变量设置为其
    上下文
    ,我可以让
    SnackBar
    显示。但是,我发现我没有办法隐藏
    SnackBar
    变更通知程序
    只提供一个可能的通知,没有任何变量。因此,我创建了两个
    变更通知程序
    属性:
    showNot和
    hideNotifier
    ,并使用它们通知侦听器
    class MyScaffold extends Scaffold {
      // TODO constructor
    
      @override
      ScaffoldState createState() => MyScaffoldState();
    }
    
    class MyScaffoldState extends ScaffoldState {
      MyErrorChangeNotifier _myErrorCN;
      Function() _listener;
    
      @override
      void initState() {
        super.initState();
    
        _listener = () {
          if (mounted) {
            showSnackBar(SnackBar(content: Text(_myErrorCN.error)));
          }
        };
    
        Future.microtask(() {
          _myErrorCN = Provider.of<MyErrorChangeNotifier>(context, listen: false)..addListener(_listener);
        });
      }
    
      @override
      void dispose() {
        _myErrorCN?.removeListener(_listener);
    
        super.dispose();
      }
    }
    
    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    
    import 'MScaffold.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Snackbar manager',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Snackbar manager'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return MScaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(),
          floatingActionButton: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              FloatingActionButton(
                heroTag: 0,
                child: Icon(Icons.add_circle_outline),
                onPressed: () {
                  ShowSnackBar().showText("You were on page 1");
                },
              ),
              FloatingActionButton(
                heroTag: 1,
                child: Icon(Icons.remove_circle_outline),
                onPressed: () {
                  ShowSnackBar().hide();
                },
              ),
              FloatingActionButton(
                heroTag: 2,
                child: Icon(Icons.add),
                onPressed: () {
                  Navigator.of(context).push(MaterialPageRoute(builder: (context) {
                    return SecondScaffold();
                  }));
                },
              ),
            ],
          ),
        );
      }
    }
    
    class SecondScaffold extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MScaffold(
          appBar: AppBar(
            title: Text("Page 2"),
          ),
          body: Center(),
          floatingActionButton: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              FloatingActionButton(
                heroTag: 0,
                child: Icon(Icons.add_circle_outline),
                onPressed: () {
                  ShowSnackBar().show(
                    SnackBar(
                      content: Text("You were on page 2"),
                    ),
                  );
                },
              ),
              FloatingActionButton(
                heroTag: 1,
                child: Icon(Icons.remove_circle_outline),
                onPressed: () {
                  ShowSnackBar().hide();
                },
              ),
              FloatingActionButton(
                heroTag: 2,
                child: Icon(Icons.remove),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          ),
        );
      }
    }
    
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/gestures.dart';
    import 'package:flutter/material.dart';
    
    class ShowSnackBar extends ChangeNotifier {
      SnackBar currentSnackBar;
      int lastHideTime = -1; //in millisecondsSinceEpoch
      String _msg;
      bool isSnackBarVisible = false;
      static final _thisClass = ShowSnackBar._internal();
      ShowSnackBar._internal();
      factory ShowSnackBar() {
        return _thisClass;
      }
      ChangeNotifier showNotifier = ChangeNotifier();
      ChangeNotifier hideNotifier = ChangeNotifier();
    
      showText(String inputMsg) {
        _msg = inputMsg;
        currentSnackBar = SnackBar(content: Text(_msg));
        isSnackBarVisible = true;
        showNotifier.notifyListeners();
      }
    
      show(SnackBar inputSnackBar) {
        currentSnackBar = inputSnackBar;
        isSnackBarVisible = true;
        showNotifier.notifyListeners();
      }
    
      hide() {
        hideNotifier.notifyListeners();
      }
    }
    
    class MScaffold extends Scaffold {
      ValueKey key;
      var appBar;
      var body;
      var floatingActionButton;
      var floatingActionButtonLocation;
      var floatingActionButtonAnimator;
      var persistentFooterButtons;
      var drawer;
      var endDrawer;
      var bottomNavigationBar;
      var bottomSheet;
      var backgroundColor;
      var resizeToAvoidBottomPadding;
      var resizeToAvoidBottomInset;
      var primary;
      var drawerDragStartBehavior;
      var extendBody;
      var extendBodyBehindAppBar;
      var drawerScrimColor;
      var drawerEdgeDragWidth;
    
      MScaffold({
        this.key,
        this.appBar,
        this.body,
        this.floatingActionButton,
        this.floatingActionButtonLocation,
        this.floatingActionButtonAnimator,
        this.persistentFooterButtons,
        this.drawer,
        this.endDrawer,
        this.bottomNavigationBar,
        this.bottomSheet,
        this.backgroundColor,
        this.resizeToAvoidBottomPadding,
        this.resizeToAvoidBottomInset,
        this.primary = true,
        this.drawerDragStartBehavior = DragStartBehavior.start,
        this.extendBody = false,
        this.extendBodyBehindAppBar = false,
        this.drawerScrimColor,
        this.drawerEdgeDragWidth,
      })  : assert(primary != null),
            assert(extendBody != null),
            assert(extendBodyBehindAppBar != null),
            assert(drawerDragStartBehavior != null),
            assert(
              !((key!=null
                &&key.value is Map<String,dynamic>)
                &&key.value.length==1
                &&key.value.containsKey('MScaffoldAutoKey')),
              "The Key you use for MScaffold cannot be a Map object that contains only one index "
                "named, 'MScaffoldAutoKey,' as this is reserved for MScaffold."
            ),
            super(key: key) {
        if (key == null) this.key = _autoKeyGen();
      }
    
      static List<Key> _autoKeys = [];
      bool _usesAutoKey = false;
      Key _autoKeyGen() {
        _usesAutoKey = true;
        Key retKey = ValueKey({'MScaffoldAutoKey': _autoKeys.length});
        _autoKeys.add(retKey);
        return retKey;
      }
    
      @override
      MScaffoldState createState() {
        return MScaffoldState(
          key: key,
          appBar: appBar,
          body: body,
          floatingActionButton: floatingActionButton,
          floatingActionButtonLocation: floatingActionButtonLocation,
          floatingActionButtonAnimator: floatingActionButtonAnimator,
          persistentFooterButtons: persistentFooterButtons,
          drawer: drawer,
          endDrawer: endDrawer,
          bottomNavigationBar: bottomNavigationBar,
          bottomSheet: bottomSheet,
          backgroundColor: backgroundColor,
          resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
          resizeToAvoidBottomInset: resizeToAvoidBottomInset,
          primary: primary,
          drawerDragStartBehavior: drawerDragStartBehavior,
          extendBody: extendBody,
          extendBodyBehindAppBar: extendBodyBehindAppBar,
          drawerScrimColor: drawerScrimColor,
          drawerEdgeDragWidth: drawerEdgeDragWidth,
          autoKeys: _usesAutoKey ? _autoKeys : null,
        );
      }
    }
    
    class MScaffoldState extends ScaffoldState {
      Key key;
      var appBar;
      var body;
      var floatingActionButton;
      var floatingActionButtonLocation;
      var floatingActionButtonAnimator;
      var persistentFooterButtons;
      var drawer;
      var endDrawer;
      var bottomNavigationBar;
      var bottomSheet;
      var backgroundColor;
      var resizeToAvoidBottomPadding;
      var resizeToAvoidBottomInset;
      var primary;
      var drawerDragStartBehavior;
      var extendBody;
      var extendBodyBehindAppBar;
      var drawerScrimColor;
      var drawerEdgeDragWidth;
      var autoKeys;
    
      MScaffoldState({
        this.key,
        this.appBar,
        this.body,
        this.floatingActionButton,
        this.floatingActionButtonLocation,
        this.floatingActionButtonAnimator,
        this.persistentFooterButtons,
        this.drawer,
        this.endDrawer,
        this.bottomNavigationBar,
        this.bottomSheet,
        this.backgroundColor,
        this.resizeToAvoidBottomPadding,
        this.resizeToAvoidBottomInset,
        this.primary = true,
        this.drawerDragStartBehavior = DragStartBehavior.start,
        this.extendBody = false,
        this.extendBodyBehindAppBar = false,
        this.drawerScrimColor,
        this.drawerEdgeDragWidth,
        this.autoKeys,
      })  : assert(primary != null),
            assert(extendBody != null),
            assert(extendBodyBehindAppBar != null),
            assert(drawerDragStartBehavior != null);
    
      Function() _listenerShow;
      Function() _listenerHide;
    
      @override
      void initState() {
        super.initState();
        _listenerShow = () {
          if (mounted) {
            Scaffold.of(_scaffoldContext)
                .showSnackBar(ShowSnackBar().currentSnackBar)
                .closed
                .then((SnackBarClosedReason reason) {
              ShowSnackBar().isSnackBarVisible = false;
              ShowSnackBar().lastHideTime = DateTime.now().millisecondsSinceEpoch;
            });
          }
        };
    
        _listenerHide = () {
          if (mounted) {
            Scaffold.of(_scaffoldContext).hideCurrentSnackBar();
          }
        };
    
        Future.microtask(() {
          if (ShowSnackBar().isSnackBarVisible) _listenerShow();
          ShowSnackBar().showNotifier.addListener(_listenerShow);
          ShowSnackBar().hideNotifier.addListener(_listenerHide);
        });
      }
    
      @override
      dispose() {
        ShowSnackBar().showNotifier?.removeListener(_listenerShow);
        ShowSnackBar().hideNotifier?.removeListener(_listenerHide);
        autoKeys?.remove(key);
        super.dispose();
      }
    
      BuildContext _scaffoldContext;
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          key: key,
          appBar: appBar,
          body: Builder(
            builder: (context) {
              _scaffoldContext = context;
              return body;
            },
          ),
          floatingActionButton: floatingActionButton,
          floatingActionButtonLocation: floatingActionButtonLocation,
          floatingActionButtonAnimator: floatingActionButtonAnimator,
          persistentFooterButtons: persistentFooterButtons,
          drawer: drawer,
          endDrawer: endDrawer,
          bottomNavigationBar: bottomNavigationBar,
          bottomSheet: bottomSheet,
          backgroundColor: backgroundColor,
          resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
          resizeToAvoidBottomInset: resizeToAvoidBottomInset,
          primary: primary,
          drawerDragStartBehavior: drawerDragStartBehavior,
          extendBody: extendBody,
          extendBodyBehindAppBar: extendBodyBehindAppBar,
          drawerScrimColor: drawerScrimColor,
          drawerEdgeDragWidth: drawerEdgeDragWidth,
        );
      }
    }