所以我有一个登录页面,当用户按下登录名时,中间件进行API调用,并且在收到响应后,reducer更改应用程序状态(将isLogged
设置为true
)。如何使用导航器转到下一页。当我尝试执行此操作时,将引发在构建过程中调用setResult()的错误。
当状态更改时,将重新构建小部件树,以便导航器无法获取适当的上下文。如何等待树重建,然后调用Navigator.of(context).push(Route)。
import 'package:flutter/material.dart';
import 'package:vattendance_flutter/common/loading_status.dart';
import 'package:vattendance_flutter/common/ui/buttons/primary_button.dart';
import 'package:vattendance_flutter/common/ui/loading/primary_circular_progress.dart';
import 'package:vattendance_flutter/common/ui/text_fields.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:vattendance_flutter/redux/app/app_state.dart';
import 'package:redux/redux.dart';
import 'package:vattendance_flutter/redux/login/login_actions.dart';
import 'package:vattendance_flutter/ui/home/home_page.dart';
class LoginPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
_ViewModel _viewModel;
TextEditingController _usernameController;
TextEditingController _passwordController;
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
@override
void initState() {
super.initState();
_usernameController = TextEditingController();
_passwordController = TextEditingController();
}
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, _ViewModel>(
converter: (store) => _ViewModel.fromStore(store),
builder: (context, viewModel) {
_viewModel = viewModel;
_stateDependentSetUp();
return StoreBuilder<AppState>(
rebuildOnChange: true,
onDidChange: (store) {},
builder: (context, viewModel) {
return Scaffold(
key: scaffoldKey,
resizeToAvoidBottomPadding: false,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'VAttendance',
style: TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.w300,
),
),
Padding(
padding: EdgeInsets.only(
top: 40.0,
left: 16.0,
right: 16.0,
),
child: PrimaryTextField(
controller: _usernameController,
hintText: "Username",
),
),
Padding(
padding: EdgeInsets.only(
top: 40.0,
left: 16.0,
right: 16.0,
),
child: PrimaryTextField(
controller: _passwordController,
hintText: "Password",
obsecureText: true,
),
),
Container(
margin: EdgeInsets.only(top: 40.0),
child: _getLoginButtonOrProgress(),
),
],
),
);
},
);
},
);
}
_stateDependentSetUp() {
var loadingData = _viewModel.loadingData;
if (_viewModel.loadingData.status == LoadingStatus.ERROR) {
scaffoldKey.currentState.showSnackBar(
SnackBar(content: Text(loadingData.message ?? "Error")));
} else if (_viewModel.loadingData.status == LoadingStatus.SUCCESS) {
scaffoldKey.currentState
.showSnackBar(SnackBar(content: Text("Welcome to VAttendance.")));
_startHome();
}
}
_getLoginButtonOrProgress() {
return _viewModel.loadingData.status == LoadingStatus.LOADING
? PrimaryCircularProgress(
progressColor: Colors.blue,
)
: PrimaryButton(
text: "Login",
onPressed: () {
FocusScope.of(context).requestFocus(FocusNode());
_viewModel.onLoginPressed(
username: _usernameController.text,
password: _passwordController.text,
);
});
}
_startHome() {
Navigator.of(scaffoldKey.currentContext).pushReplacement(
MaterialPageRoute(
builder: (context) {
return HomePage();
},
),
);
}
}
class _ViewModel {
final bool isLoggedIn;
final LoadingData loadingData;
final Function({String username, String password, Function onError})
onLoginPressed;
_ViewModel({
this.loadingData,
this.onLoginPressed,
this.isLoggedIn,
});
factory _ViewModel.fromStore(Store<AppState> store) {
return _ViewModel(
isLoggedIn: store.state.loginState.isLoggedIn,
loadingData: store.state.loginState.loadingData,
onLoginPressed: ({String username, String password, Function onError}) {
store.dispatch(LoginAction(username: username, password: password));
},
);
}
}
答案 0 :(得分:3)
您可以将Completer添加到LoginAction
中,这样它将具有3个字段:username
,password
和completer
。
当您将此完成程序传递给LoginAction
时:
Completer _initCompleter() {
return new Completer()
..future.then((_) => Navigator.of(context).pushNamed("\someRoute"));
}
完成者将等到Future
到达那里。
在中间件中,您可以调用complete()
方法来告诉小部件转到下一个屏幕。
这是中间件逻辑内部的内容:
authService.loginInWithEmail(action.email, action.password).then(
(user) {
store.dispatch(new UserProvidedAction(user));
action.completer?.complete();
},
);
您还可以使用它来处理错误,只需将其添加到完成程序的将来的onError
参数中,然后在中间件中可以调用completeError()
。