在我的代码中,我有一个方法可以根据 Widget 的键确定其位置,我在调用 onTap 时调用此函数。问题是我的代码在启动时崩溃,因为它试图获取未渲染的小部件位置。
现在我正在创建一个练习,基本上是 Duolingo 的句子形成练习的副本。在经历了这么多错误并且不得不多次更改整个布局之后,我考虑使用一个包含单词的主堆栈和一个包含两个容器(单词甲板和放置它们的位置)的列,并在其中放置“占位符”。然后,当您启动应用程序时,这些单词将位于与其各自占位符相同的位置,并且当您点击它们时,它们将转到屏幕中创建的新单词。就像移动 p 检查我的意思这里。
我面临的问题是,当我尝试使用占位符的键(目标键和原始键)来移动小部件时,我遇到了空检查运算符异常,因为从技术上讲它们尚未呈现。我尝试过使用 WidgetsBinding.instance.addPostFrameCallback 但它不能修复错误。我需要一种方法来计算它们的位置并在应用程序开始时“生成”其中的单词。
为了计算偏移量,我使用了一种方法(“getOffset”)
这是示例代码:
方法是这样的:
Offset getOffset(GlobalKey key) {
Offset offset = Offset.zero;
BuildContext context = key.currentContext!; //<----- This is where the exception is thrown
RenderBox renderBox = context.findRenderObject() as RenderBox;
offset = renderBox.localToGlobal(Offset.zero);
return offset;
}
这是我的代码:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Animation Proof of Concept',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.black),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Container xo =
Container(color: Colors.white, width: 65, height: 65, child: Text("Xo"));
Container vo =
Container(color: Colors.white, width: 65, height: 65, child: Text("Vo"));
List<Container> letters = [];
List<Widget> targets = [];
Map<int, GlobalKey> targetKeys = {};
Map<int, GlobalKey> homeKeys = {};
List<bool> isMovedList = [];
@override
void initState() {
super.initState();
letters = [vo];
isMovedList = [for (var widget in letters) false];
targetKeys = {
for (var widget in letters)
letters.indexOf(widget):
GlobalKey(debugLabel: "targetKey${letters.indexOf(widget)}")
};
homeKeys = {
for (var widget in letters)
letters.indexOf(widget):
GlobalKey(debugLabel: "homeKey${letters.indexOf(widget)}")
};
}
getOffset(GlobalKey key) {
Offset offset = Offset.zero;
BuildContext context = key.currentContext!;
RenderBox renderbox = context.findRenderObject() as RenderBox;
offset = renderbox.localToGlobal(Offset.zero);
return offset;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.purple,
body: Stack(
children: [
Column(
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: targets,
)),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: letters.map((letter) {
return Opacity(
key: homeKeys[letters.indexOf(letter)],
opacity: 0.2,
child: letter,
);
}).toList(),
))
],
),
...letters.map((letter) {
Offset targetOffset = Offset.zero;
Offset homeOffset = Offset.zero;
return AnimatedPositioned(
left: isMovedList[letters.indexOf(letter)] ? 312.5 : 312.5,
top: isMovedList[letters.indexOf(letter)] ? 235 : 55,
duration: const Duration(milliseconds: 250),
child: GestureDetector(
onTap: () {
setState(() {
WidgetsBinding.instance.addPostFrameCallback((_) {});
isMovedList[letters.indexOf(letter)] =
!isMovedList[letters.indexOf(letter)];
if (isMovedList[letters.indexOf(letter)] == false) {
targets.add(Container(
key: targetKeys[letters.indexOf(letter)],
color: Colors.red,
width: 65,
height: 65,
child: Center(
child: Text(
"T${letters.indexOf(letter)}",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20),
))));
} else {
targets.removeWhere((element) =>
element.key ==
targetKeys[letters.indexOf(letter)]);
}
});
},
child: letter));
}),
],
),
);
}
}
我尝试搬迁
WidgetsBinding.instance.addPostFramCallback()
但它
还是不行。
我的代码有几个问题,
修复:将 targetKeys 的偏移量计算包装在 if 语句中:
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
for (int i = 0; i < letters.length; i++) {
if (targetKeys[i]?.currentContext != null) {
targetOffsets[i] = getOffset(targetKeys[i]!);
}
}
});
});
WidgetsBinding.instance.addPostFrameCallback()
中计算 initState(){}
才能正常工作。
修复:将其添加到 `initState(){}我创建了两个 Map 来存储 targetOffsets 和 homeOffsets,其偏移量与键的 Map 相同,默认值为 Offset.zero。因此,在运行代码的第一秒(并非创建每个小部件),有一个默认值。这就是导致它不可能实现的主要错误。 修复这些错误并将代码调整得更好后,它按预期工作。