Skip to main content

开发纪要tips

bloc

tip 1

// 通过 BLoC 刷新数据
context.read<WorkCalendarBloc>().add(
const WorkCalendarDataRefreshed(),
); 解释这句

这句代码是 向 BLoC 发送一个事件(Event),通知它刷新日历数据。

tip 2

Emitter<WorkCalendarState> emit,
emit(state.copyWith(
viewMode: viewMode,
currentDate: currentDate,
pageIndex: _calculatePageIndex(currentDate),
));

旧 State(不可变) → copyWith 创建新 State → emit 发出 → BlocBuilder 重建 UI。

tip 3

BlocSelector和BlocConsumer 不一样地方

  1. BlocSelector:只关心状态中的某一部分 作用 从整个 State 中提取出一个值,当这个值变化时才重新构建 UI。 特点
  • ✅ 性能优化
  • ✅ 只监听 state 的部分字段
  • ❌ 没有 side effect(不能弹 Dialog、SnackBar)
  • ❌ 没有 listener
  1. BlocConsumer:同时负责监听和构建
    BlocConsumer = BlocListener + BlocBuilder
    特点
  • ✅ builder:负责更新 UI
  • ✅ listener:负责副作用

例如:

  • showDialog
  • SnackBar
  • Navigator.push
  • Toast
  • 调用其他 Bloc
BlocConsumer<LoginBloc, LoginState>(
listener: (context, state) {
if (state.status == LoginStatus.success) {
Navigator.pushNamed(context, '/home');
}

if (state.status == LoginStatus.error) {
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(content: Text(state.message)),
);
}
},
builder: (context, state) {
if (state.loading) {
return CircularProgressIndicator();
}

return LoginButton();
},
)

tip 3

BlocSelector 和 BlocBuilder 都是监听 Bloc State 变化来刷新 UI,但最大的区别在于: BlocBuilder 监听整个 State,BlocSelector 只监听 State 中某一个字段。 ⸻

  1. BlocBuilder 每次 emit(newState),都会触发 builder。
BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
return Text(state.name);
},
);

假设:

class UserState {
final String name;
final int age;
}

如果:

emit(state.copyWith(age: 20));

虽然 name 没变,但 BlocBuilder 仍然会 rebuild。

  1. BlocSelector

只选择需要的字段:

BlocSelector<UserBloc, UserState, String>(
selector: (state) => state.name,
builder: (context, name) {
return Text(name);
},
);

此时:

emit(state.copyWith(age: 20));

因为:

selector(state) => state.name

得到的值没有变化,

所以:

builder 不会重新执行

只有:

emit(state.copyWith(name: "Tom"));

才会 rebuild。

对比

BlocBuilder BlocSelector
监听对象 整个 State State 的部分字段
State 任意变化 rebuild 不一定 rebuild
性能 普通 更优
builder 参数 state selectedValue
适合 页面整体刷新 局部刷新

日历 App 的例子

State:

class CalendarState {
final DateTime currentDate;
final ViewMode viewMode;
final List<Event> events;
}

页面整体

BlocBuilder<CalendarBloc, CalendarState>(
builder: (_, state) {
return CalendarPage(state);
},
);

只要:

  • currentDate
  • viewMode
  • events

任何一个变化,

整个页面都会 rebuild。

月视图标题

BlocSelector<CalendarBloc, CalendarState, DateTime>(
selector: (state) => state.currentDate,
builder: (_, date) {
return Text(formatMonth(date));
},
);

当:

events 更新

时:

标题不会 rebuild

只有:

currentDate 改变

才刷新标题。

事件列表

BlocSelector<CalendarBloc, CalendarState, List<Event>>(
selector: (state) => state.events,
builder: (_, events) {
return EventList(events);
},
);

这样:

  • 日期变化 → EventList 不刷新
  • viewMode 变化 → EventList 不刷新
  • events 变化 → EventList 刷新

BlocBuilder + buildWhen 能实现类似效果

BlocBuilder<UserBloc, UserState>(
buildWhen: (prev, curr) =>
prev.name != curr.name,
builder: (_, state) {
return Text(state.name);
},
);

效果与:

BlocSelector<UserBloc, UserState, String>(
selector: (state) => state.name,
builder: (_, name) {
return Text(name);
},
);

几乎相同。

实际开发建议

页面级 Widget

用:

BlocBuilder

Cell、标题、按钮、统计数字等局部组件

优先用:

BlocSelector

特别是像你的 日历列表 App,事件列表、顶部月份标题、当前日期、视图模式(月/周)都很适合拆成多个 BlocSelector,这样可以减少不必要的重建,提高滚动和切换性能。

tip 4

BlocConsumer的listenWhen和listener:
这两个是 BlocConsumer(或者 BlocListener)里的两个回调,用来控制什么时候监听状态变化,以及状态变化后做什么。

BlocConsumer<WorkCalendarBloc, WorkCalendarState>(
listenWhen: (previous, current) =>
previous.schedulesByDay != current.schedulesByDay,
listener: (context, state) {
// 执行操作
},
)

tip 5

页面右侧可以拖动的进度条在flutter中叫什么 在 Flutter 中,页面右侧可以拖动的那条通常有几种情况,名称不完全一样: 在 Flutter 中,页面右侧可以拖动的那条通常有几种情况,名称不完全一样:

  1. Scrollbar(滚动条)——最常见

如果是列表、页面右侧出现一条可以拖动的条,叫 Scrollbar。

Scrollbar(
thumbVisibility: true,
child: ListView.builder(
itemCount: 100,
itemBuilder: (_, index) => ListTile(
title: Text('Item $index'),
),
),
)

效果: │ │ 内容内容内容 █ ← 可以拖动 │

如果希望用户直接拖动滚动条:

Scrollbar(
thumbVisibility: true,
interactive: true,
child: ListView(...),
)

  1. CupertinoScrollbar(iOS 风格)
CupertinoScrollbar(
child: ListView(...)
)

  1. RawScrollbar(可高度自定义)
RawScrollbar(
thumbColor: Colors.blue,
radius: Radius.circular(8),
thickness: 6,
child: ListView(...)
)

  1. Slider(滑块)

如果不是滚动页面,而是:

────○────

这种拖动控制数值的组件,叫:

Slider(
value: value,
onChanged: (v) {},
)

  1. Draggable Scrollbar(长列表快速定位)

像通讯录:

A
B
C
...
Z

拖动右侧滚动条会快速跳转,这种一般叫:

  • Draggable Scrollbar
  • Fast Scrollbar
  • Indexed Scrollbar

Flutter 官方没有提供,需要第三方库,例如:

  • draggable_scrollbar
  • scrollable_positioned_list

如果你说的是类似 Google Calendar、微信聊天记录、联系人列表那种右侧悬浮并可拖动快速滚动的条,通常称为:

Scrollbar(可交互滚动条)

或更具体:

Draggable Scrollbar / Fast Scrollbar

如果方便的话,可以发一张截图,我可以告诉你它在 Flutter 中对应的具体组件以及实现方式。