如何在Navigator.of(context).push(...)之后暂时取消订阅Stream?

问题描述 投票:0回答:1

场景:如何在Navigator.of(context).push(...)后暂时取消订阅? : 有两页。PhonePage 和:有两页。OtpPage . 用户输入电话号码 PhonePage 并被重定向到 OtpPage 来验证发给他的OTP。

问题: 与服务器对话的API使用的是 StreamController.broadcast() 来告诉应用程序该请求的响应。这个流由两个 PhonePageOtpPage 并产生事件。这两个页面会监听流,并根据事件决定做什么。

然而,在 Navigator.push(),旧页面仍在监听流。因此,当用户在 OtpPage 点击重新发送按钮。Navigator.pushPhonePage 虽然不应该被调用,但仍然被调用。

疑问 Flutter有什么处理这种情况?我试过 onDispose() 但它不被调用。如果你能解释为什么我也会感激 onDispose 也不被调用。

编码这是重现该场景的代码。你可以把它粘贴在你的IDE或DartPad上。https:/dartpad.devflutter (注:当你进入 OtpPage 和一些文本字段上的文字,点击重新发送按钮。请注意,一个新的 OtpPage 小部件被添加到导航树的顶部。这就是不受欢迎的行为)

import 'dart:async';

import 'package:flutter/material.dart';

final fakeApiResponse = StreamController.broadcast();

void main() => runApp(MyApp(),);

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PhoneNumber(),
    );
  }
}

class PhoneNumber extends StatefulWidget {
  @override
  _PhoneNumberState createState() => _PhoneNumberState();
}

class _PhoneNumberState extends State<PhoneNumber> {
  StreamSubscription apiEventListner;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Text('Enter your phone number'),
          RaisedButton(
            child: Text('Send OTP'),
            onPressed: () {
              fakeApiResponse.add('OTP Sent');
            },
          ),
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    apiEventListner = fakeApiResponse.stream.listen((data) {
      if (data == 'OTP Sent') {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => VerifyOtp(),
          ),
        );
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    apiEventListner.cancel();
  }
}

class VerifyOtp extends StatefulWidget {
  @override
  _VerifyOtpState createState() => _VerifyOtpState();
}

class _VerifyOtpState extends State<VerifyOtp> {
  StreamSubscription apiEventListner;


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          TextField(
            decoration: InputDecoration(hintText: 'Enter OTP Here'),
          ),
          RaisedButton(
            child: Text("Verify"),
            onPressed: () {
              fakeApiResponse.add('OTP Verified');
            },
          ),
          RaisedButton(
            child: Text("Didn't get the code? Resend OTP"),
            onPressed: () {
              fakeApiResponse.add('OTP Sent');
            },
          ),
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    apiEventListner = fakeApiResponse.stream.listen((data) {
      if (data == 'OTP Sent') {
        // show the dialog
        showDialog(
          context: context,
          builder: (BuildContext context) {
          return  AlertDialog(
              title: Text("OTP Resent"),
              content: Text("Enter new OTP"),

            );
          },
        );
      }else if (data == 'OTP Verified'){
        Navigator.of(context).push(MaterialPageRoute(builder: (context)=>SuccessPage()));
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    apiEventListner.cancel();
  }
}

class SuccessPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('SUCCESS!'),
      ),
    );
  }
}
flutter dart stream
1个回答
0
投票

我找到了解决方案。我打算在这里发布任何方式,以防止它可能对其他人有帮助。

诀窍是检测当前的活动路由,如果当前的widget不是当前的路由,则返回流的监听器里面。

Flutter有一个API叫做 ModalRoute route = ModalRoute.of(context); 然后 route.isCurrent 将为真,如果当前的widget是当前的路由。

然后你必须在 两者 页。

最后的工作守则将是:

import 'dart:async';

import 'package:flutter/material.dart';

final fakeApiResponse = StreamController.broadcast();

void main() => runApp(
      MyApp(),
    );

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PhoneNumber(),
    );
  }
}

class PhoneNumber extends StatefulWidget {
  @override
  _PhoneNumberState createState() => _PhoneNumberState();
}

class _PhoneNumberState extends State<PhoneNumber> {
  StreamSubscription apiEventListner;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Text('Enter your phone number'),
          RaisedButton(
            child: Text('Send OTP'),
            onPressed: () {
              fakeApiResponse.add('OTP Sent');
            },
          ),
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    apiEventListner = fakeApiResponse.stream.listen((data) {
      ModalRoute route = ModalRoute.of(context);
      String name = route?.settings?.name;
      print("Phone page isCurrent: ${route?.isCurrent} isFirst: ${route?.isFirst} active: ${route?.isActive} $name");
      if (route?.isCurrent != null && !route.isCurrent) {
        return;
      }
      if (data == 'OTP Sent') {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => VerifyOtp(),
          ),
        );
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    apiEventListner.cancel();
  }
}

class VerifyOtp extends StatefulWidget {
  @override
  _VerifyOtpState createState() => _VerifyOtpState();
}

class _VerifyOtpState extends State<VerifyOtp> {
  StreamSubscription apiEventListner;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          TextField(
            decoration: InputDecoration(hintText: 'Enter OTP Here'),
          ),
          RaisedButton(
            child: Text("Verify"),
            onPressed: () {
              fakeApiResponse.add('OTP Verified');
            },
          ),
          RaisedButton(
            child: Text("Didn't get the code? Resend OTP"),
            onPressed: () {
              fakeApiResponse.add('OTP Sent');
            },
          ),
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    apiEventListner = fakeApiResponse.stream.listen((data) {
      ModalRoute route = ModalRoute.of(context);
      String name = route?.settings?.name;
      print("OTP page isCurrent: ${route?.isCurrent} isFirst: ${route?.isFirst} active: ${route?.isActive} $name");
      if (route?.isCurrent != null && !route.isCurrent) {
        return;
      }
      if (data == 'OTP Sent') {
        // show the dialog
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text("OTP Resent"),
              content: Text("Enter new OTP"),
            );
          },
        );
      } else if (data == 'OTP Verified') {
        Navigator.of(context).push(MaterialPageRoute(builder: (context) => SuccessPage()));
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    apiEventListner.cancel();
  }
}

class SuccessPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('SUCCESS!'),
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.