Flutter异步加载列表插件async_list_view的使用
Flutter 异步加载列表插件 async_list_view 的使用
异步加载列表插件 async_list_view
可以帮助你从异步数据源中懒加载一个滚动列表。async_list_view
是基于 ListView.builder
和 StreamSummaryBuilder
的轻量级封装。
由于项目项只在用户可见时才被加载,因此 async_list_view
减少了潜在的昂贵数据库读取操作。
示例用例
- 显示从 Firestore 获取的用户聊天记录。
- 显示在线市场上的搜索结果。
- 显示从大文件中读取的日志行。
任何贡献、错误报告或功能请求都受到欢迎。
完整示例
以下是一个完整的示例代码,展示了如何使用 async_list_view
插件来实现异步加载列表。
import 'package:async_list_view/async_list_view.dart';
import 'package:flutter/material.dart';
import 'mock_database.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 2,
child: SafeArea(
child: Scaffold(
appBar: AppBar(
title: const Text('AsyncListView demo!'),
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.menu_book)),
Tab(icon: Icon(Icons.add)),
],
),
),
body: const TabBarView(
children: [
LazyFruitList(),
Center(
child: SelectableText(
'To get a fruit added to the fruit list, please file a bug '
'report:\n\n'
'https://github.com/caseycrogers/async_list_view/issues/new?assignees=caseycrogers&labels=high-priority&template=fruit-request-template.md&title=%5BFruit%5D+Add+%3Cinsert-fruit-name-here%3E+to+the+Fruit+List',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black38,
),
textAlign: TextAlign.center,
),
),
],
),
),
),
),
);
}
}
class LazyFruitList extends StatefulWidget {
const LazyFruitList({Key? key}) : super(key: key);
[@override](/user/override)
_LazyFruitListState createState() => _LazyFruitListState();
}
class _LazyFruitListState extends State<LazyFruitList> {
// 当前已加载的水果数量
int _loadedFruits = 0;
// 满足搜索条件的水果总数
int _totalFruits = countMatchingFruits('');
String _searchString = '';
late Stream<String> _fruitStream;
List<String> initialFruits = [];
void _initializeFruitStream() {
_loadedFruits = 0;
_totalFruits = countMatchingFruits(_searchString);
_fruitStream = getFruits(_searchString).map((fruit) {
// 增加计数,因为我们不能在 `itemBuilder` 中调用 `setState`
// 使用 `map` 而不是 `listen`,因为 `listen` 需要广播流,而广播流不能暂停流的底层源。
// 提示:广播流不是你的朋友。尽量避免使用它。
setState(() {
_loadedFruits += 1;
});
return fruit;
});
}
[@override](/user/override)
void initState() {
_initializeFruitStream();
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
children: [
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
initialFruits = [
initialFruits.length.toString(),
...initialFruits,
];
});
},
),
TextField(
onChanged: _onTextChanged,
decoration: const InputDecoration(
hintText: 'Search Fruits...',
),
),
Expanded(
child: AsyncListView<String>(
// 如果相同的流重复传递给 AsyncListView,则 AsyncListView 将保持其状态并不会错误地两次监听同一个流。
stream: _fruitStream,
itemBuilder: _buildFruitTile,
initialData: initialFruits,
// 如果用户滚动到当前已加载的水果之外,则显示 'loading...' 文本,以告知他们需要等待更多结果。
loadingWidget: const Padding(
padding: EdgeInsets.all(8),
child: Text(
'loading...',
style: TextStyle(fontSize: 20, color: Colors.black54),
),
),
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
noResultsWidgetBuilder: (context) {
return SelectableText(
'No fruits found for search term \'$_searchString\'. '
'If you feel a fruit has excluded in error, please file '
'a bug report:'
'\n\nhttps://github.com/caseycrogers/async_list_view/issues/new?assignees=caseycrogers&labels=high-priority&template=fruit-request-template.md&title=%5BFruit%5D+Add+%3Cinsert-fruit-name-here%3E+to+the+Fruit+List',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black38,
),
textAlign: TextAlign.center,
);
},
),
),
Container(
color: Colors.blueAccent.shade100,
child: Center(
child: Text(
'$_loadedFruits/$_totalFruits fruits loaded!',
style: const TextStyle(fontSize: 18),
),
),
),
],
);
}
// 接收加载的水果快照和 ListView 索引,并构建相应的项目。
Widget _buildFruitTile(
BuildContext context, AsyncSnapshot<List<String>> snapshot, int index) {
_loadedFruits = snapshot.data?.length ?? 0;
return ListTile(
title: Text(
snapshot.data?[index] ?? 'Something went wrong!!!',
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
);
}
void _onTextChanged(String newSearchString) {
// 如果搜索文本发生变化,才更新流,以避免昂贵的重复数据库查询。
if (_searchString == newSearchString) {
return;
}
setState(() {
_searchString = newSearchString;
_initializeFruitStream();
});
}
}
更多关于Flutter异步加载列表插件async_list_view的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter异步加载列表插件async_list_view的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,async_list_view
是一个用于在 Flutter 中异步加载列表项的插件。虽然 Flutter 本身没有直接提供名为 async_list_view
的标准库插件,但你可以通过结合 FutureBuilder
或 StreamBuilder
以及分页逻辑来实现类似的功能。
以下是一个使用 FutureBuilder
和分页逻辑来实现异步加载列表的示例代码。在这个例子中,我们假设你有一个 API 可以分页获取数据。
示例代码
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: AsyncListViewDemo(),
);
}
}
class AsyncListViewDemo extends StatefulWidget {
@override
_AsyncListViewDemoState createState() => _AsyncListViewDemoState();
}
class _AsyncListViewDemoState extends State<AsyncListViewDemo> {
List<Map<String, dynamic>> items = [];
int currentPage = 1;
bool hasMore = true;
final String apiUrl = "https://api.example.com/items"; // 替换为你的API URL
@override
void initState() {
super.initState();
loadMoreItems();
}
Future<void> loadMoreItems() async {
if (!hasMore) return;
try {
final response = await http.get(Uri.parse("$apiUrl?page=$currentPage"));
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List<dynamic>;
if (data.isEmpty) {
hasMore = false;
} else {
setState(() {
items.addAll(data.map((item) => Map.from(item)).toList());
currentPage += 1;
});
}
}
} catch (e) {
print("Error loading items: $e");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Async ListView Demo'),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]['title']), // 假设API返回的数据中有title字段
);
},
),
),
if (hasMore)
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Center(
child: ElevatedButton(
onPressed: loadMoreItems,
child: Text('Load More'),
),
),
),
],
),
);
}
}
解释
-
状态管理:
items
存储已加载的列表项。currentPage
存储当前加载的页码。hasMore
标记是否还有更多数据可以加载。
-
数据加载:
loadMoreItems
方法使用http.get
异步加载数据。- 如果响应状态码为 200,并且返回的数据不为空,则将数据添加到
items
列表中,并递增currentPage
。 - 如果返回的数据为空,则将
hasMore
设置为false
。
-
UI 构建:
- 使用
ListView.builder
构建列表。 - 如果
hasMore
为true
,则显示一个按钮用于加载更多数据。
- 使用
这种方法结合了 Flutter 的基础组件和异步编程模式,实现了类似于 async_list_view
的功能。如果你有一个更具体的 async_list_view
插件或库,请提供更多信息,以便给出更准确的示例代码。