You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter 多页面向主页StreamController发送数据并监听的实现问题

问题根因

你在二级、三级页面调用的MainAppState()是全新创建的State实例,和当前挂载在路由栈里的主页MainAppState完全不是同一个对象,对应的StreamController也是独立的新实例,往这个新实例的sink里加数据,原来主页监听的旧StreamController当然收不到任何消息。构造函数传参只适合两页面线性交互,多页面共享事件的场景直接用全局单例Stream就可以实现任意页面发数据、多页面统一监听的需求,完全匹配你多页面加购、购物车页统一展示的业务场景。


修复实现

1. 改造Stream工具类为单例

stream.dart 代码修改如下:

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

class MyStream {
  // 全局单例实例
  static final MyStream _instance = MyStream._internal();
  // 工厂构造函数,所有调用方拿到的都是同一个实例
  factory MyStream() => _instance;
  MyStream._internal();

  // 广播流支持多页面同时监听,指定泛型避免动态类型风险
  final StreamController<String> controller = StreamController<String>.broadcast();

  String setNum(String data) {
    controller.sink.add(data);
    debugPrint("Count $data");
    return data;
  }

  // App退出时可调用释放资源
  void dispose() {
    controller.close();
  }
}

2. 主页代码调整

main.dart 不需要手动创建MyStream实例,直接获取全局单例监听即可:

import 'dart:math';
import 'package:flutter/material.dart';
// 引入你自己的stream.dart、second.dart、third.dart路径

class MainApp extends StatefulWidget {
  const MainApp({Key? key}) : super(key: key);

  @override
  State<MainApp> createState() => MainAppState();
}

class MainAppState extends State<MainApp> {
  String streamNum = "0";
  final MyStream streamNumber = MyStream();
  StreamSubscription? _streamSub;

  @override
  void initState() {
    super.initState();
    _streamSub = streamNumber.controller.stream.listen((event) {
      setState(() {
        streamNum = event;
      });
    });
  }

  @override
  void dispose() {
    // 取消本页监听避免内存泄漏
    _streamSub?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            streamNum,
            style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                  onPressed: () {
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (BuildContext context) =>
                                const SecondPage()));
                  },
                  child: const Text("Second Page")),
              const SizedBox(
                width: 20,
              ),
              ElevatedButton(
                  onPressed: () {
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (BuildContext context) =>
                                const ThirdPage()));
                  },
                  child: const Text("Third Page"))
            ],
          )
        ],
      ),
    ));
  }
}

3. 二级、三级页面代码调整

不需要再获取主页State实例,直接调用全局单例发数据即可。
second.dart 代码:

import 'dart:math';
import 'package:flutter/material.dart';
// 引入你自己的stream.dart路径

class SecondPage extends StatelessWidget {
  const SecondPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                  onPressed: () {
                    String num = Random().nextInt(20).toString();
                    MyStream().setNum(num);
                  },
                  child: const Text("Add Stream to Main Page"))
            ],
          ),
        ));
  }
}

third.dart 代码:

import 'dart:math';
import 'package:flutter/material.dart';
// 引入你自己的stream.dart路径

class ThirdPage extends StatefulWidget {
  const ThirdPage({Key? key}) : super(key: key);

  @override
  State<ThirdPage> createState() => _ThirdPageState();
}

class _ThirdPageState extends State<ThirdPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                  onPressed: () {
                    String num = Random().nextInt(20).toString();
                    MyStream().controller.sink.add(num);
                  },
                  child: const Text("Add Stream to Main Page"))
            ],
          ),
        ));
  }
}

方案说明
  • 整个App内所有页面调用MyStream()拿到的都是同一个实例,不管是首页、商品详情页、收藏页,调用add方法往sink添加数据,所有监听了这个流的页面(比如主页、购物车页)都能收到数据,完全满足多页面触发加购、统一状态同步的需求
  • 广播流支持多个页面同时监听,不会出现监听冲突问题
  • 如果后续需要传递商品对象而非字符串,直接把StreamController的泛型从String替换成自定义的商品模型类即可

内容的提问来源于stack exchange,提问作者Kerimoff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 06:54:09