【问题标题】:Bring screen to the top of navigator将屏幕带到导航器的顶部
【发布时间】:2019-06-22 22:05:42
【问题描述】:

我已经搜索了一段时间,但找不到任何选项来执行此操作。这甚至可能吗?

在以下场景中

Page1 > Page2 > Page3 > Page4

我可以从 Page4 导航到 Page2 而不创建新的 Page2 吗?

想要的结果:

Page1 >??empty?? > Page3 > Page4 > Page2

正常结果:

Page1 > Page2 > Page3 > Page4 > different Page2

这甚至可以通过颤振实现吗?

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    是的,您可以从第 4 页返回到第 2 页

    Navigator.popUntil(context, ModalRoute.withName("/screen2"));
    

    【讨论】:

    • 是的,但如果我这样做,它只留下 Page1 > Page2 ,因为 Page3 和 Page4 被“弹出”[删除]。 ;-;
    【解决方案2】:

    好的,过了一会儿我找到了答案。

    理想的答案应该有一些先决条件

    • 它应该在被推到顶部时保持它的状态
    • 应该可以从应用中的任何地方推送
    • 应该是同一个屏幕 [不是重建的]
    • 应该可以有自定义过渡
    • 不应以任何方式卸载 [状态丢失]

    还有最重要的一个

    • 它应该能够到达任何导航器的顶部,但它不应该停留在导航器树中。

    解决办法

    应用根 Scaffold 中的背景小部件

    寻找这个我找到了this背景的中篇文章。

    为了我个人的使用,我删除了滑动功能,并添加了一个后退按钮处理程序(因此它充当导航器中的屏幕)。

    import 'package:flutter/material.dart';
    import 'package:meta/meta.dart';
    
    const _kFlingVelocity = 2.0;
    
    class Backdrop extends StatefulWidget {
      final Widget frontLayer;
      final Widget backLayer;
      final ValueNotifier<bool> panelVisible;
    
      Backdrop(
          {@required this.frontLayer, @required this.backLayer, this.panelVisible})
          : assert(frontLayer != null),
            assert(backLayer != null);
    
      @override
      createState() => _BackdropState();
    }
    
    class _BackdropState extends State<Backdrop>
        with SingleTickerProviderStateMixin {
      final _backdropKey = GlobalKey(debugLabel: 'Backdrop');
      AnimationController _controller;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
          duration: Duration(milliseconds: 300),
          value: (widget.panelVisible?.value ?? true) ? 1.0 : 0.0,
          vsync: this,
        );
    
        widget.panelVisible?.addListener(_subscribeToValueNotifier);
        if (widget.panelVisible != null) {
          _controller.addStatusListener((status) {
            if (status == AnimationStatus.completed)
              widget.panelVisible.value = true;
            else if (status == AnimationStatus.dismissed)
              widget.panelVisible.value = false;
          });
        }
      }
    
      void _subscribeToValueNotifier() {
        if (widget.panelVisible.value != _backdropPanelVisible)
          _toggleBackdropPanelVisibility();
      }
    
      @override
      void didUpdateWidget(Backdrop oldWidget) {
        super.didUpdateWidget(oldWidget);
        oldWidget.panelVisible?.removeListener(_subscribeToValueNotifier);
        widget.panelVisible?.addListener(_subscribeToValueNotifier);
      }
    
      @override
      void dispose() {
        _controller.dispose();
        widget.panelVisible?.dispose();
        super.dispose();
      }
    
      bool get _backdropPanelVisible =>
          _controller.status == AnimationStatus.completed ||
          _controller.status == AnimationStatus.forward;
    
      void _toggleBackdropPanelVisibility() => _controller.fling(
          velocity: _backdropPanelVisible ? -_kFlingVelocity : _kFlingVelocity);
    
      Future<bool> _onWillPop() async {
        if (widget.panelVisible.value) {
          widget.panelVisible.value = false;
          return false;
        }
    
        return true;
      }
    
      @override
      Widget build(BuildContext context) {
        return LayoutBuilder(builder: (context, constraints) {
          final panelDetailsPosition = Tween<Offset>(
            begin: Offset(0.0, 1.0),
            end: Offset(0.0, 0.0),
          ).animate(_controller.view);
    
          return WillPopScope(
              onWillPop: _onWillPop,
              child: Container(
                key: _backdropKey,
                child: Stack(
                  children: <Widget>[
                    widget.backLayer,
                    SlideTransition(
                        position: panelDetailsPosition, child: widget.frontLayer),
                  ],
                ),
              ));
        });
      }
    }
    

    我在全球商店有final frontPanelVisible = ValueNotifier&lt;bool&gt;(false);,所以我可以从应用程序的任何地方调用它并“推动”这个传送的魔法屏幕。

    在我的应用程序的主小部件中,我有这样的东西

    class Panels extends StatelessWidget {
      final frontPanelVisible = ValueNotifier<bool>(false);
    
      @override
      Widget build(BuildContext context) {
        return Backdrop(
          frontLayer: MainAppScreen(),
          backLayer: MagicalScreen(),
          panelVisible: frontPanelVisible,
        );
      }
    }
    

    在层次结构中跳转非常神奇,而且,它节省了大量资源 [因为我只有 1 个相同内容的屏幕,而不是 15 个屏幕],也许稍微修改一下,我可以在那里有一个嵌套的导航器.无限可能。

    【讨论】:

    • 需要注意的是,您也可以嵌套导航器,并拥有一个带屏幕的导航器和另一个导航器。但是,由于我只想要导航规则之外的 1 个屏幕,我发现使用背景更有用。检查stackoverflow.com/questions/46502751/…
    猜你喜欢
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多