Flutter导航器 - 从页面栈中交换页面

8

我的情况有点复杂。我正在将2个页面推送到导航器(navigator)。

A - B

B是最新推送的页面。

由于某些原因,我不想通过从B弹出来实现从B到A的导航。我希望替换/交换它们。这是由于状态(state)的原因。我想保留A和B页面的状态,因为有一些构建成本极高的widget(Unity view widget)。我该如何在B页面中使用Navigator才能在不销毁我的页面的情况下将B与A交换?

以下是步骤:

  • A作为默认页面打开。

  • B通过

    Navigator.of(context).pushNamed();

    被推送。页面堆栈(Page Stack)为A-B。

  • 从B页面,我想导航到A,同时仍然保持B页面处于活动状态。

  • - 在某些神奇的操作之后,我缺少 -

  • 页面堆栈变为B-A。用户看到的是页面A。没有一个页面被销毁。它们被交换了。

在Flutter中是否有一种方法可以实现这个功能?


1
使用Navigator 2.0吗?(我指的是MaterialApp.router - pskink
看起来太复杂了,而且我的应用程序已经设置了很多路由。这将是最后的选择。感谢您的建议。 - cs guy
1
据我所知,您也可以混合使用它们,请参见https://docs.google.com/document/d/1Q0jx0l4-xymph9O6zLaOY4d_f7YFpNWX_eGbzYxr9wY/edit#heading=h.ririyyxrggqr和^F“无页路由”。 - pskink
我真的不认为在Navigator 2.0中这是可能的,我有什么遗漏吗? - cs guy
你找到解决方案了吗?@csguy - Johanna
3个回答

3

您可以使用 PageView 来简单地设置配置,像这样:

PageController _pagerController = PageController(keepPage: true);

PageView(
      physics: NeverScrollableScrollPhysics(),
      controller: _pagerController,
      children: [
         Page_A(),
         Page_B(),
      ],
),

用于页面之间的切换:

goNextPage() {
  _pagerController.animateToPage(
    currentPage + 1,
    duration: Duration(milliseconds: 500),
    curve: Curves.ease,
  );
}

goPrevPage() {
  _pagerController.animateToPage(
    currentPage - 1,
    duration: Duration(milliseconds: 500),
    curve: Curves.ease,
  );
}

希望对您有用。


这是一个聪明的解决方案,但PageView是否确保不可见的小部件处于暂停状态?我的意思是,我认为当您使用导航器推送新页面时,旧页面不会占用太多内存。使用这种PageView方法,每个页面都仍然在内存中处于活动状态,对吗? - cs guy
是的,两个页面都是活动的。但这取决于您使用的数据类型,如果数据像类模型一样简单,您可以将模型存储在静态字段中,并在用户单击切换到第二页时使用此模型完成Page_B,但如果所有Page_B都很重要,例如滚动、填充文本字段等,简单的方法是使用“PageView”。 - Saeed Fekri
这个缺乏自定义动画,例如淡入淡出,而这对于页面切换非常重要 :( - cs guy
这是您自定义的内容!您可以使用许多转换效果!例如:"https://medium.com/flutter-community/a-deep-dive-into-pageview-in-flutter-with-custom-transitions-581d9ea6dded" 或 "https://pub.dev/packages/transformer_page_view" 或 "https://dev59.com/cbzpa4cB1Zd3GeqPIE17" :( - Saeed Fekri
不是因为它不允许自定义动画。我想要完全控制动画。不是预定义的动画。我希望与导航器动画相同。 - cs guy

0

也许 IndexedStack 是你正在寻找的。它像电视频道一样在小部件之间切换。在更改时,小部件不会被重建。

根据您的需求,使用动画切换页面,AnimatedSwitcher 似乎是最接近您想要的,除了它会重建小部件。因此,我认为 IndexedStack 是你想要的一个好的开始。

你还可以参考 animated_indexed_stack.dart带有 IndexedStack 的 AnimatedSwitcher这个线程 中提到。


我想保留动画效果。 - cs guy
我会更新答案,并提供一些其他人如何实现带动画的IndexedStack的示例。 - yellowgray
我无法承担重建小部件的成本,因为每次重建都需要大约3秒钟才能激活Unity。我在启动画面中创建了这个小部件,并将其保留在整个应用程序中。 - cs guy

-1

PageView、ListView和GridView构建器专为大量对象设计,并在滚动时重新构建。

Column或Row保存所有子构建。

您可以尝试:

SingleChildScrollView(NeverScrollable)-> Row -> A(相同屏幕大小)和B(相同)

然后动画或跳转到:

对于A偏移0,对于B偏移屏幕宽度。

我已经尝试过,效果很好。

enter image description here

    
///
class AtoBExample extends StatefulWidget {
  @override
  _AtoBExampleState createState() => _AtoBExampleState();
}

class _AtoBExampleState extends State<AtoBExample> {
  ScrollController scrollController = ScrollController();

  _toB() {
    // !!! Forward or any operations animations in here
    // e.g animate A's opacity 1 to 0 and wait finish

    scrollController.jumpTo(MediaQuery.of(context).size.width);

    // And animate B's opacity 0 to 1
  }

  _toA() {
    // !!! Forward or any operations animations in here
    // e.g animate B's opacity 1 to 0 and wait finish

    scrollController.jumpTo(0);

    // And animate A's opacity 0 to 1
  }

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return Scaffold(
      body: SingleChildScrollView(
        controller: scrollController,
        scrollDirection: Axis.horizontal,
        physics: const NeverScrollableScrollPhysics(),
        child: Row(
          children: [
            // A
            Builder(
              builder: (c) {
                print('GREEN BUILD');
                return InkWell(
                  onTap: (){
                    print('TO B');
                    _toB();
                  },
                  child: Container(
                    width: size.width,
                    height: size.height,
                    color: Colors.green,
                  ),
                );
              },
            ),
            Builder(
              builder: (c) {
                print('RED BUILD');
                return InkWell(
                  onTap: (){
                    print('TO A');
                    _toA();
                  },
                  child: Container(
                    width: size.width,
                    height: size.height,
                    color: Colors.red,
                  ),
                );
              },
            )
          ],
        ),
      ),
    );
  }
}

在发布回答之前,请阅读其他答案中的评论。我已经说过“这缺乏自定义动画,例如淡入淡出,这对于页面切换非常重要:(”,这也适用于您的答案。 - cs guy
@csguy 抱歉。但我想补充一下:对于淡入淡出动画,请使用Transform.opacity()包装一个小部件,并使用动画值定义不透明度。将此动画转发到(例如)_toA方法中。如果您使用此方法,则应使用jumpTo而不是animateTo。 - Mehmet Yaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接