如何解决Flutter重做的问题:带按钮的小部件和带倒数计时器的小部件之间的问题共享状态
几天以来,我一直在尝试通过用户互动将带有倒数计时器的有状态子级动画小部件连接到父级有状态小部件。我在一个类似的问题(使用Tween,我没有用)上找到了来自Andrey的answer,它已经帮了很多忙,但我还是没有用。我的假设是,孩子的initState可能是原因。计时器的代码来自here。
我删除了很多代码,包括一些引用的函数/类。这样应该可以更清楚地了解逻辑:
- 在MainPageState中,我声明并初始化动画的_controller
- 在MainPageState中,我将无状态窗口小部件CreateKeypad托管为“ go”键
- 单击go后,此事件将返回MainPageState并执行
_controller.reverse(from: 1.0);
- 在MainPageState中,我调用有状态的控件CountDownTimer来呈现计时器
- 在_CountDownTimerState中,我不确定自己的initState是否正确
- 在_CountDownTimerState中,我使用timer code source 中的CustomTimerPainter构建动画
动画应在顶部渲染一个白色的甜甜圈和一个逐渐减小的红色弧。但是,我只看到白色的甜甜圈,没有看到红色计时器的弧线。任何提示都将受到高度赞赏。
class MainPage extends StatefulWidget {
MainPage({Key key,this.title}) : super(key: key);
final String title;
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> with TickerProviderStateMixin {
AnimationController _controller;
var answer="0",correctAnswer = true,result = 0;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this,duration: Duration(seconds: 7));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
),child: SafeArea(
child: Container(
child: Column(
children: <Widget>[
CreateKeypad( // creates a keypad with a go button. when go is clicked,countdown shall start
prevInput: int.parse((answer != null ? answer : "0")),updtedInput: (int val) {
setState(() => answer = val.toString());
},goSelected: () {
setState(() {
if (answer == result.toString()) {
correctAnswer = true;
}
final problem = createProblem();
result = problem.result;
});
_controller.reverse(from: 1.0); // start the countdown animation
Future.delayed(const Duration(milliseconds: 300,),() => setState(() => correctAnswer = true));
},CountDownTimer(_controller),// show countdown timer
]
),)
);
}
}
// CREATE KEYPAD - all keys but "1! and "go" removed
class CreateKeypad extends StatelessWidget {
final int prevInput;
final VoidCallback goSelected;
final Function(int) updtedInput;
CreateKeypad({@required this.prevInput,@required this.updtedInput,this.goSelected});
@override
Widget build(BuildContext context) {
return Row(
children: <Widget> [
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(2.0),child: SizedBox(
width: 80.0,height: 80.0,child: CupertinoButton(
child: Text("1",style: TextStyle(color: CupertinoColors.black)),onPressed: () {
updtedInput(1);
},Padding(
padding: const EdgeInsets.all(2.0),child: CupertinoButton(
child: Text("Go!",onPressed: () => goSelected(),],]
);
}
}
// CREATE COUNTDOWN https://medium.com/flutterdevs/creating-a-countdown-timer-using-animation-in-flutter-2d56d4f3f5f1
class CountDownTimer extends StatefulWidget {
CountDownTimer(this._controller);
final AnimationController _controller;
@override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer> with TickerProviderStateMixin {
@override
void initState() {
super.initState(); // here I have some difference to Andrey's answer because I do not use Tween
}
String get timerString {
Duration duration = widget._controller.duration * widget._controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60)
.toString()
.padLeft(2,'0')}';
}
@override
Widget build(BuildContext context) {
return Container(
child: AnimatedBuilder(
animation: widget._controller,builder:
(BuildContext context,Widget child) {
return CustomPaint(
painter: CustomTimerPainter( // this draws a white donut and a red diminishing arc on top
animation: widget._controller,backgroundColor: Colors.white,color: Colors.red,));
},);
}
}
解决方法
您可以在下面复制粘贴运行完整代码
步骤1:您可以将controller
放在CountDownTimerState
内
步骤2:使用GlobalKey
CountDownTimer(key: _key)
第3步:使用start()
调用_CountDownTimerState
内部的函数_key.currentState
goSelected: () {
setState(() {
...
_controller.reverse(from: 10.0); // start the countdown animation
final _CountDownTimerState _state = _key.currentState;
_state.start();
...
class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
_controller =
AnimationController(vsync: this,duration: Duration(seconds: 7));
super
.initState(); // here I have some difference to Andrey's answer because I do not use Tween
}
...
void start() {
setState(() {
_controller.reverse(from: 1.0);
});
}
工作演示
完整代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
class MainPage extends StatefulWidget {
MainPage({Key key,this.title}) : super(key: key);
final String title;
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> with TickerProviderStateMixin {
AnimationController _controller;
var answer = "0",correctAnswer = true,result = 0;
GlobalKey _key = GlobalKey();
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this,duration: Duration(seconds: 7));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
//navigationBar: CupertinoNavigationBar(),child: SafeArea(
child: Container(
color: Colors.blue,child: Column(children: <Widget>[
CreateKeypad(
// creates a keypad with a go button. when go is clicked,countdown shall start
prevInput: int.parse((answer != null ? answer : "0")),updtedInput: (int val) {
setState(() => answer = val.toString());
},goSelected: () {
setState(() {
if (answer == result.toString()) {
correctAnswer = true;
}
/*final problem = createProblem();
result = problem.result;*/
});
print("go");
_controller.reverse(from: 10.0); // start the countdown animation
final _CountDownTimerState _state = _key.currentState;
_state.start();
/* Future.delayed(
const Duration(
milliseconds: 300,),() => setState(() => correctAnswer = true));*/
},Container(
height: 400,width: 400,child: CountDownTimer(key: _key)),// show countdown timer
]),));
}
}
// CREATE KEYPAD - all keys but "1! and "go" removed
class CreateKeypad extends StatelessWidget {
final int prevInput;
final VoidCallback goSelected;
final Function(int) updtedInput;
CreateKeypad(
{@required this.prevInput,@required this.updtedInput,this.goSelected});
@override
Widget build(BuildContext context) {
return Row(children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(2.0),child: SizedBox(
width: 80.0,height: 80.0,child: CupertinoButton(
child:
Text("1",style: TextStyle(color: CupertinoColors.black)),onPressed: () {
updtedInput(1);
},Padding(
padding: const EdgeInsets.all(2.0),child: CupertinoButton(
child:
Text("Go!",onPressed: () => goSelected(),],]);
}
}
// CREATE COUNTDOWN https://medium.com/flutterdevs/creating-a-countdown-timer-using-animation-in-flutter-2d56d4f3f5f1
class CountDownTimer extends StatefulWidget {
CountDownTimer({Key key}) : super(key: key);
//final AnimationController _controller;
@override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
_controller =
AnimationController(vsync: this,duration: Duration(seconds: 7));
super
.initState(); // here I have some difference to Andrey's answer because I do not use Tween
}
String get timerString {
Duration duration = _controller.duration * _controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2,'0')}';
}
void start() {
setState(() {
_controller.reverse(from: 1.0);
});
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green,child: AnimatedBuilder(
animation: _controller,builder: (BuildContext context,Widget child) {
return CustomPaint(
painter: CustomTimerPainter(
// this draws a white donut and a red diminishing arc on top
animation: _controller,backgroundColor: Colors.green,color: Colors.red,));
},);
}
}
class CustomTimerPainter extends CustomPainter {
CustomTimerPainter({
this.animation,this.backgroundColor,this.color,}) : super(repaint: animation);
final Animation<double> animation;
final Color backgroundColor,color;
@override
void paint(Canvas canvas,Size size) {
Paint paint = Paint()
..color = backgroundColor
..strokeWidth = 10.0
..strokeCap = StrokeCap.butt
..style = PaintingStyle.stroke;
canvas.drawCircle(size.center(Offset.zero),size.width / 2.0,paint);
paint.color = color;
double progress = (1.0 - animation.value) * 2 * math.pi;
//print("progress ${progress}");
canvas.drawArc(Offset.zero & size,math.pi * 1.5,-progress,false,paint);
}
@override
bool shouldRepaint(CustomTimerPainter old) {
//print(animation.value);
return true;
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',theme: ThemeData(
primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,home: MainPage(),);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。