Flutter BLoC:在build()方法中StreamBuilder中的Navigator.pop

时间:2019-04-13 12:49:39

标签: flutter bloc

我遵循BLoC模式并订阅流,并对构建方法中的状态更改做出反应。加载数据后,我想关闭屏幕。

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Bloc'),
      ),
      body: SafeArea(
        child: StreamBuilder<UserState>(
          stream: _userBloc.user,
          initialData: UserInitState(),
          builder: (context, snapshot) {
            if (snapshot.data is UserInitState) {
              return _buildInit();
            }
            if (snapshot.data is UserDataState) {
              Navigator.pop(context, true);
              return Container();
            }
            if (snapshot.data is UserLoadingState) {
              return _buildLoading();
            }
          },
        ),
      ),
    );
  } 

Navigator.pop(context, true);方法执行build()时,我得到:

I/flutter ( 4360): ══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter ( 4360): The following assertion was thrown while notifying status listeners for AnimationController:
I/flutter ( 4360): setState() or markNeedsBuild() called during build.
I/flutter ( 4360): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter ( 4360): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter ( 4360): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter ( 4360): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter ( 4360): Otherwise, the framework might not visit this widget during this build phase.

以BLoC模式处理此类案件的正确方法是什么?

我提出的解决方案之一是开始收听initState()上的流。在这种情况下,我需要broadcast()流,因为我有2个订阅者。

有没有更好的解决方案?

3 个答案:

答案 0 :(得分:0)

我可以想象三种可能的解决方案:

1)在我看来,最好是重组小部件。据我所知,您需要一个“加载屏幕” ..我没有理由认为这必须是它自己的导航项,而不仅仅是另一个小部件。

即。您可以推let str = `From: DEMO - Sliced Invoices Order Number Suite 5A-1204 Order Number 123 Somewhere Street Order Number Your City AZ 12345 Order Number admin@slicedinvoices.com Order Number` let op = str.match(/^.+?(?=\s{2,}|\n)/gm) console.log(op)吗?升级..因此您的构建器方法如下:

StreamBuilder

2)我想我个人将创建一个if (!snapshot.hasData) { return LoadingScreen(snapshot); } // Whatever you do with your data. ,在StatefulWidget中手动收听流,并手动调用initState()。不需要setState()

3)作为一种疯狂的解决方法,您可能会在构建器中使用StreamBuilder。 (有可能您必须使用Future(() { Navigator.of(context).pop(); });方法的context,而不是生成器。但我还是不建议使用该解决方案)

答案 1 :(得分:0)

我想我已经为您找到了解决方案。 (请检查)

使您的代码看起来像:

Widget build(BuildContext context) {
    // other stuff

    if (snapshot.data is UserDataState) {
      myCallback(() {
        Navigator.pop(context, true);
      });
    }

    // other stuff
}
// after build method (but still in the same class,...) write below method

void myCallback(Function callback) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      callback();
    });
}

希望有帮助。只需尝试一下,请在这里举报也可以帮助其他人!

Source (flutter_bloc Login medium article)

Description

答案 2 :(得分:0)

import SwiftUI

@main
struct SpinnerApp: App {    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State var isAnimating = false
    @State var text = ""
    
    var animation: Animation {
        Animation.linear(duration: 3.0)
            .repeatForever(autoreverses: false)
    }

    var body: some View {
        HStack {
            Spacer()
            VStack {
                Circle()
                    .foregroundColor(Color.orange)
                    .frame(height: 100)
                    .rotationEffect(Angle(degrees: self.isAnimating ? 360 : 0.0))
                    .animation(self.isAnimating ? animation : .default)
                    .onAppear { self.isAnimating = true }
                    .onDisappear { self.isAnimating = false }
                if self.text != "" {
                    Text(self.text)
                }
            }
            Spacer()
        }
        .background(Color.gray)
        .onAppear(perform: {
            DispatchQueue.main.asyncAfter(deadline: .now()+4, execute: {
                self.text = "Test"
            })
        })
    }
}