Flutter真棒通知如何修复StateError(坏状态:流已被侦听,)

eqoofvh9  于 2023-02-09  发布在  Flutter
关注(0)|答案(1)|浏览(147)

当我退出flutter应用程序并尝试再次登录时,我收到此错误:

状态错误(错误状态:流已被收听。)

给我这个错误的代码在我的第一页:

@override
  void initState() {
    AwesomeNotifications().actionStream.listen((notification) async {
      if (notification.channelKey == 'scheduled_channel') {
        var payload = notification.payload['payload'];
        var value = await FirebaseFirestore.instance
            .collection(widget.user.uid)
            .doc(payload)
            .get();
        navigatorKey.currentState.push(PageRouteBuilder(
            pageBuilder: (_, __, ___) => DetailPage(
                user: widget.user,
                i: 0,
                docname: payload,
                color: value.data()['color'].toString(),
                createdDate: int.parse((value.data()['date'].toString())),
                documentId: value.data()['documentId'].toString(),)));
      }
    });
    super.initState();
  }

和包含注销代码的另一页上。

await FirebaseAuth.instance.signOut();
 if (!mounted) return;
 Navigator.pushNamedAndRemoveUntil(context,
 "/login", (Route<dynamic> route) => false);

我能做些什么来解决这个问题?当我注销时是否可以停止收听actionstream?或者我应该用另一种方法来做?

0kjbasz6

0kjbasz61#

流是一次性使用的,它们取代了UI的回调,一开始一次性使用的流看起来毫无用处,但这可能是缺乏远见。总之(至少对我来说)flutter提供了所有必要的小部件来避免流的混乱,你可以在ChangeNotifierImplementers 部分找到它们,所有这些都实现了TextEditingController之类的其他小部件。
因此,理想的做法(同样,至少对我来说)是将小部件视为集群,其中流只是将它们捆绑在一个用例中,例如,小部件StreamBuilder被设计为按需 * 构建 *,因此它只需要一些东西来泵送更改,以形成一个 “活对象”,就像在时钟中一样,一个周期函数向流添加新值,小部件只需要侦听和更新。
为了解决你的问题,你可以让.actionStream适合你正在使用它的情况,或者稍微改变一下你是如何使用它的(有一个猴子补丁是不好的,但你决定是否值得)。
这个例子并不完全是一个 “这是什么是错误的,修复它”,它更多的是展示如何使用pushNamedAndRemoveUntilStreamSubscription可以得到实现。我也使用了InheritedWidget,只是因为它在这种情况下是如此有用。有一件事你应该检查多一点是变量count不停止递增时,route_a是焦点。流是独立的,只要小部件还在,它就将是活动的,在您的情况下,重建侦听小部件是错误的。

import 'dart:async';
import 'package:flutter/material.dart';

void main() => runApp(App());

const String route_a = '/route_a';
const String route_b = '/route_b';
const String route_c = '/route_c';

class App extends StatelessWidget {
  Stream<int> gen_nums() async* {
    while (true) {
      await Future.delayed(Duration(seconds: 1));
      yield 1;
    }
  }

  @override
  Widget build(BuildContext ctx) {
    return ReachableData(
      child: MaterialApp(
        initialRoute: route_a,
        routes: <String, WidgetBuilder>{
          route_a: (_) => Something(stream: gen_nums()),
          route_b: (_) => FillerRoute(),
          route_c: (_) => SetMount(),
        },
      ),
    );
  }
}

class ReachableData extends InheritedWidget {
  final data = ReachableDataState();

  ReachableData({super.key, required super.child});

  static ReachableData of(BuildContext ctx) {
    final result = ctx.dependOnInheritedWidgetOfExactType<ReachableData>();
    assert(result != null, 'Context error');
    return result!;
  }

  @override
  bool updateShouldNotify(ReachableData old) => false;
}

class ReachableDataState {
  String? mount;
}

// route a
class Something extends StatefulWidget {
  // If this widget needs to be disposed then use the other
  // constructor and this call in the routes:
  // Something(subscription: gen_nums().listen(null)),

  // final StreamSubscription<int> subscription;
  // Something({required this.subscription, super.key});

  final Stream<int> stream;
  Something({required this.stream, super.key});

  @override
  State<Something> createState() => _Something();
}

class _Something extends State<Something> {
  int count = 0;

  void increment_by(int i) => setState(
        () => count += i,
      );

  @override
  void initState() {
    super.initState();
    widget.stream.listen(increment_by);
    // To avoid any funny errors you should set the subscription
    // on pause or the callback to null on dispose
    // widget.subscription.onData(increment_by);
  }

  @override
  Widget build(BuildContext ctx) {
    var mount = ReachableData.of(ctx).data.mount ?? 'No mount';
    return Scaffold(
      body: InkWell(
        child: Text('[$count] Push Other / $mount'),
        onTap: () {
          ReachableData.of(ctx).data.mount = null;
          Navigator.of(ctx).pushNamed(route_b);
        },
      ),
    );
  }
}

// route b
class FillerRoute extends StatelessWidget {
  const FillerRoute({super.key});

  @override
  Widget build(BuildContext ctx) {
    return Scaffold(
      body: InkWell(
        child: Text('Go next'),
        // Option 1: go to the next route
        // onTap: () => Navigator.of(ctx).pushNamed(route_c),

        // Option 2: go to the next route and extend the pop
        onTap: () => Navigator.of(ctx)
            .pushNamedAndRemoveUntil(route_c, ModalRoute.withName(route_a)),
      ),
    );
  }
}

// route c
class SetMount extends StatelessWidget {
  const SetMount({super.key});

  @override
  Widget build(BuildContext ctx) {
    return Scaffold(
      body: InkWell(
        child: Text('Set Mount'),
        onTap: () {
          ReachableData.of(ctx).data.mount = 'Mounted';
          // Option 1: pop untill reaches the correct route
          // Navigator.of(ctx).popUntil(ModalRoute.withName(route_a));

          // Option 2: a regular pop
          Navigator.of(ctx).pop();
        },
      ),
    );
  }
}

相关问题