我是 Flutter 新手,我不确定这是否是预期行为以及常见的 UI/UX 模式。
我有一个带有 ListView 的页面,每个图块都包含一个图像和文本。这些图块是从对象列表生成的。每个对象都有两个属性:text 和 imageId。 imageId 属性用于从本地数据库的“images”表中检索图像。 “images”表有两列:id (TEXT) 和图片 (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 秒才能加载。
图像从本地数据库加载需要这么长时间,这正常吗?从用户体验的角度来看,在 1/2 秒内显示许多加载程序是很丑陋的。我可以在显示 ListView 之前在中心显示一个加载程序,但我不知道如何让图像小部件加载图像并在显示所有内容之后。
我再次尝试使用 PrecacheImage。
在本例中,图像是 MemoryImage 而不是 Uint8List,Image 小部件使用 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.toList();
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 示例几乎可以处理所有可以转换为字节数据的内容,而且速度也很快