这周我开始颤抖,无法解决这个问题。
我正在构建一个登录页面,该页面调用API进行登录,并在重定向到主页之后。
这是第一个代码块中Navigator.pushReplacement
生成的异常。
在那一刻apiCall.isFetching
是错误的,原因是提取结束,并且apiCall.response
包含所需的数据。
异常详细信息:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Consumer<ApiCallChangeNotifier>(dirty, dependencies: [InheritedProvider<ApiCallChangeNotifier>]):
setState() or markNeedsBuild() called during build.
This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: Overlay-[LabeledGlobalKey<OverlayState>#4dc85]
state: OverlayState#bd97e(tickers: tracking 1 ticker, entries: [OverlayEntry#2941b(opaque: false; maintainState: false), OverlayEntry#37814(opaque: false; maintainState: true), OverlayEntry#f92c0(opaque: false; maintainState: false), OverlayEntry#da26d(opaque: false; maintainState: true)])
The widget which was currently being built when the offending call was made was: Consumer<ApiCallChangeNotifier>
dirty
dependencies: [InheritedProvider<ApiCallChangeNotifier>]
User-created ancestor of the error-causing widget was:
Expanded file:///C:/flutter_test/lib/screens/login/LoginScreen.dart:153:37
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3687:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3702:6)
#2 State.setState (package:flutter/src/widgets/framework.dart:1161:14)
#3 OverlayState.insertAll (package:flutter/src/widgets/overlay.dart:346:5)
#4 OverlayRoute.install (package:flutter/src/widgets/routes.dart:43:24)
...
这是我创建登录按钮的功能,它是从 LoginScreen (StatelessWidget)
的 build 功能调用的Widget loginButton(BuildContext context) {
return Consumer<ApiCallChangeNotifier>(
builder: (context, apiCall, child) => apiCall.isFetching
? CircularProgressIndicator()
: apiCall.response != null
? Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
(apiCall.response as LoginResponse).email)))
: RaisedButton(
...
onPressed: () {
attemptLogin(context);
},
...
));
}
尝试登录功能:
void attemptLogin(BuildContext context) {
Provider.of<ApiCallChangeNotifier>(context, listen: false).callApi(
MyApiServices().attemptLogin,
{
'email': emailController.value.text,
'password': passwordController.value.text,
},
urlController.value.text
);
}
ApiCallChangeNotifier
class ApiCallChangeNotifier extends ChangeNotifier {
bool isFetching = false;
Object response;
Future<LoginResponse> callApi(apiFunction, bodyParams, customUrl) async {
isFetching = true;
notifyListeners();
response = await apiFunction(bodyParams, customUrl);
isFetching = false;
notifyListeners();
return response;
}
}
MyApiServices.attemptLogin
是处理API调用并返回对象LoginResponse的函数
希望我提供了足够的信息!
答案 0 :(得分:1)
对我来说,那是我在构建完成之前使用导航器的时候! 只需将导航代码放在这里:
WidgetsBinding.instance.addPostFrameCallback((_) {
// Do everything you want here...
});
答案 1 :(得分:0)
我没有尝试从LoginResponse Consumer
推送新路线,而是修改了tryLogin()以等待结果并导航到新路线!
void attemptLogin(BuildContext context) async {
LoginResponse _apiResponse =
await Provider.of<ApiCallChangeNotifier>(context, listen: false)
.callApi(
MyApiServices().attemptLogin,
{
'email': emailController.value.text,
'password': passwordController.value.text,
},
urlController.value.text);
if (_apiResponse != null) {
if (_apiResponse.email != null) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(_apiResponse.email)));
} else if (_apiResponse.errorMessage != null) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(_apiResponse.errorMessage)));
} else {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(KanbanBOXApi().unknownErrorMessage)));
}
}
}