Flutter重复点击当前导航页触发ConnectionState.waiting问题求助
问题描述
应用加载时事件可正常显示,切换至其他页面无异常,但点击当前所在页面的导航栏按钮时,页面会进入ConnectionState.waiting状态,无法显示事件,其他页面也存在同样问题。
提供的主页代码
class MainPage extends StatefulWidget { const MainPage({super.key}); @override State<MainPage> createState() => _MainPageState(); } /** * Future builder is a staeful widhget that subs to a future that will return its value. * value starts calculates returns and finishes. * it takes the shiunk of code that produces the value and subs to it and i will give you the ability to * return various widgets that needs to be returned to the screen * ties ui with db. * */ class _MainPageState extends State<MainPage> { late final EventsService _eventsService; late final Future<DatabaseUser> _user; late final Stream<List<DatabaseEvent>> _events; int _currentIndex = 1; String _title = "Crewmunity"; @override void initState() { /** we create a new event service every time * so we will create a singleton * we create a service a service or class which is only one in our entire application. * there shouldnt be other copies. * */ _eventsService = EventsService(); _eventsService.open(); _user = _eventsService.getOrCreateUser(email: userEmail); _events = _eventsService.allEvents; super.initState(); } // no need for now // @override // dispose() { // _eventsService.close(); // super.dispose(); // } String get userEmail => AuthService.firebase().currentUser!.email!; @override Widget build(BuildContext context) { var theme = Theme.of(context); final style = theme.textTheme.displaySmall!.copyWith( color: theme.colorScheme.onPrimary, ); // the textTheme has different fonts return Scaffold( appBar: CustomAppbar(title: _title), body: FutureBuilder( future: _user, //_eventsService.getOrCreateUser(email: userEmail), builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.done: return StreamBuilder( stream: _events, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.active: /** * listview box scroll view which is a stateless widget * builder we will pass 2 params * we need for the item count to listen to the snapshot. */ if (snapshot.hasData) { final allEvents = snapshot.data as List<DatabaseEvent>; allEvents.sort((a, b) => b.date.compareTo(a.date)); return EventListView( events: allEvents, onDeleteEvent: (event) async { await _eventsService.deleteEvent(id: event.id); }, onTap: (event) { Navigator.of(context).pushNamed( createUpdateEventRoute, arguments: event); }, isUserPage: false); } else { return const CircularProgressIndicator(); } case ConnectionState.waiting: return const Text("Waiting for all events"); default: return CircularProgressIndicator(); } }, ); default: return const CircularProgressIndicator(); } }, ), bottomNavigationBar: CustomBottomNanBar( currentIndex: _currentIndex, ), ); } }
问题原因
- 页面重复重建:点击当前导航栏按钮时,BottomNavigationBar默认逻辑可能会销毁并重新创建当前页面实例,导致
initState重新执行,Stream订阅被重置,进入等待状态。 - EventsService未实现单例:当前代码每次进入页面都会新建
EventsService实例,重复打开数据库连接,导致流的订阅状态混乱。 - 未保持页面状态:页面切换时没有保留状态,每次重建都需要重新初始化Future和Stream。
解决方案
1. 实现EventsService单例
确保整个应用只有一个服务实例,避免重复创建数据库连接:
class EventsService { // 单例实现 static final EventsService _instance = EventsService._internal(); factory EventsService() => _instance; EventsService._internal(); // 初始化逻辑放在内部,确保只执行一次 bool _isOpened = false; void open() { if (!_isOpened) { // 原open逻辑 _isOpened = true; } } // 其他原有代码... }
2. 保持页面状态
使用AutomaticKeepAliveClientMixin让页面在切换时保留状态,避免重建:
class _MainPageState extends State<MainPage> with AutomaticKeepAliveClientMixin { // ... 原有代码 @override bool get wantKeepAlive => true; // 开启状态保持 @override Widget build(BuildContext context) { super.build(context); // 必须调用super.build // ... 原有build代码 } }
3. 优化BottomNavigationBar页面切换逻辑
使用IndexedStack包裹页面列表,确保页面实例不会被销毁:
// 假设导航栏对应的页面列表 final List<Widget> _pages = [ // 其他页面 MainPage(), // 其他页面 ]; // 在Scaffold的body中替换为IndexedStack body: IndexedStack( index: _currentIndex, children: _pages, ),
4. 增加Stream错误处理
在StreamBuilder中添加错误分支,避免因流异常导致页面卡住:
case ConnectionState.active: if (snapshot.hasError) { return Text('加载失败: ${snapshot.error}'); } if (snapshot.hasData) { // 原有显示逻辑 } else { return const CircularProgressIndicator(); }
内容的提问来源于stack exchange,提问作者jason
相关产品推荐
相关产品推荐

