在 BottomNavigationBar 选项卡之间传递 StreamBuilder => 错误状态:已收听流
Pass StreamBuilder between BottomNavigationBar tabs => Bad state: Stream has already been listened to
我正在尝试将 Stream 从一个选项卡传递到另一个选项卡,但是当我在 home.dart 中返回时
我可以 close/destroy 选项卡更改时的 Stream 吗?
当应用程序运行时,数据被正确获取并且一切都是 good.The 问题仅在我更改选项卡时出现。
数据存储到 firestore 数据库中。
我收到这个错误:
Bad state: Stream has already been listened to
这是我的Home.dart
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
Home createState() => Home();
}
class Home extends State<HomePage> {
int _currentIndex;
var _tabs = [];
List<Company> currentCompaniesList = List();
StreamController<List<Company>> _streamController;
Stream<List<Company>> companiesStream;
_getData() async {
_companyService
.getCompanies()
.then((value) => _streamController.sink.add(value));
}
@override
void initState() {
super.initState();
_currentIndex = 0;
_streamController = StreamController<List<Company>>();
_getData();
companiesStream = _streamController.stream;
}
}
@override
Widget build(BuildContext context) {
_tabs = [
CompanyTab(stream: companiesStream),
MapTab(),
Center(child: Text('Profile')),
Center(child: Text('Settings')),
];
return Scaffold(
...
actions: ...,
body: _tabs[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
backgroundColor: BACKGROUND_COLOR,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
)
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
}
}
这是我的CompanyTab.dart
class CompanyTab extends StatefulWidget {
Stream stream;
CompanyTab({Key key, this.stream}) : super(key: key);
@override
_CompanyTabState createState() => _CompanyTabState(stream);
}
class _CompanyTabState extends State<CompanyTab> {
Stream stream;
_CompanyTabState(this.stream);
@override
void initState() {
super.initState();
}
StreamBuilder companyList() {
return StreamBuilder<List<Company>>(
initialData: [],
stream: stream,
builder: (BuildContext context, AsyncSnapshot<List<Company>> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.connectionState == ConnectionState.none ||
snapshot.data == null) {
return LoadingWidget();
} else {
return ListView.builder(
padding: const EdgeInsets.all(10),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Company company = snapshot.data.elementAt(index);
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 1.0, horizontal: 4.0),
child: Card(
child: ListTile(
onTap: () {},
title: Text(company.name),
...
),
),
),
);
});
}
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: companyList(),
),
);
}
}
这是关于小部件生命周期的。我可以给你两个建议。
1。将 _streamController 和 _getData() 方法移动到 _CompanyTabState。
默认情况下 BottomNavigationBar
当你离开时销毁选项卡
一个,当你 return 回到它时再次初始化它。如果需要
您需要移动 _streamController
和 _getData()
的行为
方法进入 _CompanyTabState
。别忘了打电话
_streamController.close()
里面的dispose()
方法
_CompanyTabState
,很重要。 _companyService
可以
注入 _CompanyTabState
。这是生命周期的问题。
应该像这样工作:
...
class _CompanyTabState extends State<CompanyTab> {
final _streamController = StreamController<List<Company>>();
final CompanyService _companyService;
_CompanyTabState(this._companyService);
@override
void initState() {
super.initState();
_getData();
}
StreamBuilder companyList() {
return StreamBuilder<List<Company>>(
initialData: [],
stream: _streamController.stream,
builder: (BuildContext context, AsyncSnapshot<List<Company>> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.connectionState == ConnectionState.none ||
snapshot.data == null) {
return LoadingWidget();
} else {
return ListView.builder(
padding: const EdgeInsets.all(10),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Company company = snapshot.data.elementAt(index);
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 1.0, horizontal: 4.0),
child: Card(
child: ListTile(
onTap: () {},
title: Text(company.name),
...
),
),
),
);
});
}
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: companyList(),
),
);
}
@override
void dispose() {
super.dispose();
_streamController.close();
}
void _getData() {
_companyService
.getCompanies()
.then((value) => _streamController.sink.add(value));
}
}
2。使用 IndexedStack
您可以在离开标签时保存标签的状态和小部件数据(如滚动偏移、输入的文本等)。这是 iOS UITabBarController-like 行为。使用 IndexedStack 实现:
...
return Scaffold(
...
actions: ...,
body: IndexedStack(
children: _tabs,
index: _currentIndex,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
backgroundColor: BACKGROUND_COLOR,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
)
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
使用哪个选项由您决定,如果您愿意,可以同时使用两者。但我强烈建议将 _streamController 移至 _CompanyTabState,因为它们的生命周期应该相同。
我正在尝试将 Stream 从一个选项卡传递到另一个选项卡,但是当我在 home.dart 中返回时 我可以 close/destroy 选项卡更改时的 Stream 吗? 当应用程序运行时,数据被正确获取并且一切都是 good.The 问题仅在我更改选项卡时出现。 数据存储到 firestore 数据库中。
我收到这个错误:
Bad state: Stream has already been listened to
这是我的Home.dart
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
Home createState() => Home();
}
class Home extends State<HomePage> {
int _currentIndex;
var _tabs = [];
List<Company> currentCompaniesList = List();
StreamController<List<Company>> _streamController;
Stream<List<Company>> companiesStream;
_getData() async {
_companyService
.getCompanies()
.then((value) => _streamController.sink.add(value));
}
@override
void initState() {
super.initState();
_currentIndex = 0;
_streamController = StreamController<List<Company>>();
_getData();
companiesStream = _streamController.stream;
}
}
@override
Widget build(BuildContext context) {
_tabs = [
CompanyTab(stream: companiesStream),
MapTab(),
Center(child: Text('Profile')),
Center(child: Text('Settings')),
];
return Scaffold(
...
actions: ...,
body: _tabs[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
backgroundColor: BACKGROUND_COLOR,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
)
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
}
}
这是我的CompanyTab.dart
class CompanyTab extends StatefulWidget {
Stream stream;
CompanyTab({Key key, this.stream}) : super(key: key);
@override
_CompanyTabState createState() => _CompanyTabState(stream);
}
class _CompanyTabState extends State<CompanyTab> {
Stream stream;
_CompanyTabState(this.stream);
@override
void initState() {
super.initState();
}
StreamBuilder companyList() {
return StreamBuilder<List<Company>>(
initialData: [],
stream: stream,
builder: (BuildContext context, AsyncSnapshot<List<Company>> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.connectionState == ConnectionState.none ||
snapshot.data == null) {
return LoadingWidget();
} else {
return ListView.builder(
padding: const EdgeInsets.all(10),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Company company = snapshot.data.elementAt(index);
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 1.0, horizontal: 4.0),
child: Card(
child: ListTile(
onTap: () {},
title: Text(company.name),
...
),
),
),
);
});
}
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: companyList(),
),
);
}
}
这是关于小部件生命周期的。我可以给你两个建议。
1。将 _streamController 和 _getData() 方法移动到 _CompanyTabState。
默认情况下 BottomNavigationBar
当你离开时销毁选项卡
一个,当你 return 回到它时再次初始化它。如果需要
您需要移动 _streamController
和 _getData()
的行为
方法进入 _CompanyTabState
。别忘了打电话
_streamController.close()
里面的dispose()
方法
_CompanyTabState
,很重要。 _companyService
可以
注入 _CompanyTabState
。这是生命周期的问题。
应该像这样工作:
...
class _CompanyTabState extends State<CompanyTab> {
final _streamController = StreamController<List<Company>>();
final CompanyService _companyService;
_CompanyTabState(this._companyService);
@override
void initState() {
super.initState();
_getData();
}
StreamBuilder companyList() {
return StreamBuilder<List<Company>>(
initialData: [],
stream: _streamController.stream,
builder: (BuildContext context, AsyncSnapshot<List<Company>> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.connectionState == ConnectionState.none ||
snapshot.data == null) {
return LoadingWidget();
} else {
return ListView.builder(
padding: const EdgeInsets.all(10),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Company company = snapshot.data.elementAt(index);
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 1.0, horizontal: 4.0),
child: Card(
child: ListTile(
onTap: () {},
title: Text(company.name),
...
),
),
),
);
});
}
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: companyList(),
),
);
}
@override
void dispose() {
super.dispose();
_streamController.close();
}
void _getData() {
_companyService
.getCompanies()
.then((value) => _streamController.sink.add(value));
}
}
2。使用 IndexedStack
您可以在离开标签时保存标签的状态和小部件数据(如滚动偏移、输入的文本等)。这是 iOS UITabBarController-like 行为。使用 IndexedStack 实现:
...
return Scaffold(
...
actions: ...,
body: IndexedStack(
children: _tabs,
index: _currentIndex,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
backgroundColor: BACKGROUND_COLOR,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
),
BottomNavigationBarItem(
...
)
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
使用哪个选项由您决定,如果您愿意,可以同时使用两者。但我强烈建议将 _streamController 移至 _CompanyTabState,因为它们的生命周期应该相同。