在中间页中使用PushReplacement时无法弹出值

时间:2020-10-28 19:43:35

标签: flutter

我有一个页面可以推送另一个页面,并等待返回/弹出的值进行处理。我希望第二页能够进行委派(让另一页返回值)。像这样:

// page 1
Navigator.of(context).push(Page2()).then((value) => print(value)); // process value; perhaps setState or whatever

// page 2
Navigator.of(context).pushReplacement(Page3()); // let another page handle the rest

// page 3
Navigator.of(context).pop(99);

但是,第一页上的未来永远不会返回,并且永远不会打印(或以我认为合适的方式处理)值。之所以使用pushReplacement,是因为我不希望用户回到第2页。

这是一个flutter示例应用程序,您可以尝试https://dartpad.dartlang.org/flutter来了解我的意思。

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Page1(),
    );
  }
}

class Page1 extends StatefulWidget {
  @override
  Page1State createState() => Page1State();
}

class Page1State extends State<Page1> {
  List<int> numbers = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            Text('Page 1'),
            ...numbers.map((number) => Text(number.toString())),
            RaisedButton(
              child: Text('go to page 2'),
              onPressed: () {
                Navigator.of(context)
                    .push(MaterialPageRoute(builder: (context) => Page2()))
                    .then((number) {
                  if (number != null) {
                    setState(() {
                      numbers.add(number);
                    });
                  }
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            Text('Ready?'),
            RaisedButton(
              child: Text('Yes'),
              onPressed: () {
                Navigator.of(context).pushReplacement(
                    MaterialPageRoute(builder: (context) => Page3()));
              },
            ),
            RaisedButton(
              child: Text('No'),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ],
        ),
      ),
    );
  }
}

class Page3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            Text('Pick a number'),
            ...[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(
              (number) => RaisedButton(
                child: Text(number.toString()),
                onPressed: () => Navigator.of(context).pop(number),
              ),
            ),
            RaisedButton(
              child: Text('Cancel'),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ],
        ),
      ),
    );
  }
}

我必须弄清楚用例。第一页将从服务器或其他任何设备上获取数据,因此我将使用FutureBuilder或类似的设备。第二页是用于创建/添加其他项目的表单。提交表单,如果服务器对数据满意,则响应将包含新创建的数据。然后,我想用显示数据的成功页面替换当前表单页面。当用户弹出此页面时,我希望使用数据更新第一页的列表。

可以使用Bloc,ValueNotifier等类似的状态管理功能,但是此页面寿命很短,我希望使用尽可能少的样板代码来实现此功能。另外,我发现,如果需要访问其他页面,则需要在MaterialApp上方/之前添加使用这些状态管理类。对于可能深藏在我的应用程序中的页面,我想避免在main中添加Bloc / ValueNotifier /任何内容。只是觉得它太重要了。

2 个答案:

答案 0 :(得分:0)

您可以给pushReplacement一个结果来返回,就像弹出一样。

因此,在这种情况下,您可以做的是再次给它一个将来(例如,使用Completer),以便原始推送小部件可以再次等待其真实结果

void method() async {
  final completer = Completer();
  final result = await navigator.pushReplacement(route, result: completer.future);
  completer.complete(result);
}

答案 1 :(得分:0)

在这种情况下,您不能使用navigator.pushReplacement,因为它会触发Page1中的回调运行,并且该回调将不再可用。回调需要从/通过Page2获取值;只有从Page2中调用Navigator.pop(context)时,才会调用Page1中的'then'。

  1. 首先在Page2中:
// replace this
Navigator.of(context).pushReplacement(Page3());
// to this
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page3()));
  1. 现在您需要弹出2次才能从Page3转到Page1:

    不幸的是,这不可能-只需-使用Navigator.of(context).popUntil。

    A)丑陋且冒险的版本-不需要命名路由

    Navigator.of(context).pop(); 
    Navigator.of(context).pop(number);
    

    也许您也应该在第一个弹出窗口中包含返回值(数字),以确保正确到达。 不确定第二行中的弹出窗口是否会在此解决方案之后结束!

    B)也要在第2页中等待导航响应(如第1页),然后在第3页中弹出一次,然后在第2页中-为此不需要命名路由

    也许您可以实现以下内容(即使使用命名的路由):Flutter popuntil with return data

    C)将Page1设置为命名路线,并使用路线设置传递数据

    (由于渲染效果不佳,我在代码中将mainAxisAlignment参数设置了几个位置)

    import 'package:flutter/material.dart';
    
    final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
          debugShowCheckedModeBanner: false,
          home: Page1(),
          initialRoute: '/page1',
          onGenerateRoute: (settings) {
            switch (settings.name) {
              case '/page1':
                return MaterialPageRoute(
                  settings: RouteSettings(name: '/page1', arguments: Map()),
                  builder: (_) => Page1(),
                );
              default:
                return null;
            }
          },
        );
      }
    }
    
    class Page1 extends StatefulWidget {
      @override
      Page1State createState() => Page1State();
    }
    
    class Page1State extends State<Page1> {
      List<int> numbers = [];
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('Page 1'),
                ...numbers.map((number) => Text(number.toString())),
                RaisedButton(
                  child: Text('go to page 2'),
                  onPressed: () {
                    Navigator.of(context).push(MaterialPageRoute(builder: (_) => Page2())).then((_) {
                      final Map arguments = ModalRoute.of(context).settings.arguments as Map;
                      final int number = arguments['number'] ?? null;
                      if (number != null) {
                        setState(() {
                          numbers.add(number);
                        });
                      }
                    });
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class Page2 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('Ready?'),
                RaisedButton(
                  child: Text('Yes'),
                  onPressed: () {
                    Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page3()));
                  },
                ),
                RaisedButton(
                  child: Text('No'),
                  onPressed: () => Navigator.of(context).pop(),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class Page3 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('Pick a number'),
                ...[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(
                      (number) => RaisedButton(
                    child: Text(number.toString()),
                    onPressed: () {
                      Navigator.of(context).popUntil((route) {
                        if (route.settings.name == '/page1') {
                          (route.settings.arguments as Map)['number'] = number;
                          return true;
                        } else {
                          return false;
                        }
                      });
                    },
                  ),
                ),
                RaisedButton(
                  child: Text('Cancel'),
                  onPressed: () => Navigator.of(context).pop(),
                ),
              ],
            ),
          ),
        );
      }
    }
    

其他选项1

尽可能使用命名的路由,并使用整体模式提供者模式实施一些更高级别,更严格的路由,状态,事件处理功能。

现在要花很长时间来解释这些,只是寻找这些。

其他选项2

无论如何,在我看来,这似乎是一个xy问题,一个设计错误。整个问题似乎源于设计缺陷。如果给定的示例代码不是仅为测试而创建的,那么我认为您应该用flutter dialog代替Page2,否则您有很多其他机会可以避免陷入这种极端情况。