【问题标题】:How to temporarily unsubscribe to Stream after Navigator.of(context).push(....)?如何在 Navigator.of(context).push(....) 之后临时取消订阅 Stream?
【发布时间】:2020-06-05 02:44:48
【问题描述】:

场景:有两个页面。 PhonePageOtpPage 。用户在PhonePage 中输入电话号码并被重定向到OtpPage 以验证发送给他的OTP。

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

但是,Navigator.push() 之后,旧页面仍在监听流。因此,当OtpPage 中的用户点击重新发送按钮时,PhonePage 中的Navigator.push 仍会被调用,尽管它不应该调用。

问题 Flutter 必须如何处理这种情况?我试过onDispose(),但它没有被调用。 如果您也能解释为什么也没有调用 onDispose,我将不胜感激。

代码:这是重现场景的代码。您可以将其粘贴到您的 IDE 或 DartPad https://dartpad.dev/flutter (注​​意:当您转到 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!'),
      ),
    );
  }
}

【问题讨论】:

  • 我无权访问 Api 代码,因为它是一个库,因此我无法控制响应事件名称。 Api 只知道一件事:Send Otp 将响应 Otp Sent。它没有重新发送 Otp 的概念。您只需再次调用 Send Otp 即可重新发送它

标签: flutter dart stream


【解决方案1】:

我找到了解决方案。无论如何我都会在这里发布它,以防它可能对其他人有帮助。

诀窍是检测当前活动路由,如果当前小部件不是 Streams 侦听器内的当前路由,则返回。

Flutter 有一个名为 ModalRoute route = ModalRoute.of(context); 的 API,如果当前小部件是当前路由,route.isCurrent 将为真。

然后您必须在两个页面中添加此检查。

最终的工作代码是:

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!'),
      ),
    );
  }
}

【讨论】:

    猜你喜欢
    • 2018-05-08
    • 2020-08-25
    • 2015-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多