我正在尝试在 Flutter 中创建一个登录页面,并希望在推送到下一页之前进行动画处理。一切正常,除了做(登录中的错误信息,所以它调用“failedLoginAnimation”函数,当我在表单中输入正确的信息之后)它开始在它的末尾动画我的应用程序崩溃并显示给我但是当我输入在一切正常之前,直接正确的信息而不犯错误。PS:我是新来的,大约一个月左右,甚至更多的动画。
══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (11398): The following assertion was thrown while notifying listeners for AnimationController:
I/flutter (11398): Looking up a deactivated widget's ancestor is unsafe.
I/flutter (11398): At this point the state of the widget's element tree is no longer stable. To safely refer to a
I/flutter (11398): widget's ancestor in its dispose() method, save a reference to the ancestor by calling
I/flutter (11398): inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.
I/flutter (11398):
I/flutter (11398): When the exception was thrown, this was the stack:
I/flutter (11398): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3232:9)
I/flutter (11398): #1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3241:6)
I/flutter (11398): #2 Element.ancestorStateOfType (package:flutter/src/widgets/framework.dart:3289:12)
I/flutter (11398): #3 Navigator.of (package:flutter/src/widgets/navigator.dart:1271:19)
I/flutter (11398): #4 Navigator.popAndPushNamed (package:flutter/src/widgets/navigator.dart:825:22)
I/flutter (11398): #5 StaggerAnimationSignIn.build.<anonymous closure> (package:test/premium/pages/login/loginAnimation.dart:146:25)
I/flutter (11398): #6 _AnimationController&Animation&AnimationEagerListenerMixin&AnimationLocalListenersMixin.notifyListeners (package:flutter/src/animation/listener_helpers.dart:124:19)
I/flutter (11398): #7 AnimationController._tick (package:flutter/src/animation/animation_controller.dart:693:5)
I/flutter (11398): #8 Ticker._tick (package:flutter/src/scheduler/ticker.dart:228:5)
I/flutter (11398): #9 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter (11398): #10 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleBeginFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:906:11)
I/flutter (11398): #11 __InternalLinkedHashMap&_HashVMBase&MapMixin&_LinkedHashMapMixin.forEach (dart:collection/runtime/libcompact_hash.dart:370:8)
I/flutter (11398): #12 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleBeginFrame (package:flutter/src/scheduler/binding.dart:904:17)
I/flutter (11398): #13 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleBeginFrame (package:flutter/src/scheduler/binding.dart:834:5)
I/flutter (11398): #14 _invoke1 (dart:ui/hooks.dart:159:13)
I/flutter (11398): #15 _beginFrame (dart:ui/hooks.dart:129:3)
I/flutter (11398):
I/flutter (11398): The AnimationController notifying listeners was:
I/flutter (11398): AnimationController#97d39(⏭ 1.000; paused)
I/flutter (11398): ════════════════════════════════════════════════════════════════════════════════════════════════════
我的登录页面
import 'package:flutter/material.dart';
import 'loginAnimation.dart';
import 'freeAccessAnimation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/animation.dart';
import 'package:test/premium/tools/jsonTools.dart';
import 'dart:async';
import 'package:test/premium/tools/design/color/designColors.dart';
import './Components/PremiumLink.dart';
import './Components/ForgotPasswordLink.dart';
import './Components/Logo.dart';
import './Components/SignInButton.dart';
import './Components/FreeAccessButton.dart';
import 'package:flutter/services.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
import 'package:test/premium/tools/internet/checkInternet.dart';
import 'package:test/premium/pages/tools/alerts.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:test/premium/variables/variablesApp.dart';
import 'package:test/premium/tools/internet/sendHTTPRequest.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key key}) : super(key: key);
@override
LoginScreenState createState() => new LoginScreenState();
}
class LoginScreenState extends State<LoginScreen>
with TickerProviderStateMixin {
static bool hasFinishedLogin;
static bool hasFailedLogin;
static String userLoginToken ;
static String tokenFileExist ;
static String backToLogin;
static bool isFirstCallAction;
static bool hasFinishLogAnim;
AnimationController _loginButtonController;
AnimationController _freeButtonController;
static final usernameController = TextEditingController();
static final passwordController = TextEditingController();
static var animationStatusLogin = 0;
static var animationStatusFree = 0;
Future pause(Duration d) => new Future.delayed(d);
_launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
void tokensManagement(){
if(userLoginToken == null && tokenFileExist == null && backToLogin == "true") {
JTools.deleteFileToken().then((e){
if(e == "Token has been deleted"){
setState(() {
tokenFileExist = 'false';
userLoginToken = null;
backToLogin = 'false';
});
}
});
}else{
JTools.readDataToken().then((e){
String _token = e;
pause(new Duration(milliseconds: 500)).then((e){
JTools.tokenFileExist().then((value){
tokenFileExist = value.toString();
userLoginToken = _token;
}).then((b){
if(_token != null){
if(userLoginToken.isNotEmpty && tokenFileExist == 'true'){
//TODO: on loading
APIToken.token = _token;
CheckInternet().checkInternet().then((e){
if(e == true){
PostHTTP().sendRequestAPI(APIToken.token, 'membership').then((value){
if(value['status'] == "200"){
String membership = value['membership'];
print(membership);
if(membership == "138" || membership == "136"){
print('User is premium');
Navigator.of(context).pushNamed('/main');
}else {
print("User isn't premium ");
JTools.deleteFileToken().then((e) {
if (e == "Token has been deleted") {
setState(() {
tokenFileExist = 'false';
userLoginToken = null;
});
pause(Duration(milliseconds: 1000)).then((e){
Alerts(context: context).alertNotPremium();
});
}
});
}
}else{
if(value['status'] == "404"){
setState(() {
tokenFileExist = 'false';
userLoginToken = null;
backToLogin = 'false';
});
pause(Duration(milliseconds: 1000)).then((e){
Alerts(context: context).alertErrorConnectionServer();
});
}
if(value['status'] == "401" || value['status'] == "403")
{print("User isn't premium ");
JTools.deleteFileToken().then((e) {
if (e == "Token has been deleted") {
setState(() {
tokenFileExist = 'false';
userLoginToken = null;
backToLogin = 'false';
});
pause(Duration(milliseconds: 1000)).then((e){
Alerts(context: context).alertNotPremium();
});
}
});}
}
});
}else{
Alerts(context: context).alertInternet();
failedLoginAnimation();
}
});
}
else{
setState(() {
});
}
}else{
setState(() {
});
}
});
});
});
}
}
@override
void initState() {
super.initState();
_loginButtonController = new AnimationController(
duration: new Duration(milliseconds: 3000), vsync: this);
_freeButtonController = new AnimationController(
duration: new Duration(milliseconds: 3000), vsync: this);
tokensManagement();
animationStatusLogin = 0;
animationStatusFree = 0;
print('init LOGIN');
}
@override
void dispose() {
super.dispose();
_loginButtonController.dispose();
_freeButtonController.dispose();
}
Future<Null> _playAnimationLogin() async {
try {
await _loginButtonController.forward();
} on TickerCanceled {}
}
Future<Null> _playAnimationFree() async {
try {
await _freeButtonController.forward();
await _freeButtonController.reverse();
} on TickerCanceled {}
}
Widget formLogin(){
return new Container(
margin: new EdgeInsets.symmetric(horizontal: 20.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Form(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new Container(
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(
width: 0.5,
color: DesignColors.animatedLoginColor,
),
),
),
child: new TextFormField(
controller: usernameController,
keyboardType: TextInputType.emailAddress,
style: const TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
icon: new Icon(
Icons.person_outline,
color: Colors.white,
),
border: InputBorder.none,
hintText: "Email/Username",
hintStyle: const TextStyle(color: Colors.white, fontSize: 15.0),
contentPadding: const EdgeInsets.only(
top: 30.0, right: 30.0, bottom: 30.0, left: 5.0),
),
),
),
new Container(
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(
width: 0.5,
color: DesignColors.animatedLoginColor,
),
),
),
child: new TextFormField(
controller: passwordController,
obscureText: true,
style: const TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
icon: new Icon(
Icons.lock_outline,
color: Colors.white,
),
border: InputBorder.none,
hintText: "Password",
hintStyle: const TextStyle(color: Colors.white, fontSize: 15.0),
contentPadding: const EdgeInsets.only(
top: 30.0, right: 30.0, bottom: 30.0, left: 5.0),
),
),
),
],
)),
],
));
}
void failedLoginAnimation(){
setState(() {
hasFailedLogin = true;
_playAnimationLogin();
animationStatusLogin = 0;
});
}
Widget logPage(){
return new WillPopScope(
onWillPop: () async => false,
child: new Scaffold(
backgroundColor: DesignColors.backgroundColor,
body: Center(
child: Theme(
data: ThemeData(splashColor: Colors.transparent, textSelectionHandleColor: Colors.blue, highlightColor: Colors.transparent),
child:new ListView(
shrinkWrap: true,
children: <Widget>[
new Stack(
alignment: AlignmentDirectional.bottomCenter,
children: <Widget>[
new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Logo(image: DecorationImage(image: ExactAssetImage('assets/images/AppLogo.png'), fit: BoxFit.cover),),
formLogin(),
new PremiumLink(action: (){_launchURL("someurl");}),
new ForgotPasswordLink(action: (){_launchURL("someurl");}),
],
),
animationStatusLogin == 0
? new Padding(
padding: const EdgeInsets.only(bottom: 160.0),
child: new InkWell(
onTap: () {
setState(() {
animationStatusLogin = 1;
hasFinishedLogin = false;
hasFinishLogAnim = false;
hasFailedLogin = false;
isFirstCallAction = true;
});
_playAnimationLogin();
},
child: new SignIn()),
)
: new StaggerAnimationSignIn( action: (){
CheckInternet().checkInternet().then((e){
if(e == true){
PostHTTP().sendLogin({'username': usernameController.text, 'password': passwordController.text}).then((e){
String responseStatus = e['status'];
if(responseStatus != "200"){
Alerts(context: context).alertFalseInfosLogin();
failedLoginAnimation();
}else{
String token = e['token'];
PostHTTP().sendRequestAPI(token, 'membership').then((value){
if(value['status'] == "200"){
String membership = value['membership'];
print(membership);
if(membership == "138" || membership == "136"){
APIToken.token = token;
JTools.saveLogin(token);
print('User is premium ');
FocusScope.of(context).requestFocus(new FocusNode());
hasFinishedLogin = true;
hasFinishLogAnim = false;
hasFailedLogin = false;
_playAnimationLogin();
}else{
print("User isn't premium ");
Alerts(context: context).alertNotPremium();
failedLoginAnimation();
}
}else{
if(value['status'] == "404"){Alerts(context: context).alertErrorConnectionServer(); failedLoginAnimation();}
if(value['status'] == "401" || value['status'] == "403"){Alerts(context: context).alertNotPremium(); failedLoginAnimation();}
}
});
}
});
}else{
Alerts(context: context).alertInternet();
failedLoginAnimation();
}
});
//Navigator.of(context).popAndPushNamed('/main');
},
buttonController:
_loginButtonController.view
),
animationStatusFree == 0
? new Padding(
padding: const EdgeInsets.only(bottom: 85.0),
child: new InkWell(
onTap: () {
setState(() {
animationStatusFree = 1;
});
_playAnimationFree();
},
child: new FreeAccess()),
)
: new StaggerAnimationFree(
buttonController:
_freeButtonController.view),
],
),
],
),
),
)
)
);
}
Widget waitingPage(){
return WillPopScope(child: Scaffold(backgroundColor: DesignColors.backgroundColor.withOpacity(1), body: Center(child: new CircularProgressIndicator(
value: null,
strokeWidth: 1.0,
valueColor: new AlwaysStoppedAnimation<Color>(
DesignColors.robot1Theme),
)),), onWillPop:() async => false);
}
@override
Widget build(BuildContext context) {
timeDilation = 0.4;
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
if(userLoginToken == null && tokenFileExist == 'false'){
return logPage();
}
if(userLoginToken == null && tokenFileExist == null && backToLogin == "true"){
//TODO: on loading
backToLogin ="false";
tokensManagement();
return logPage();
}else if(userLoginToken == null && tokenFileExist == null){
//TODO: on loading
return waitingPage();
}
if(userLoginToken.isNotEmpty && tokenFileExist == null){
//TODO: on loading
return waitingPage();
}
if(userLoginToken.isEmpty && tokenFileExist == 'true'){
return logPage();
}
return logPage();
}
}
我的动画课
import 'package:flutter/material.dart';
import 'package:test/premium/tools/design/color/designColors.dart';
import 'login.dart';
class StaggerAnimationSignIn extends StatelessWidget {
final action;
StaggerAnimationSignIn({Key key, this.buttonController, @required this.action})
: buttonSqueezeanimation = new Tween(
begin: 320.0,
end: 70.0,
)
.animate(
new CurvedAnimation(
parent: buttonController,
curve: new Interval(
0.0,
0.175,
),
),
),
buttomZoomOut = new Tween(
begin: 70.0,
end: 5000.0,
)
.animate(
new CurvedAnimation(
parent: buttonController,
curve: new Interval(
0.55,
0.999,
curve: Curves.bounceOut,
),
),
),
containerCircleAnimation = new EdgeInsetsTween(
begin: const EdgeInsets.only(bottom: 160.0),
end: const EdgeInsets.only(bottom: 0.0),
)
.animate(
new CurvedAnimation(
parent: buttonController,
curve: new Interval(
0.500,
0.800,
curve: Curves.ease,
),
),
),
super(key: key);
final AnimationController buttonController;
final Animation<EdgeInsets> containerCircleAnimation;
final Animation buttonSqueezeanimation;
final Animation buttomZoomOut;
Widget _buildAnimation(BuildContext context, Widget child) {
return new Padding(
padding: buttomZoomOut.value == 80
? const EdgeInsets.only(bottom: 160.0)
: containerCircleAnimation.value,
child: new InkWell(
onTap: () {
},
child: new Hero(
tag: "fadeLogin",
child: buttomZoomOut.value <= 300
? new Container(
width: buttomZoomOut.value == 70
? buttonSqueezeanimation.value
: buttomZoomOut.value,
height:
buttomZoomOut.value == 70 ? 60.0 : buttomZoomOut.value,
alignment: FractionalOffset.center,
decoration: new BoxDecoration(
color: DesignColors.animatedLoginColor,
borderRadius: buttomZoomOut.value < 400
? new BorderRadius.all(const Radius.circular(30.0))
: new BorderRadius.all(const Radius.circular(0.0)),
),
child: buttonSqueezeanimation.value > 75.0
? new Text(
"Sign In",
style: new TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w300,
letterSpacing: 0.3,
),
)
: buttomZoomOut.value < 300.0
? new CircularProgressIndicator(
value: null,
strokeWidth: 1.0,
valueColor: new AlwaysStoppedAnimation<Color>(
Colors.white),
)
: null)
: new Container(
width: buttomZoomOut.value,
height: buttomZoomOut.value,
decoration: new BoxDecoration(
shape: buttomZoomOut.value < 500
? BoxShape.circle
: BoxShape.rectangle,
color: DesignColors.animatedLoginColor,
),
),
)),
);
}
@override
Widget build(BuildContext context) {
if(LoginScreenState.isFirstCallAction){
buttonController.addListener(() {
if (LoginScreenState.isFirstCallAction) {
print("1");
LoginScreenState.isFirstCallAction = false;
action();
}
if (LoginScreenState.hasFailedLogin) {
print("3");
LoginScreenState.hasFailedLogin = false;
buttonController.reverse();
LoginScreenState.animationStatusLogin = 0;
}
if (buttonController.value > 0.2 &&
LoginScreenState.hasFinishedLogin == false &&
LoginScreenState.hasFailedLogin == false &&
LoginScreenState.hasFinishLogAnim == false) {
print("2");
buttonController.stop();
}
if (LoginScreenState.hasFinishedLogin && !LoginScreenState.hasFailedLogin && !LoginScreenState.hasFinishLogAnim && buttonController.value == 1.0) {
print("4");
LoginScreenState.hasFinishedLogin = false;
LoginScreenState.hasFailedLogin = false;
LoginScreenState.hasFinishLogAnim = true;
LoginScreenState.animationStatusLogin = 0;
Navigator.popAndPushNamed(context, "/main");
return;
}
});
}
return new AnimatedBuilder(
builder: _buildAnimation,
animation: buttonController,
);
}
}
更新:
嗨解决了我认为的问题,我仍然需要做一些测试,但似乎调用 Navigator.popAndPushNamed(context, "/main");
AnimationClass 不是一个好主意我只是更改了我的登录类,当动画完成时我调用Navigator.popAndPushNamed(context, "/main");
我的登录类。我会在接下来的日子里继续更新这篇文章。
我的构建登录类的新开始
@override
Widget build(BuildContext context) {
_loginButtonController.addListener((){
if(_loginButtonController.isCompleted){
FocusScope.of(context).requestFocus(new FocusNode());
Navigator.popAndPushNamed(context, "/main");
}
});
并删除Navigator.popAndPushNamed(context, "/main");
并return;
在我的动画结束通话