【问题标题】:Flutter_bloc: Is there a cleaner way to access the state of a flutter Cubit than a redundant if/else statement?Flutter_bloc:有没有比多余的 if/else 语句更简洁的方式来访问 Flutter Cubit 的状态?
【发布时间】:2021-05-14 18:19:54
【问题描述】:

我正在使用 flutter_bloc 并创建了一个 sessionState,当用户成功通过身份验证并且状态保存用户对象 (did) 时触发该会话状态。
我使用 Cubit 来更改状态,然后在 ui 中显示不同的屏幕。但是,每当我想访问保存用户信息的已验证 sessionState 的 did 对象时,我必须在每个文件中创建一个 if else 语句来检查 sessionState 是否为 Verified,如果这是真的,我可以访问与state.did 反对。
我很想知道是否可以将此 did 对象提供给所有底层小部件,而无需手动传递每个小部件。
我希望能够从上下文中访问 did 对象,以便我可以在提供它的任何地方访问它。
我的会话状态:

abstract class SessionState {}

class UnkownSessionState extends SessionState {}

class Unverified extends SessionState {}

class Verified extends SessionState {
  Verified({required this.did});

  final Did did;
}

SessionCubit 启动状态用于定义应用的全局状态并因此显示不同的屏幕。

class SessionCubit extends Cubit<SessionState> {
  SessionCubit(this.commonBackendRepo) : super(UnkownSessionState()) {
    attemptGettingDid();
  }

  final CommonBackendRepo commonBackendRepo;
  final SecureStorage secureStorage = SecureStorage();

  Future<void> attemptGettingDid() async {
    try {
        //more logic
        emit(Unverified());
    } catch (e) {
      emit(Unverified());
    }
  }

  void showUnverified() => emit(Unverified());
  void showSession(Did did) {
    emit(Verified(did: did));
  }
}

这是我目前在每个文件中访问 did 对象的方式:

BlocBuilder<SessionCubit, SessionState>(builder: (context, state) {
  if (state is Verified) {
    return CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          elevation: 0.0,
          backgroundColor: Theme.of(context).scaffoldBackgroundColor,
          title: Text(
            state.did.username,
            style: Theme.of(context).textTheme.headline5,
          ),
        ),
        SliverToBoxAdapter(
            child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: CredentialDetailsView(),
        ))
      ],
    );
  } else {
    return const Text("Unverified");
  }
});

编辑:
这就是我想象我的场景的最佳解决方案的方式:

  1. 创建 if/else 以检查状态是否在一个父窗口小部件中得到验证。
  2. 如果状态已验证,则使用“did”对象创建一个作为子级的 Provider,这样该 Provider 的子级就不必访问 SessionState,而只需访问提供的 did 对象。

回应罗伯特·桑德伯格的回答
我还有一个AppNavigator,可以启动authNavigatorsessionNavigator。所以sessionNavigator 是所有小部件的父小部件,仅当状态为Verified 时才会显示。所以我认为这将是一个用Provider.value 包装 sessionNavigator 的好地方,正如你所解释的那样。
我用 Provider.value 小部件包装了我的 SessionNavigator 并提供了状态对象:

class AppNavigator extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<SessionCubit, SessionState>(
      builder: (context, state) {
        return Navigator(
          pages: [
            if (state is UnkownSessionState)
              MaterialPage(child: StartupScreen()),
            //show auth flow
            if (state is Unverified)
              MaterialPage(
                  child: BlocProvider(
                create: (context) => AuthCubit(context.read<SessionCubit()),
                child: AuthNavigator(),
              )),
            //show session flow
            if (state is Verified)
              MaterialPage(
                  child: Provider.value(
                // here I can access the state.did object
                value: state,
                child: SessionNavigator(),
              ))
          ],
          onPopPage: (route, result) => route.didPop(result),
        );
      },
    );
  }
}

为了测试,我尝试在我的主屏幕中观看提供的值,它是 SessionNavigator 的子屏幕:

...
class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    //
    final sessionState = context.watch<SessionState>();
    return CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          floating: true,
          expandedHeight: 60.0,
          backgroundColor: Colors.white,
          flexibleSpace: FlexibleSpaceBar(
              title: Text(
                // I can't access state.did because value is of type SessionState
                sessionState.did,
                style: Theme.of(context).textTheme.headline6,
              ),
              titlePadding:
                  const EdgeInsetsDirectional.only(start: 20, bottom: 16)),
        )
      ],
    );
  }
}

但是因为我的值的类型是SessionState,所以我无法访问底层 Verified 状态的 did 对象。我想提供 state.did 对象,但我不知道如何查看该类型。
我还收到了我的主屏幕在构建上下文中找不到提供程序的错误。会不会因为我的主屏幕有 Navigator 作为父级而发生?
这是我的应用架构的可视化:

【问题讨论】:

  • 如果您希望我将我的答案翻译到您的特定课程,请告诉我。我为即将到来的观众写了一些笼统的东西。

标签: flutter dart


【解决方案1】:

我有时会使用这种模式,听起来我的解决方案非常适合您想象的最佳解决方案:)

在父母中:

BlocBuilder<MyBloc, MyBlocState>(
          builder: (context, state) {
            return state.when(
              stateA: () => SomeWidgetA(),
              stateB: () => SomeWidgetB(),
              stateC: (myValue) {
                return Provider.value(
                  value: myValue,
                  child: SomeWidgetC(),
                );
              }),
           }
          )

...然后在SomeWidgetC() 和所有它的孩子中,我执行以下操作以获取myValue(它的类型为MyValueType

final myValue = context.watch<MyValueType>();

如果您愿意,您也可以使用常规的InheritedWidget 而不是使用Provider package,但是您可以使用 Provider 获得很多漂亮的功能。

编辑 回答您的其他问题。

在您的情况下,如果您像在 AppNavigator 中一样提供整个状态,那么您应该在 SessionNavigator 中查找 Verified 状态:

final sessionState = context.watch<Verified>();

或者,如果您只需要 Did 对象,那么只需提供它,在 AppNavigator 中:

if (state is Verified)
  MaterialPage(
    child: Provider.value(
             value: state.did,
             child: SessionNavigator(),
           ))

然后在 SessionNavigator 中:

final did = context.watch<Did>();

编辑 3: 嵌套导航器可能会出现您的问题。看看这个SO answer 以获得一些指导。

如果您继续挣扎,我建议您针对特定问题打开新的 SO 问题,因为这开始变得有点宽泛 :) 我认为原始问题已得到解答,即使您尚未进入决赛解决方案。

【讨论】:

  • 这看起来很有希望。我刚刚尝试过,但遇到了两个障碍,在我的问题中有深入描述(请参阅我的问题中最低的 Edit 参考)。如果您还有任何问题,请随时再次问我。
  • 观看已验证状态对我有用。感谢您的帮助!
【解决方案2】:

您可以使用(state as Verified).did.username 之类的东西,但我仍然建议您使用 if/else 模式,因为您可以确保您使用的是正确的状态。

在我的个人项目中,我使用 Freezed 包 (https://pub.dev/packages/freezed) 来处理这种情况,因为有一些有用的方法可以解决这个问题。例如,通过使用 freezed,您的问题可以解决为:

return state.maybeWhen(
  verified: (did) => CustomScrollView(...),
  orElse: () => const Text("Unverified"),
)

【讨论】:

    猜你喜欢
    • 2021-04-21
    • 1970-01-01
    • 2022-08-19
    • 2011-02-05
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多