我正在研究基于 Bloc 的 ListView 分页。
但问题是每当从 API 获取新数据并将数据附加到先前的状态并发出时,ListView 就会刷新并且光标位置会跳转到应用程序的顶部。
下面是我的代码。
屏幕
class DaumListScreen extends StatelessWidget with DaumLogic {
const DaumListScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<DaumListBloc, DaumListState>(builder: (context, state) {
if (state is DaumListLoadedState) {
List<DaumListValue> daumLists = state.daumList.value;
return NotificationListener<ScrollNotification>(
onNotification: (scrollInfo) {
scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent
? serviceLocator<DaumListBloc>()
.add(FetchDaumListNextPageEvent())
: null;
return true;
},
child: ListView.builder(
itemCount: daumLists.length,
itemBuilder: (BuildContext context, int index) {
return DaumListWidget(
title: daumLists[index].title,
departureDate: daumLists[index].date,
thumbnail: daumLists[index].thumbnail,
subTitle: daumLists[index].title);
},
),
);
}
// default view
return const SingleChildScrollView();
});
}
}
块
class DaumListBloc extends Bloc<DaumListEvent, DaumListState> {
DaumListBloc({this.daumRepository})
: assert(daumRepository != null),
super(InitialDaumListState()) {
on<FetchDaumListEvent>(((event, emit) async {
emit(DaumListLoadingState());
try {
final DaumList? data = await daumRepository?.fetchDaumList();
// daum list loaded
emit(DaumListLoadedState(
data ?? DaumList(nextToken: "", queryExecutionId: "", value: [])));
} catch (e) {
print(e);
emit(DaumListErrorState(e.toString()));
}
}));
on<FetchDaumListNextPageEvent>(((event, emit) async {
final currentState = state;
emit(DaumListLoadingState());
if (currentState is DaumListLoadedState) {
final nextToken = currentState.daumList.nextToken;
final queryExecutionId = currentState.daumList.queryExecutionId;
try {
final DaumList data = await daumRepository?.fetchDaumList(
nextToken: nextToken, queryExecutionId: queryExecutionId) ??
DaumList(nextToken: "", queryExecutionId: "", value: []);
final DaumList concatenatedData = DaumList(
nextToken: data.nextToken,
queryExecutionId: data.queryExecutionId,
value: currentState.daumList.value + data.value);
// daum list loaded
emit(DaumListLoadedState(concatenatedData));
} catch (e) {
print(e);
emit(DaumListErrorState(e.toString()));
}
} else {
emit(const DaumListErrorState(
"pagination failed: previous data does not exist"));
}
}));
}
final DaumRepository? daumRepository;
}
主要
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
flex: 1,
child: MultiBlocProvider(providers: [
BlocProvider(
create: (context) => serviceLocator<DaumListBloc>()
..add(FetchDaumListEvent()))
], child: const DaumListScreen()))
])));
}));
}
我不知道哪一个导致了当前位置状态的丢失。
我将 Bloc Builder 更改为 Bloc Listener,即使状态发生变化,它也能保持位置。
class DaumListScreen extends StatefulWidget {
const DaumListScreen({super.key});
@override
State<DaumListScreen> createState() => _DaumListScreenState();
}
class _DaumListScreenState extends State<DaumListScreen> {
List<DaumListValue> daumLists = [];
late DaumListBloc _daumListBloc;
@override
void initState() {
_daumListBloc = serviceLocator<DaumListBloc>();
DaumListState initialState = _daumListBloc.state;
if (initialState is DaumListLoadedState) {
setState(() {
daumLists = initialState.daumList.value;
});
}
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocListener<DaumListBloc, DaumListState>(
listener: (context, state) {
if (state is DaumListLoadedState) {
setState(() {
daumLists = state.daumList.value;
});
}
},
child: NotificationListener<ScrollNotification>(
onNotification: (scrollInfo) {
if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent) {
_daumListBloc.add(FetchDaumListNextPageEvent());
}
return true;
},
child: ListView.builder(
itemCount: daumLists.length,
itemBuilder: (BuildContext context, int index) {
return DaumListWidget(
title: daumLists[index].title,
departureDate: daumLists[index].date,
thumbnail: daumLists[index].thumbnail,
subTitle: daumLists[index].title);
},
),
));
}
}