Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do Navigator.popUntil properly when using different animations for each push

Tags:

flutter

I am trying to rebuild iOS app in Flutter, but facing a problem with navigation.

Here what I am trying to do:

  1. List of Added Exchange Pairs with Add button (A screen)
  2. Add button opens Picker with Exchanges (B screen) with transition from bottom to top.
  3. By tapping on exchange it pushes new Picker with Pairs (C screen) with transition from right to left.
  4. when user taps on pair it closes all pickers at once and deliver result of picking to A screen.

I have tried double pop and popUntil but result always same, I see 2 back transitions (left to right and top to bottom) at same time.

How it looks in iOS native app:

How it looks in iOS native app

How it looks in Flutter app:

How it looks in Flutter app

Solved with nested Navigator

Wrapped Screen B with Navigator and used this navigator to push screen C, on screen C used root navigator to pop. Result is below: Final result

like image 530
nonameden Avatar asked Feb 04 '23 00:02

nonameden


2 Answers

Here the example of how I solved it:

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');
        },
      ),
    );
  }
}
like image 104
nonameden Avatar answered Feb 06 '23 14:02

nonameden


When you build your MaterialApp by setting home: and routes: you can achieve "pop to root" without hardcoding what route to pop until by;

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

Because Navigator.defaultRouteName will be set to whatever you set home: to.

Going a bit off-topic but, this is especially nice if you have "variable" home screen, as in using a FutureBuilder to decide what will be the home screen. For example, if you are showing a splash screen until you are loading the initial state from disk.

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();
          }
        },
      ),
like image 23
Eralp Karaduman Avatar answered Feb 06 '23 13:02

Eralp Karaduman