我正在尝试根据来自后端的 (base64) 数据显示图像,但我不断收到错误 bytes != null': is not true.
这是我的代码:
class _FuncState extends State<Func> {
Uint8List userIconData;
void initState() {
super.initState();
updateUI();
}
void updateUI() async {
await getUserIconData(1, 2, 3).then((value) => userIconData = value);
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
Container(
child: CircleAvatar(
backgroundImage: Image.memory(userIconData).image, // <--- problem here
maxRadius: 20,
),
),
),
);
}
}
帮助代码:
Future<Uint8List> getUserIconData(
role,
id,
session,
) async {
var url = Uri.https(kMobileAppAPIURL, kMobileAppAPIFolder);
var response = await http.post(url, body: {
'method': 'getUserProfilePic',
'a': id.toString(),
'b': role.toString(),
'c': session.toString(),
});
if (response.statusCode == 200) {
Map data = jsonDecode(response.body);
return base64Decode(data['img']);
}
return null;
}
我已使用调试器逐步完成代码,并确认辅助函数正在为图像返回正确的字节序列。
感谢您的指点。
进一步说明。错误还说:
<块引用>要么断言表明框架本身存在错误,要么我们 应在此错误消息中提供更多信息 帮助您确定并解决根本原因。在任一情况下, 请通过在 GitHub 上提交错误报告此断言
答案 0 :(得分:2)
这很简单;如果您看一下您的代码,您应该能够完成此操作序列。
initState
被调用。 异步 http 调用已启动。 userIconData == nullbuild
被调用。 发生构建,抛出错误。 userIconData == null由于没有调用 setState
,您的构建函数将不会再次运行。如果你这样做了,就会发生这种情况(但你之前仍然会有例外)。
build
被调用。 userIconData 已设置。 userIconData == 您的图片这里的关键是理解异步调用(任何返回未来并可选择使用 async
和 await
的东西)不会立即返回,而是在稍后的某个时间返回,并且您不能依靠他们在此期间设置了您需要的东西。如果您之前尝试过使用从磁盘加载的图像来执行此操作并且成功,那只是因为 Flutter 执行了一些技巧,这些技巧只有在从磁盘加载是同步的情况下才有可能。
这里有两个选项可以替代您编写代码的方式。
class _FuncState extends State<Func> {
Uint8List? userIconData;
// if you're using any data from the `func` widget, use this instead
// of initState in case the widget changes.
// You could also check the old vs new and if there has been no change
// that would need a reload, not do the reload.
@override
void didUpdateWidget(Func oldWidget) {
super.didUpdateWidget(oldWidget);
updateUI();
}
void updateUI() async {
await getUserIconData(widget.role, widget.id, widget.session).then((value){
// this ensures that a rebuild happens
setState(() => userIconData = value);
});
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
// this only uses your circle avatar if the image is loaded, otherwise
// show a loading indicator.
child: userIconData != null ? CircleAvatar(
backgroundImage: Image.memory(userIconData!).image,
maxRadius: 20,
) : CircularProgressIndicator(),
),
),
);
}
}
做同样事情的另一种方法是使用 FutureBuilder。
class _FuncState extends State<Func> {
// using late isn't entirely safe, but we can trust
// flutter to always call didUpdateWidget before
// build so this will work.
late Future<Uint8List> userIconDataFuture;
@override
void didUpdateWidget(Func oldWidget) {
super.didUpdateWidget(oldWidget);
userIconDataFuture =
getUserIconData(widget.role, widget.id, widget.session);
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
child: FutureBuilder(
future: userIconDataFuture,
builder: (BuildContext context, AsyncSnapshot<Uint8List> snapshot) {
if (snapshot.hasData) {
return CircleAvatar(
backgroundImage: Image.memory(snapshot.data!).image,
maxRadius: 20);
} else {
return CircularProgressIndicator();
}
},
),
),
),
);
}
}
请注意,加载指示器只是一种选择;我实际上建议为您的头像(即灰色的“用户”图像)设置一个硬编码的默认值,在加载图像时将其关闭。
请注意,我在这里使用了空安全代码,因为这将使此答案具有更长的使用寿命,但是要切换回非空安全代码,您只需删除无关的 ?
、{{1 }} 和 !
在代码中。
答案 1 :(得分:1)
错误信息对我来说很清楚。当您将 userIconData
传递给 null
构造函数时,它是 Image.memory
。
在渲染图像之前使用 FutureBuilder 或条件来检查 userIconData
是否为空,如果是,则手动显示加载指示器,或类似的内容。此外,您还需要实际设置状态以触发重新渲染。不过我会选择前者。