当我将BottomNavigationBar
与BLoC模式一起使用时,会导致错误Bad state: Stream has already been listened to.
我可能只在一个地方收听BLoC流。
我的代码如下。
main.dart
import 'package:flutter/material.dart';
import 'package:bottom_tab_bloc/app_state_bloc.dart';
import 'package:bloc_provider/bloc_provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return BlocProvider(
creator: (context, _bag) => AppStateBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String activeTab = "tab1";
final bottomTabs = ["tab1", "tab2"];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(activeTab),
),
body: buildTab(activeTab),
bottomNavigationBar: BottomNavigationBar(
currentIndex: bottomTabs.indexOf(activeTab),
onTap: (int index) {
setState(() {
activeTab = bottomTabs[index];
});
},
items: bottomTabs.map((tab) =>
buildBnbItem(tab)
).toList(),
),
);
}
Widget buildTab(String tab) {
if (tab == "tab1") {
return TabOne();
} else if (tab == "tab2") {
return TabTwo();
}
}
BottomNavigationBarItem buildBnbItem (String tab) {
assert(bottomTabs.contains(tab));
if (tab == "tab1") {
return BottomNavigationBarItem(
title: Text('Tab1'),
icon: Icon(Icons.looks_one),
);
} else if (tab == "tab2") {
return BottomNavigationBarItem(
title: Text('Tab1'),
icon: Icon(Icons.looks_two),
);
}
}
}
class TabOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AppStateBloc bloc = BlocProvider.of<AppStateBloc>(context);
return StreamBuilder(
stream: bloc.outValue1,
builder: (context, snapshot) =>
Center(child: Text(snapshot.data.toString())),
);
}
}
class TabTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AppStateBloc bloc = BlocProvider.of<AppStateBloc>(context);
return StreamBuilder(
stream: bloc.outValue2,
builder: (context, snapshot) =>
Center(child: Text(snapshot.data.toString())),
);
}
}
app_state_bloc.dart
import 'dart:async';
import 'package:bloc_provider/bloc_provider.dart';
class AppStateBloc implements Bloc {
StreamController<int> _value1Controller
= StreamController<int>();
Sink<int> get _inValue1 => _value1Controller.sink;
Stream<int> get outValue1 => _value1Controller.stream;
StreamController<int> _updateValue1Controller
= StreamController<int>();
Sink<int> get updateValue1 =>
_updateValue1Controller.sink;
StreamController<int> _value2Controller
= StreamController<int>();
Sink<int> get _inValue2 => _value2Controller.sink;
Stream<int> get outValue2 => _value2Controller.stream;
StreamController<int> _updateValue2Controller
= StreamController<int>();
Sink<int> get updateValue2 =>
_updateValue2Controller.sink;
AppStateBloc(){
_inValue1.add(1);
_updateValue1Controller.stream.listen(_updateValue1);
_inValue2.add(2);
_updateValue2Controller.stream.listen(_updateValue2);
}
@override
void dispose() {
_value1Controller.close();
_updateValue1Controller.close();
_value2Controller.close();
_updateValue2Controller.close();
}
void _updateValue1(int value1) {
_inValue1.add(value1);
}
void _updateValue2(int value2) {
_inValue2.add(value2);
}
}
我只能第一次从TabTwo
转到TabOne
,但是当我回到TabOne
时会发生错误。
我也尝试在StreamController<int>.broadcast()
中使用app_state_bloc.dart
,但是snapshot.data
始终是null
。
BottomNavigationBar
?AppStateBloc.dispose()
?何时何地调用AppStateBloc.dispose()
?snapshot.data
始终为空?答案 0 :(得分:1)
为什么流被调用两次以上,尽管我写了每个流 在一个地方?
该错误与流的订阅者数量有关。默认情况下,StreamController
仅允许一个订阅者。这就是TabOne
第一次可以正常运行,但随后会中断的原因。
此代码中是否调用了AppStateBloc.dispose()?何时何地 是AppStateBloc.dispose()吗?
在删除BlocProvider
小部件时会调用它,但是由于它被用作应用程序的根目录,所以我猜这只会在应用程序关闭时发生。
为什么广播流的snapshot.data始终为null?
没有侦听器时,广播流不会缓冲事件。由于您是在创建TabOne
之前写流,因此该事件丢失了,您得到了null
。
如何使用BLoC模式实现BottomNavigationBar?
我想这取决于您的用例,但是对于此特定示例,如果将rxdart的BehaviorSubject替换为StreamController
,则可以正常工作,因为这样您就可以总是向您发送最后一个事件的广播流。