flutter - 已从 BottomNavigationBar 项中侦听流
问题描述
在我的项目中,我正在使用带有自定义提供程序的架构。
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider(
{@required this.child,
@required this.bloc,
@required this.blocContext,
Key key})
: super(key: key);
final T bloc;
final Widget child;
final BlocContextBase<T> blocContext;
@override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context) {
final BlocProvider<T> provider = context.findAncestorWidgetOfExactType();
return provider.bloc;
}
}
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>> {
@override
void initState() {
super.initState();
widget.blocContext.subscribe(widget.bloc, context);
}
@override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
在我切换到 BottomNavigationBar 之前,一切都运行良好。
class TabContainerContent {
final Widget firstTab;
final Widget secondTab;
final Widget thirdTab;
TabContainerContent(
{@required this.firstTab,
@required this.secondTab,
@required this.thirdTab});
}
class TabContainerScreen extends StatefulWidget {
final TabContainerContent _content;
TabContainerScreen({@required TabContainerContent content})
: _content = content;
@override
_TabContainerScreenState createState() => _TabContainerScreenState();
}
class _TabContainerScreenState extends State<TabContainerScreen> {
int _selectedIndex = 0;
List<BottomNavigationBarItem> _bottomBarItems = [];
List<Widget> _tabWidgets = [];
@override
void initState() {
super.initState();
_tabWidgets.add(widget._content.firstTab);
_bottomBarItems.add(
BottomNavigationBarItem(
icon: Icon(Icons.map),
label: 'Profile',
),
);
_tabWidgets.add(widget._content.secondTab);
_bottomBarItems.add(
BottomNavigationBarItem(
icon: Icon(Icons.location_city),
label: 'Search',
),
);
_tabWidgets.add(widget._content.thirdTab);
_bottomBarItems.add(
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'Ads',
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _tabWidgets[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: _bottomBarItems,
currentIndex: _selectedIndex,
onTap: (int value) {
setState(() {
_selectedIndex = value;
});
},
),
);
}
}
仅当该集团在我的集团中有一个流控制器并且我在我的小部件中使用 StreamBuilder 时,问题才会开始。如果我转到另一个选项卡然后返回。然后我得到错误流已经被收听。在我看来,我没有在某处删除一些数据。
这就是集团的样子。
class ProfileBloc extends IProfileBloc {
final StreamController<User> _userControllerOne = StreamController<User>();
Sink<User> get _inUserState => _userControllerOne.sink;
Stream<User> get outUserState => _userControllerOne.stream;
@override
void dispose() {
_userControllerOne.close();
super.dispose();
}
}
--
class SearchBloc extends ISearchBloc {
final StreamController<User> _userControllerTwo = StreamController<User>();
Sink<User> get _inUserState => _userControllerTwo.sink;
Stream<User> get outUserState => _userControllerTwo.stream;
@override
void dispose() {
_userControllerTwo.close();
super.dispose();
}
}
--
class AdsBloc extends IAdsBloc {
final StreamController<User> _userControllerThree = StreamController<User>();
Sink<User> get _inUserState => _userControllerThree.sink;
Stream<User> get outUserState => _userControllerThree.stream;
@override
void dispose() {
_userControllerThree.close();
super.dispose();
}
}
这就是我的 UI 的样子
class ProfileScreen extends StatefulWidget {
ProfileScreen({Key key}) : super(key: key);
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
IProfileBloc _bloc;
@override
void initState() {
super.initState();
_bloc = BlocProvider.of(context);
_bloc.loadUser();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: StreamBuilder(
stream: _bloc.outUserState,
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
return Container();
},
),
));
}
}
--
class SearchScreen extends StatefulWidget {
SearchScreen({Key key}) : super(key: key);
@override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
ISearchBloc _bloc;
@override
void initState() {
super.initState();
_bloc = BlocProvider.of(context);
_bloc.loadUser();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: StreamBuilder(
stream: _bloc.outUserState,
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
return Container();
},
),
));
}
}
--
class AdsScreen extends StatefulWidget {
AdsScreen({Key key}) : super(key: key);
@override
_AdsScreenState createState() => _AdsScreenState();
}
class _AdsScreenState extends State<AdsScreen> {
IAdsBloc _bloc;
@override
void initState() {
super.initState();
_bloc = BlocProvider.of(context);
_bloc.loadUser();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: StreamBuilder(
stream: _bloc.outUserState,
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
return Container();
},
),
));
}
}
我也用注射器。
class ApplicationAssembly {
static final Injector injector = Injector('ApplicationAssemblyInjector');
//----------------------------------------------------------------------------
static void initialize() {
_registerManagers();
_registerBlocs();
_registerModuleBuilders();
}
static void _registerManagers() {
}
static void _registerBlocs() {
injector.map<IProfileBloc>((i) => ProfileBloc());
injector.map<IAdsBloc>((i) => AdsBloc());
injector.map<ISearchBloc>((i) => SearchBloc());
}
static void _registerModuleBuilders() {
//TAB BAR MODULES---------------------------------------------------------------
injector.map<ProfileModuleBuilder>((i) => () {
return BlocProvider(
child: ProfileScreen(),
bloc: i.get<IProfileBloc>(),
blocContext: ProfileBlocContext());
});
injector.map<SearchModuleBuilder>((i) => () {
return BlocProvider(
child: SearchScreen(),
bloc: i.get<ISearchBloc>(),
blocContext: SearchBlocContext());
});
injector.map<AdsModuleBuilder>((i) => () {
return BlocProvider(
child: AdsScreen(),
bloc: i.get<IAdsBloc>(),
blocContext: AdsBlocContext());
});
//------------------------------------------------------------------------------
//TAB BAR-----------------------------------------------------------------------
injector.map<TabModuleBuilder>((i) => () {
Widget profileTab = i.get<ProfileModuleBuilder>()();
Widget searchTab = i.get<SearchModuleBuilder>()();
Widget adsTab = i.get<AdsModuleBuilder>()();
return TabContainerScreen(
content: TabContainerContent(
firstTab: profileTab, secondTab: searchTab, thirdTab: adsTab),
);
});
//------------------------------------------------------------------------------
}
}
解决方案
我的项目中有同样的错误。
就我而言,我需要重用 StreamController。场景是,有重置按钮从头开始搜索信息。
反正我是这样解决的。
- 添加
.close()
到重置按钮。 - 在搜索中再次初始化 StreamController。
if (streamController.isCloded) {
streamController = new StreamController<List<A>>();
}
在您的情况下,我认为 tabview 重新生成(或重新构建?)视图。所以 StreamController 重新初始化。
我认为您会在 initState 中添加初始化 StreamContrller。
@override
void initState(){
super.initState();
if (streamController == null){
streamController = new StreamController();
}
我希望这会对你有所帮助。
推荐阅读
- javascript - node - 如何将 MongoDB 时间戳转换为日期
- forms - 表单正在替换 Symfony 4 中的变量
- java - 从 Java 中的双端队列获取映射键列表
- python - 带有重复标签名称的 Python XML 解析
- android - 带有切换按钮的自定义通知
- spring-integration - 如何使用 spring 集成 dsl 从 JMS 队列中解组 XML
- mvcgrid.net - 在网格中保留数据
- spring - 如何在没有弹簧安全的情况下在百里香中进行会话
- ios - 初始化 MPSNNGraph 时出错
- apache-spark - 具有多个 kafka 集群的 Spark 结构化流