Flutter中如何通过Bloc正确实现状态切换并跳转至下一页
规范实现Flutter+Bloc启动页加载与导航
问题核心
现有启动页实现中,在BlocBuilder的builder回调里触发导航会触发Flutter导航锁报错,且StartDone状态下仍会显示多余文本,需要更符合Bloc最佳实践的写法。
优化方案
核心思路是分离UI构建与副作用逻辑:用BlocListener处理导航这类状态变化的副作用,BlocBuilder专注UI渲染;同时把初始化逻辑移到initState避免重复触发。
修改后的启动页代码
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:money_lover/home_page/home_page.dart'; import 'package:money_lover/start/bloc/start_cubit.dart'; class StartPage extends StatelessWidget { const StartPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return BlocProvider( create: (_) => StartCubit(), child: const StartView(), ); } } class StartView extends StatefulWidget { const StartView({Key? key}) : super(key: key); @override State<StartView> createState() => _StartViewState(); } class _StartViewState extends State<StartView> { @override void initState() { super.initState(); // 仅在初始化时启动加载逻辑,避免build重复触发 context.read<StartCubit>().startRunning(); } @override Widget build(BuildContext context) { return Scaffold( body: BlocListener<StartCubit, StartState>( // 专门监听状态变化执行副作用(导航) listener: (context, state) { if (state is StartDone) { // 使用pushReplacement替代push,避免返回启动页 Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const HomePage()), ); } }, child: Center( child: BlocBuilder<StartCubit, StartState>( // 仅负责UI渲染,不处理副作用 builder: (context, state) { if (state is StartDoing) { return Text('加载中:第 ${state.step + 1}/4 步'); } // 初始或过渡状态显示加载指示器,不显示多余文本 return const CircularProgressIndicator(); }, ), ), ), ); } }
Cubit代码优化(可选)
把重复的延迟逻辑用循环简化,提升代码可读性:
void loadData() async { for (int i = 0; i < 4; i++) { emit(StartDoing(i)); await Future.delayed(const Duration(seconds: 1)); } emit(StartDone()); }
优化说明
- 避免导航锁报错:
BlocListener的listener回调是在状态变化后、build周期外执行的,无需依赖Future.delayed或addPostFrameCallback。 - 职责分离:
BlocBuilder只处理UI渲染,BlocListener处理导航等副作用,符合Bloc架构的设计原则。 - UI控制精准:
StartDone状态下直接触发导航,不会显示多余的状态文本,初始/加载状态显示合理的加载提示。
内容的提问来源于stack exchange,提问作者Tien
相关产品推荐
相关产品推荐

