面试题答案
一键面试实现分页加载的基本步骤
- 定义数据模型:创建一个数据模型类来表示从后端API返回的数据结构,例如
ListItem
类。 - 初始化状态:在Flutter的
StatefulWidget
中,初始化当前页码、数据列表、加载状态等状态变量。例如:
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
int _page = 1;
List<ListItem> _dataList = [];
bool _isLoading = false;
}
- 创建API请求方法:编写一个方法来调用后端API,根据页码获取数据。这个方法返回一个
Future
,例如:
Future<List<ListItem>> _fetchData(int page) async {
// 使用http或dio等网络库发送请求
// 假设返回的数据格式为List<Map<String, dynamic>>,将其转换为ListItem列表
List<Map<String, dynamic>> responseData = await http.get('api-url?page=$page');
return responseData.map((json) => ListItem.fromJson(json)).toList();
}
- 加载数据方法:在状态类中编写一个方法来加载数据。这个方法负责更新状态变量,如设置
_isLoading
为true
,调用API请求方法,处理返回的数据并更新_dataList
,最后设置_isLoading
为false
。例如:
Future<void> _loadData() async {
setState(() {
_isLoading = true;
});
try {
List<ListItem> newData = await _fetchData(_page);
setState(() {
_dataList.addAll(newData);
_page++;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
// 处理错误
print('Error fetching data: $e');
}
}
- 触发加载:在
initState
方法中调用_loadData
方法来初始化数据加载。还可以在列表滚动到末尾时触发加载更多数据,例如使用ScrollController
:
class _MyPageState extends State<MyPage> {
ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_loadData();
_scrollController.addListener(() {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent &&!_isLoading) {
_loadData();
}
});
}
}
利用Future
和Stream
优化异步数据加载性能
- 避免不必要的重复请求:
- 使用缓存:可以使用一个全局的缓存来存储已经请求过的数据。在发送请求前,先检查缓存中是否已经有对应页码的数据。如果有,则直接使用缓存数据,而不发送新的请求。例如:
Map<int, List<ListItem>> _cache = {};
Future<List<ListItem>> _fetchData(int page) async {
if (_cache.containsKey(page)) {
return _cache[page]!;
}
List<Map<String, dynamic>> responseData = await http.get('api-url?page=$page');
List<ListItem> newData = responseData.map((json) => ListItem.fromJson(json)).toList();
_cache[page] = newData;
return newData;
}
- 利用
Future
的特性:可以使用Future.microtask
来延迟任务执行,避免在短时间内多次触发相同的请求。例如,如果用户快速多次点击加载更多按钮,可以使用Future.microtask
来确保只执行一次加载操作。
Future<void> _loadData() async {
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.microtask(() async {
try {
List<ListItem> newData = await _fetchData(_page);
setState(() {
_dataList.addAll(newData);
_page++;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
print('Error fetching data: $e');
}
});
}
- 使用
Stream
优化:- 创建
Stream
:可以创建一个Stream
来管理数据加载过程。Stream
可以更好地处理异步事件流,比如在数据加载完成、错误发生等不同状态下通知监听者。例如:
- 创建
class DataFetcher {
final StreamController<List<ListItem>> _streamController = StreamController<List<ListItem>>();
Stream<List<ListItem>> get dataStream => _streamController.stream;
Future<void> fetchData(int page) async {
try {
List<Map<String, dynamic>> responseData = await http.get('api-url?page=$page');
List<ListItem> newData = responseData.map((json) => ListItem.fromJson(json)).toList();
_streamController.add(newData);
} catch (e) {
_streamController.addError(e);
}
}
void dispose() {
_streamController.close();
}
}
- 监听
Stream
:在Flutter组件中监听这个Stream
,并根据接收到的数据或错误进行相应处理。例如:
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
List<ListItem> _dataList = [];
DataFetcher _dataFetcher = DataFetcher();
@override
void initState() {
super.initState();
_dataFetcher.dataStream.listen((data) {
setState(() {
_dataList.addAll(data);
});
}, onError: (e) {
print('Error fetching data: $e');
});
_dataFetcher.fetchData(1);
}
@override
void dispose() {
_dataFetcher.dispose();
super.dispose();
}
}
处理加载过程中的错误
- 在
Future
中处理错误:在_fetchData
方法中,使用try - catch
块捕获请求过程中可能发生的错误。例如:
Future<List<ListItem>> _fetchData(int page) async {
try {
List<Map<String, dynamic>> responseData = await http.get('api-url?page=$page');
return responseData.map((json) => ListItem.fromJson(json)).toList();
} catch (e) {
throw Exception('Error fetching data: $e');
}
}
在调用_fetchData
的_loadData
方法中,同样使用try - catch
块来处理错误,并根据情况更新UI状态,如显示错误提示等。
2. 在Stream
中处理错误:当使用Stream
时,通过在listen
方法中传入onError
回调来处理错误。例如上面MyPage
组件中的_dataFetcher.dataStream.listen((data) {... }, onError: (e) {... })
,在onError
回调中可以记录错误日志、显示错误提示等操作。