替换MaterialApp中的初始路线而不动画?

问题描述 投票:21回答:4

我们的应用程序建立在Scaffold之上,到目前为止,我们已经能够使用NavigatorStatepushNamed()pushReplacementNamed()等)中提供的调用来满足我们的大部分路由和导航要求。我们不想要的是,当用户从我们的抽屉(导航)菜单中选择一个项目时,可以使用任何类型的“推送”动画。我们希望从导航菜单中单击目标屏幕,以有效地成为堆栈的新初始路径。目前我们正在使用pushReplacementNamed()来确保应用栏中没有后退箭头。但是,右下方的幻灯片意味着正在构建堆栈。

在没有动画的情况下更改初始路线的最佳选择是什么?我们可以这样做同时同时关闭抽屉的动画吗?或者我们是否正在考虑这样的情况:我们需要从导航器转移到仅使用单个脚手架并在用户想要更换屏幕时直接更新“正文”?

我们注意到在replace()上有一个NavigatorState调用,我们认为这可能是开始寻找的正确位置,但目前还不清楚如何访问最初在new MaterialApp()中建立的各种路线。像replaceNamed()这样的东西可能是有序的;-)

dart flutter
4个回答
45
投票

你在做什么听起来有点像BottomNavigationBar,所以你可能想要考虑其中一个而不是Drawer

但是,当用户点击抽屉项目时,拥有单个Scaffold并更新body是一种非常合理的方法。您可能会考虑将FadeTransition从一个身体转变为另一个身体。

或者,如果您喜欢使用Navigator但不想使用默认的幻灯片动画,则可以通过扩展MaterialPageRoute来自定义(或禁用)动画。这是一个例子:

import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

class MyCustomRoute<T> extends MaterialPageRoute<T> {
  MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
      : super(builder: builder, settings: settings);

  @override
  Widget buildTransitions(BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child) {
    if (settings.isInitialRoute)
      return child;
    // Fades between routes. (If you don't want any animation, 
    // just return child.)
    return new FadeTransition(opacity: animation, child: child);
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Navigation example',
      onGenerateRoute: (RouteSettings settings) {
        switch (settings.name) {
          case '/': return new MyCustomRoute(
            builder: (_) => new MyHomePage(),
            settings: settings,
          );
          case '/somewhere': return new MyCustomRoute(
            builder: (_) => new Somewhere(),
            settings: settings,
          );
        }
        assert(false);
      }
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Navigation example'),
      ),
      drawer: new Drawer(
        child: new ListView(
          children: <Widget> [
            new DrawerHeader(
              child: new Container(
                  child: const Text('This is a header'),
              ),
            ),
            new ListTile(
              leading: const Icon(Icons.navigate_next),
              title: const Text('Navigate somewhere'),
              onTap: () {
                Navigator.pushNamed(context, '/somewhere');
              },
            ),
          ],
        ),
      ),
      body: new Center(
        child: new Text(
          'This is a home page.',
        ),
      ),
    );
  }
}

class Somewhere extends StatelessWidget {
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Text(
          'Congrats, you did it.',
        ),
      ),
      appBar: new AppBar(
        title: new Text('Somewhere'),
      ),
      drawer: new Drawer(
        child: new ListView(
          children: <Widget>[
            new DrawerHeader(
              child: new Container(
                child: const Text('This is a header'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

3
投票

感谢你提供非常有用的答案,科林!

一个可能有用的小mod:由于其他原因,当我的屏幕(Scaffold)进来时,我想要一个淡入动画,但它是在堆栈上,当我外出时我想要一个常规的滑动,因为它与Navigator.pushReplacement(...)一起添加。非常容易实现这一点:

class FadeInSlideOutRoute<T> extends MaterialPageRoute<T> {
  FadeInSlideOutRoute({WidgetBuilder builder, RouteSettings settings})
      : super(builder: builder, settings: settings);

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    if (settings.isInitialRoute) return child;
    // Fades between routes. (If you don't want any animation,
    // just return child.)
    if (animation.status == AnimationStatus.reverse)
      return super.buildTransitions(context, animation, secondaryAnimation, child);
    return FadeTransition(opacity: animation, child: child);
  }
}

3
投票

你也可以使用PageRouteBuilder

例:

@override
Widget build(BuildContext context) {
  return RaisedButton(
    onPressed: () {
      Navigator.push(
        context,
        PageRouteBuilder(
          pageBuilder: (context, anim1, anim2) => SecondScreen(),
          transitionsBuilder: (context, anim1, anim2, child) => FadeTransition(opacity: anim1, child: child),
          transitionDuration: Duration(seconds: 1),
        ),
      );
    },
  );
}

如果您不想要任何动画,请将transitionsBuilder替换为以上

transitionsBuilder: (context, anim1, anim2, child) => Container(child: child),

0
投票

要在显示路径时阻止动画,请将RouteSettings(isInitialRoute: true)传递给PageRoute构造函数。

this answer with working example and screen recording

© www.soinside.com 2019 - 2024. All rights reserved.