开发纪要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 不一样地方
- BlocSelector:只关心状态中的某一部分 作用 从整个 State 中提取出一个值,当这个值变化时才重新构建 UI。 特点
- ✅ 性能优化
- ✅ 只监听 state 的部分字段
- ❌ 没有 side effect(不能弹 Dialog、SnackBar)
- ❌ 没有 listener
- 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 中某一个字段。 ⸻
- 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。
⸻
- 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 中,页面右侧可以拖动的那条通常有几种情况,名称不完全一样:
- Scrollbar(滚动条)——最常见
如果是列表、页面右侧出现一条可以拖动的条,叫 Scrollbar。
Scrollbar(
thumbVisibility: true,
child: ListView.builder(
itemCount: 100,
itemBuilder: (_, index) => ListTile(
title: Text('Item $index'),
),
),
)
效果: │ │ 内容内容内容 █ ← 可以拖动 │
如果希望用户直接拖动滚动条:
Scrollbar(
thumbVisibility: true,
interactive: true,
child: ListView(...),
)
⸻
- CupertinoScrollbar(iOS 风格)
CupertinoScrollbar(
child: ListView(...)
)
⸻
- RawScrollbar(可高度自定义)
RawScrollbar(
thumbColor: Colors.blue,
radius: Radius.circular(8),
thickness: 6,
child: ListView(...)
)
⸻
- Slider(滑块)
如果不是滚动页面,而是:
────○────
这种拖动控制数值的组件,叫:
Slider(
value: value,
onChanged: (v) {},
)
⸻
- 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 中对应的具体组件以及实现方式。