4

这周我开始在颤振中发展,我无法解决这个问题。

我正在构建一个登录页面,该页面调用 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)的构建函数中调用的

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 调用并返回 Object LoginResponse 的函数

希望我已经提供了足够的信息!

4

2 回答 2

5

对我来说,这是我在构建完成之前使用导航器的时候!只需将您的导航代码放在这里:

WidgetsBinding.instance.addPostFrameCallback((_) {
  // Do everything you want here...
});
于 2020-08-14T08:21:53.633 回答
1

LoginResponse Consumer我修改了尝试登录()来等待结果并导航到新路由,而不是尝试推送新路由!

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)));
      }
    }
  }
于 2019-09-23T14:51:25.197 回答