如何有条件地在StreamBuilder中有条件地重定向构建器函数而不会出现错误?

时间:2019-05-02 16:20:27

标签: dart flutter

我正在尝试获取一个身份验证表单,以使用三元语句有条件地在Flutter StreamBuilder小部件中重定向。

当重定向条件返回true时,我将显示红色屏幕和以下日志:

I/flutter ( 3787): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 3787): The following assertion was thrown building StreamBuilder<Map<dynamic, dynamic>>(dirty, state:
I/flutter ( 3787): _StreamBuilderBaseState<Map<dynamic, dynamic>, AsyncSnapshot<Map<dynamic, dynamic>>>#66400):
I/flutter ( 3787): setState() or markNeedsBuild() called during build.
I/flutter ( 3787): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter ( 3787): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter ( 3787): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter ( 3787): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter ( 3787): Otherwise, the framework might not visit this widget during this build phase.
I/flutter ( 3787): The widget on which setState() or markNeedsBuild() was called was:
I/flutter ( 3787):   Overlay-[LabeledGlobalKey<OverlayState>#4f97a](state: OverlayState#5df28(tickers: tracking 2
I/flutter ( 3787):   tickers, entries: [OverlayEntry#09e48(opaque: false; maintainState: false),
I/flutter ( 3787):   OverlayEntry#61a61(opaque: false; maintainState: true), OverlayEntry#79842(opaque: false;
I/flutter ( 3787):   maintainState: false), OverlayEntry#11ff2(opaque: false; maintainState: true)]))
I/flutter ( 3787): The widget which was currently being built when the offending call was made was:
I/flutter ( 3787):   StreamBuilder<Map<dynamic, dynamic>>(dirty, state: _StreamBuilderBaseState<Map<dynamic, dynamic>,
I/flutter ( 3787):   AsyncSnapshot<Map<dynamic, dynamic>>>#66400)

违规小部件:

import 'package:flutter/material.dart';
import '../blocs/auth_bloc.dart';

class LoginPage extends StatelessWidget {
  final authBloc = AuthBloc();
  Map<String, bool> initialData = {'loginSuccess': false, 'isLoading': false};

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
        stream: authBloc.redirect,
        initialData: initialData,
        builder: (context, snapshot) {
          return Scaffold(body: _render(context, snapshot));
        });
  }

  Widget _render(BuildContext context, AsyncSnapshot snapshot) {
    return !snapshot.data['loginSuccess'] && snapshot.data['isLoading']
        ? _circularSpinner()
        : snapshot.data['loginSuccess'] && !snapshot.data['isLoading']
            ? _redirect(context)
            : _buildPage();
  }

  _redirect(BuildContext context) {
    return Navigator.pushReplacementNamed(context, '/dashboard');
  }

  Widget _buildPage() {
    return Container(
      margin: EdgeInsets.all(20.0),
      child: Center(
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              _emailField(authBloc),
              _padding(),
              _passwordField(authBloc),
              _padding(),
              _submitButton(authBloc)
            ],
          ),
        ),
      ),
    );
  }

  Widget _circularSpinner() {
    return Center(
      child: CircularProgressIndicator(),
    );
  }

  Widget _emailField(AuthBloc authBloc) {
    return StreamBuilder(
      stream: authBloc.email,
      builder: (BuildContext context, snapshot) {
        return TextField(
          onChanged: authBloc.emailChanged,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
            hintText: 'you@example.com',
            labelText: 'Email Address',
            errorText: snapshot.error,
            border: OutlineInputBorder(),
          ),
        );
      },
    );
  }

  Widget _passwordField(AuthBloc authBloc) {
    return StreamBuilder(
      stream: authBloc.password,
      builder: (BuildContext context, snapshot) {
        return TextField(
          onChanged: authBloc.passwordChanged,
          obscureText: true,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
            hintText: '8 characters or more with at least 1 number',
            labelText: 'Password',
            errorText: snapshot.error,
            border: OutlineInputBorder(),
          ),
        );
      },
    );
  }

  Widget _padding() {
    return Padding(
      padding: EdgeInsets.only(top: 20.0),
    );
  }

  Widget _submitButton(AuthBloc authBloc) {
    return StreamBuilder(
        stream: authBloc.submitValid,
        builder: (context, snapshot) {
          return RaisedButton(
            child: Text('Login'),
            color: Colors.blue,
            onPressed: snapshot.hasError ? null : authBloc.submitForm,
          );
        });
  }
}

我已经在Google上进行了搜索,但找不到与Navigator相关的任何内容。

我希望该小部件重定向到“仪表板”小部件。相反,我得到红色错误屏幕。

2 个答案:

答案 0 :(得分:1)

我今天遇到了同样的问题。我尝试了@ user8467470的解决方案,但StreamBuilder已注册到我的Stream中。所以我最终得到了这个解决方案:

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: StreamBuilder<Response<ExchangeRate>>(
            stream: _bloc.exchangeDataStream,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                switch (snapshot.data.status) {
                  case Status.LOADING:
                    return LoadingWidget(loadingMessage: snapshot.data.message);
                  case Status.COMPLETED:
                    WidgetsBinding.instance.addPostFrameCallback((_) {
                      Navigator.pushReplacementNamed(context, '/home');
                    });
                    break;
                  case Status.ERROR:
                    return Container(
                      child: Text(snapshot.data.message),
                    );
                }
              }
              return Container();
            }));
  }

这样,我的集团中只有一个流。

答案 1 :(得分:0)

我找到了一种解决方法,但这并不漂亮。我以为在其他任何人遇到相同问题的情况下也会在这里分享它,但是我当然希望其他更有经验的Flutter开发人员可以提供更好的解决方案和理由。

似乎出于任何原因,StreamBuilder都不喜欢您,由于其构建流程而无法使用构建器功能。结果,我将导航移到了StreamBuilder之外,但移到了build方法中,并添加了一个像这样的侦听器:

@override
  Widget build(BuildContext context) {
    // Manually listening for redirect conidtions here
    // once a response is received from the server
    authBloc.loginSuccess.listen((data) {
      if (data) {
        Navigator.pushReplacementNamed(context, '/dashboard');
      }
    });

    return StreamBuilder(
        stream: authBloc.isLoading,
        initialData: false,
        builder: (context, snapshot) {
          print(snapshot.data);
          return Scaffold(
            body: snapshot.data ? _circularSpinner() : _buildPage(),
          );
        });
  }

它现在似乎可以工作,但是我会继续关注最佳实践。