如何为每次推送使用不同的动画时正确执行Navigator.popUntil

时间:2018-04-16 00:18:40

标签: flutter

我正在尝试在Flutter中重建iOS应用,但面临导航问题。

这就是我要做的事情:

  1. 添加按钮(A屏幕)的已添加Exchange对的列表
  2. 添加按钮打开带有交换的Picker(B屏幕),从下到上进行过渡。
  3. 通过点击交换,它会推出新的Picker with Pirs(C 屏幕)从右到左过渡。
  4. 当用户点击一对时,它会立即关闭所有选择器并将选择结果传送到A屏幕。
  5. 我尝试过双重pop和popUntil,但结果总是一样,我同时看到2个后退过渡(从左到右,从上到下)。

    它在iOS原生app中的外观:

    How it looks in iOS native app

    它在Flutter应用程序中的外观:

    How it looks in Flutter app

    使用嵌套导航器解决

    使用Navigator包裹屏幕B并使用此导航器按屏幕C,在屏幕C上使用根导航器弹出。结果如下: Final result

2 个答案:

答案 0 :(得分:2)

这是我如何解决的示例:

import 'package:flutter/material.dart';

void main() {
  MaterialPageRoute.debugEnableFadingRoutes = true;
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _result = "--";

  void _openSubscreen() {
    Navigator.of(context).push<String>(
      new MaterialPageRoute(
        settings: RouteSettings(name: '/subscreen'),
        builder: (context) => SubScreen(),
      ),
    ).then((result) => setState((){
      _result = result;
    }));
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'Result from navigator:',
            ),
            new Text(
              _result,
              textAlign: TextAlign.center,
              style: Theme.of(context).textTheme.headline,
            ),
            SizedBox(height: 32.0,),
            OutlineButton(
              onPressed: _openSubscreen,
              child: Text('Start flow'),
            ),
          ],
        ),
      ),
    );
  }
}

class SubScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: new Navigator(
        onGenerateRoute: (routeSettings) {
          final path = routeSettings.name;
          if (path == '/') {
            return new MaterialPageRoute(
              settings: routeSettings.copyWith(isInitialRoute: true),
              builder: (_) => SubScreenPage1(),
            );
          } else if (path == '/nexpage') {
            return new MaterialPageRoute(
              settings: routeSettings,
              builder: (_) => SubScreenPage2(),
            );
          }
        },
      ),
    );
  }
}

class SubScreenPage1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Center(
      child: OutlineButton(
        child: Text('Next sub page!'),
        onPressed: () {
          Navigator.of(context).pushNamed('/nexpage');
        },
      ),
    );
  }
}

class SubScreenPage2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Center(
      child: OutlineButton(
        child: Text('Deliver result!'),
        onPressed: () {
          final date = DateTime.now().toString();
          Navigator
              .of(context, rootNavigator: true)
              .pop('Delivered at $date');
        },
      ),
    );
  }
}

答案 1 :(得分:1)

通过设置MaterialApphome:构建routes:时,您可以实现“弹出到根目录”,而无需对弹出的路径进行硬编码;

Navigator.popUntil(
  context,
  ModalRoute.withName(Navigator.defaultRouteName),
);

因为Navigator.defaultRouteName将被设置为您设置的home:

有点偏离主题,但是,如果您具有“可变”主屏幕,则这尤其好,例如使用FutureBuilder来决定将要成为主屏幕。例如,如果要显示启动屏幕,直到从磁盘加载初始状态。

home: isUserLoggedIn
    ? HomePage()
    : FutureBuilder(
        future: () async {
          print('Initializing');
          print('Waiting For NoReason');
          await Future.delayed(const Duration(seconds: 1));
          print('Initialization Complete');
        }(),
        builder: (_, snap) {
          if (snap.connectionState == ConnectionState.waiting) {
            return SplashPage();
          } else {
            return LogInPage();
          }
        },
      ),