我有一个带有
ListView
的页面,每个图块都包含图像和文本。这些图块是从对象列表生成的。每个对象都有属性 text
和 imageId
。 imageId
用于从本地数据库的“images”表中检索图像。 “图像”表具有列 id
(TEXT) 和 picture
(BLOB)。我将 SQLite 与 sqflite(移动)和 sqflite_common_ffi(桌面)一起使用。
List<Request> _requests = [];
List<ImagesItem> _images = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
loadRequests();
}
void loadRequests() async {
try {
var requests = await RequestsRepository().getAllRequests();
requests = requests.take(1).toList(); // take(1) for testing purposes
var alreadyStoredImages = _images.map((image) => image.id).toSet();
var images = await ImageRepository().getImagesByIds(
requests
.map((request) => request.imageId)
.where((imageId) => !alreadyStoredImages.contains(imageId))
.toList(),
);
setState(() {
_images = images;
_requests = requests;
});
} catch (error) {
// Handle error
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _isLoading
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: _requests.length,
itemBuilder: (context, index) {
final request = _requests[index];
final image = _images.firstWhere(
(img) => img.id == request.imageId,
);
return ListTile(
leading: Image.memory(
image.picture,
width: 50,
height: 50,
),
);
},
),
);
}
文本会立即出现,但图像需要大约 2 秒才能加载。从本地数据库加载图像需要这么长时间,这正常吗?我可以在显示 ListView 之前显示加载程序,但我不知道如何让图像小部件加载图像,然后显示所有内容。
在另一次使用
PrecacheImage
的尝试中,图像是 MemoryImage
而不是 Uint8List
。图像小部件使用 MemoryImage
而不是 Image.memory()
。页面几乎立即加载。代码更改为 precacheImage
:
class _RequestWithImagePreCache {
final String id;
final MemoryImage image;
_RequestWithImagePreCache(this.id, this.image);
}
List<_RequestWithImagePreCache> _images = [];
void loadRequests() async {
try {
var requests = await RequestsRepository().getAllRequests();
requests = requests.take(1).toList(); // take(1) for testing purposes
var alreadyStoredImages = _images.map((image) => image.id).toSet();
var images = await ImageRepository().getImagesByIds(
requests
.map((request) => request.imageId)
.where((imageId) => !alreadyStoredImages.contains(imageId))
.toList(),
);
List<_RequestWithImagePreCache> imagesCached = [];
await Future.wait(images.map((image) async {
final memoryImage = MemoryImage(image.picture);
await precacheImage(memoryImage, context);
imagesCached.add(_RequestWithImagePreCache(image.id, memoryImage));
}));
setState(() {
_images = imagesCached;
_requests = requests;
});
} catch (error) {
// Handle any errors during data fetching
} finally {
setState(() {
_isLoading = false;
});
}
}
我不清楚
precacheImage
的使用,因为所有指南都在从网络检索的图像上下文中使用它,或者使用我相信使用 precacheImage
或类似内容的包 cached_network_image 。是否有可遵循的行为以及为什么即使我必须将图像推送到缓存而不是从数据库检索后直接使用它们,第二种情况加载速度也会更快?
也许本地数据库需要时间来查询和返回,但是
cached_network_image
使用CacheManager,这样速度会更快,因为它从缓存中获取图像,有时如果您有记录器记录从源(db,api)获得的内容,它也会减慢您的进程
示例如何使用 DefaultCachedManager 缓存图像字节
class CachedBytesManager {
final CacheManager _cacheManager = DefaultCacheManager();
Future<Uint8List> handleGetImageBytes(String fragment) async {
// Get image from internet as bytes
return res.data;
}
Future<File> getBytesCached({
required String fragment,
}) async {
final fileInfo = await _cacheManager.getFileFromMemory(fragment);
// Explain : store file if don't have file in cache
if (fileInfo == null) {
final imageBytes = await handleGetImageBytes(fragment);
// Explain : store bytes in cache
final file = await _cacheManager.putFile(
'',
imageBytes,
// Explain : key to get cache
key: fragment.toString(),
// Explain : cached duration
maxAge: const Duration(days: 7),
);
// Explain : then return file from store cache process
return file;
}
// Explain : return file if cache contain data
return fileInfo.file;
}
}
实际上这个 CacheManager 示例几乎可以处理所有可以转换为字节数据的内容,而且速度也很快