如何使用BLoC实现仅重建屏幕动态上部区域(每秒更新)
用BLoC优化Flutter动态区域重绘,避免静态区域不必要重建
核心思路
把动态区域的状态(计时器数值、动画值、事件名称等)交给BLoC统一管理,让上部动态区域仅监听BLoC的状态变化进行局部重建,下部静态区域完全不参与重绘流程。
步骤1:定义BLoC的事件与状态
先封装动态区域需要的所有数据为状态类,再定义触发更新的事件类:
// 触发计时器更新的事件 class TimerTickEvent {} // 存储动态区域所有数据的状态类 class CounterState { final double value; final String smallEventName; final String hour; final String minute; final String second; CounterState({ required this.value, required this.smallEventName, required this.hour, required this.minute, required this.second, }); }
步骤2:实现BLoC逻辑
创建Bloc类处理计时器逻辑,每秒生成包含最新数据的状态:
class CounterBloc extends Bloc<TimerTickEvent, CounterState> { late final Timer _timer; final Animation<double> animation; final double baseValue; final String eventName; late Duration counter; CounterBloc({ required this.animation, required this.baseValue, required this.eventName, required Duration initialCounter, }) : super(CounterState( value: animation.value * baseValue, smallEventName: eventName, hour: _formatHour(initialCounter), minute: _formatMinute(initialCounter), second: _formatSecond(initialCounter), )) { counter = initialCounter; // 启动每秒计时器 _timer = Timer.periodic(const Duration(seconds: 1), (_) { counter = counter - const Duration(seconds: 1); add(TimerTickEvent()); }); on<TimerTickEvent>((event, emit) { emit(CounterState( value: animation.value * baseValue, smallEventName: eventName, hour: _formatHour(counter), minute: _formatMinute(counter), second: _formatSecond(counter), )); }); } // 抽离时间格式化逻辑,避免重复代码 static String _formatHour(Duration counter) { return counter.inHours > 9 ? counter.toString().substring(0, 2) : '0${counter.toString().substring(0, 1)}'; } static String _formatMinute(Duration counter) { return counter.inHours > 9 ? counter.toString().substring(3, 5) : counter.toString().substring(2, 4); } static String _formatSecond(Duration counter) { return counter.inHours > 9 ? counter.toString().substring(6, 8) : counter.toString().substring(5, 7); } @override Future<void> close() { _timer.cancel(); return super.close(); } }
步骤3:封装动态区域为独立Widget
把原上部CustomPaint区域封装成单独Widget,用BlocBuilder监听BLoC状态变化,仅该Widget会在状态更新时重建:
class DynamicCounterArea extends StatelessWidget { const DynamicCounterArea({super.key}); @override Widget build(BuildContext context) { return Expanded( flex: 5, child: BlocBuilder<CounterBloc, CounterState>( builder: (context, state) { return CustomPaint( painter: CustomHomeScreenCounter( value: state.value, smallEventName: state.smallEventName, hour: state.hour, minute: state.minute, second: state.second, ), ); }, ), ); } }
步骤4:在父Widget中集成BLoC
在包含上下区域的父Widget里,用BlocProvider提供CounterBloc,静态区域直接放在外层,不会随状态变化重建:
class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation<double> _animation; final double _baseValue = 1.0; // 根据实际业务替换 final String _eventName = 'Fajr in'; // 根据实际业务替换 late Duration _initialCounter; // 初始计时器值,根据实际业务初始化 @override void initState() { super.initState(); // 初始化动画控制器 _animationController = AnimationController( vsync: this, duration: const Duration(seconds: 1), )..repeat(); _animation = CurvedAnimation(parent: _animationController, curve: Curves.linear); // 示例初始计时器值:1小时30分20秒 _initialCounter = const Duration(hours: 1, minutes: 30, seconds: 20); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return BlocProvider( create: (context) => CounterBloc( animation: _animation, baseValue: _baseValue, eventName: _eventName, initialCounter: _initialCounter, ), child: Column( children: [ // 动态区域:仅在BLoC状态变化时重建 const DynamicCounterArea(), // 静态区域:永远不会因计时器更新重建 Expanded( flex: 5, child: Container( color: Colors.grey[200], child: const Center( child: Text('静态内容区域'), ), ), ), ], ), ); } }
关键说明
- BLoC统一管理动态数据的更新逻辑,避免在父Widget中维护状态导致全局重绘。
BlocBuilder仅监听CounterBloc的状态变化,触发DynamicCounterArea局部重建,下部静态区域完全不受影响。- 时间格式化逻辑抽离到BLoC内部,减少Widget层代码冗余,保证状态数据的统一性。
内容的提问来源于stack exchange,提问作者Seif Kandil
相关产品推荐
相关产品推荐

