颤振:如何改变状态从一个孩子AnimatedSwitcher的小部件



我创建了一个屏幕在扑动,显示一个问题,并显示可能的答案作为按钮数组。问题和答案位于AnimatedSwitcher小部件中,因此单击答案按钮后,将显示下一个问题和答案。不幸的是,AnimatedSwitcher小部件只有在按钮位于子小部件之外时才能工作。这不是我想要的行为,因为我希望答案和按钮都是动画的一部分。

是否有办法做到这一点,或者可能有一个更好的小部件使用?谢谢你的帮助!

import 'package:flutter/material.dart';
int index = 0;
final widgets = [
QuestionWidget(
question:
Question("What is your favorite color?", ["red", "blue", "green"]),
key: const Key('1')),
QuestionWidget(
question: Question("How do you do today?", ["great", "not so well"]),
key: const Key('2')),
QuestionWidget(
question: Question("Do you like Flutter", ["yes", "no"]),
key: const Key('3')),
];
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
@override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
body: Center(
child: SizedBox(
width: size.width * 0.7,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
transitionBuilder: (child, animation) =>
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: const Offset(0.0, 0.0))
.animate(animation),
child: FadeTransition(
opacity: animation, child: child)),
child: widgets[index]),
],
))));
}
}
class QuestionWidget extends StatefulWidget {
final Question question;
const QuestionWidget({
required this.question,
Key? key,
}) : super(key: key);
@override
State<QuestionWidget> createState() => _QuestionWidgetState();
}
class _QuestionWidgetState extends State<QuestionWidget> {
@override
Widget build(BuildContext context) {
return Column(children: [
Text(
widget.question.questionText,
style: const TextStyle(
fontSize: 25,
),
),
Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.center,
spacing: 5.0,
children: [
for (var i in widget.question.answers)
ElevatedButton(
child: Text(i.toString()),
onPressed: () {
final isLastIndex = index == widgets.length - 1;
setState(() => index = isLastIndex ? 0 : index + 1);
})
],
)
]);
}
}
class Question {
String questionText;
List<String> answers;
Question(this.questionText, this.answers);
}

在Flutter中,在全局变量中存储可变状态不是一种有效的方法。

颤振发展的主要规则之一是:状态向下,事件向上

在您的示例中,似乎Test小部件应该负责定义当前问题的索引,因此您需要将其作为State的一部分。Question小部件不应该关心当选择正确答案时该做什么,它应该只知道如何检测这样的事件以及通知谁。

把它们放在一起:

  1. Test应存储当前问题索引
  2. Test应该选择在给定时刻显示哪个Question
  3. Question应在选择正确答案时通知Test
  4. Test应根据上述事件更改当前索引。

在你的例子中,通知事件可以仅仅是调用构造函数参数中提供的回调函数。

在代码:

class TestState extends State<Test> {
int _index = 0;
@override
Widget build(BuildContext context) {
...
QuestionWidget(
key: Key(index.toString()),
question: questions[index],
onCorrectAnswer: () => setState(() => index++)),
),
...
}
}
class QuestionWidget extends StatelessWidget {
final void Function() onCorrectAnswer;
@override
Widget build(BuildContext context) {
...
ElevatedButton(
onPressed: () => onCorrectAnswer(),
),
...
}
}

我强烈推荐阅读Flutter文档对状态管理的看法

在mfkw1的回答的帮助下,我想出了这个解决方案。我对此有点挣扎,因为我没有看到QuestionWidget变成了StatelessWidget

import 'package:flutter/material.dart';
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
@override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
int index = 0;
next() {
final isLastIndex = index == 2;
setState(() => index = isLastIndex ? 0 : index + 1);
}
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final widgets = [
QuestionWidget(
question: Question(
"What is your favorite color?", ["red", "blue", "green"]),
key: const Key("1"),
onAnswer: () => next()),
QuestionWidget(
question: Question("How do you do today?", ["great", "not so well"]),
key: const Key("2"),
onAnswer: () => next()),
QuestionWidget(
question: Question("Do you like Flutter?", ["yes", "no"]),
key: const Key("3"),
onAnswer: () => next()),
];
var size = MediaQuery.of(context).size;
return Scaffold(
body: Center(
child: SizedBox(
width: size.width * 0.7,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
transitionBuilder: (child, animation) =>
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: const Offset(0.0, 0.0))
.animate(animation),
child: FadeTransition(
opacity: animation, child: child)),
child: widgets[index]),
],
))));
}
}
class QuestionWidget extends StatelessWidget {
final Question question;
final Function() onAnswer;
const QuestionWidget({
required this.question,
required this.onAnswer,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(children: [
Text(
question.questionText,
style: const TextStyle(
fontSize: 25,
),
),
Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.center,
spacing: 5.0,
children: [
for (var i in question.answers)
ElevatedButton(
child: Text(i.toString()),
onPressed: () {
onAnswer();
})
],
)
]);
}
}
class Question {
String questionText;
List<String> answers;
Question(this.questionText, this.answers);
}