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
相关产品推荐
相关产品推荐

