【问题标题】:Build the architecture for a Flutter App with flutter_bloc使用 flutter_bloc 构建 Flutter App 的架构
【发布时间】:2020-04-24 13:40:34
【问题描述】:

我必须用 Flutter 开发一个简单的应用程序,我是第一次尝试 bloc 模式。我正在使用flutter_bloc 包,但我有两个主要问题并且卡住了:

  1. 应用程序中的所有屏幕都有块,这些块依赖于 repos 向服务器发出请求,并且对于每个请求,我都需要一个令牌。我在authentication_bloc(登录)处获得了令牌,但我无法将该令牌共享给所有集团,除非我让它们依赖于 auth_bloc(拥有存储令牌的用户模型的集团)。对于一个简单的应用程序,也许它可以工作,但是如果其他 bloc 除了 auth_bloc 之外还有更多 bloc 依赖项,那么将所有 bloc 作为依赖项注入可能会变得一团糟。什么替代方案可能是一个好的解决方案?

  2. 对于导航,我有一个bloc 用于底部导航栏。但我希望选项卡上的所有屏幕都可以调用底部栏块的change_tab() 函数。同样是同样的问题,如果其他集团依赖于底栏集团,我只能在底栏集团添加事件。我不想将身份验证块和底栏块添加为对管理所有屏幕的所有块的依赖项。

感谢建议、替代方案或文档,让我可以学习为应用构建一个比示例更复杂的良好架构 here

【问题讨论】:

  • 我认为 bloc 对于复杂的应用来说不是很好的模式,bloc 包含在同一个类中的核心逻辑和 UI 管理(状态),在复杂的应用中我们应该将两者分开。在我的应用程序中,我尝试拆分为 3 种布局:用例(可重用的核心逻辑,如 auth)、块(专用于屏幕逻辑、调用用例和接口 Presenter.display... 通知)和视图模型(实现演示者并创建状态流对于小部件)。这不是真正的块模式,但逻辑、状态表示和可重用的核心逻辑被正确分离。无论如何,每个应用程序都需要找到她最好的架构,祝你好运:p
  • 嗯,我曾经使用 MVC 变体逻辑构建我的应用程序,但我读到 BLoC 是构建更复杂应用程序的更好选择,所以我决定试一试。我想我不能强制我所有的应用程序都构建在某种架构中 xD
  • Flutter 仍然很新,没有独特的好架构,但你可以将你的 MVC 方式与 bloc 模式合并,也许结果会很好。我认为 bloc 不够分离,难以测试,可重用性......但许多其他人会喜欢这个。将未来的回调转换为带状态的流是好的,但尝试多层分离,我认为它会更好。

标签: flutter dart bloc


【解决方案1】:

您可以将令牌存储在Repository,例如userRepository.persistToken(event.token)await userRepository.deleteToken();
存储库的行为类似于存储库模式。您无需知道数据来自云端或本地数据库
在下面的示例中,userRepository 可以是应用级别,因此所有 bloc 都可以使用它
完整示例代码https://github.com/felangel/bloc/blob/master/examples/flutter_login/lib/authentication/authentication_bloc.dart
代码 sn -p

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();
  final userRepository = UserRepository();
  runApp(
    BlocProvider<AuthenticationBloc>(
      create: (context) {
        return AuthenticationBloc(userRepository: userRepository)
          ..add(AppStarted());
      },
      child: App(userRepository: userRepository),
    ),
  );
}

...

import 'package:meta/meta.dart';
import 'package:bloc/bloc.dart';
import 'package:user_repository/user_repository.dart';

import 'package:flutter_login/authentication/authentication.dart';

class AuthenticationBloc
    extends Bloc<AuthenticationEvent, AuthenticationState> {
  final UserRepository userRepository;

  AuthenticationBloc({@required this.userRepository})
      : assert(userRepository != null);

  @override
  AuthenticationState get initialState => AuthenticationUninitialized();

  @override
  Stream<AuthenticationState> mapEventToState(
    AuthenticationEvent event,
  ) async* {
    if (event is AppStarted) {
      final bool hasToken = await userRepository.hasToken();

      if (hasToken) {
        yield AuthenticationAuthenticated();
      } else {
        yield AuthenticationUnauthenticated();
      }
    }

    if (event is LoggedIn) {
      yield AuthenticationLoading();
      await userRepository.persistToken(event.token);
      yield AuthenticationAuthenticated();
    }

    if (event is LoggedOut) {
      yield AuthenticationLoading();
      await userRepository.deleteToken();
      yield AuthenticationUnauthenticated();
    }
  }
}

您可以使用MultiBlocProvider 在应用程序级别包装您的块
下面的例子是地图和位置
https://gitlab.kaleidos.net/piweek/betover/betover-mobile/blob/7b9368288743be602f37014f7a49ed4ef943afc1/lib/main.dart

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();

  runApp(
    MultiBlocProvider(
      providers: [
        BlocProvider<MapBloc>(
          create: (context) => MapBloc(),
        ),
        BlocProvider<PoisBloc>(
          create: (context) => PoisBloc(
            mapBloc: BlocProvider.of<MapBloc>(context),
          ),
        ),
      ],
      child: App(),
    )
  );
}

class App extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Bet Over',
      theme: appTheme,
      initialRoute: '/',
      routes: {
        '/': (context) =>
          MultiBlocProvider(
            providers: [
              BlocProvider<MapBloc>(
                create: (context) => BlocProvider.of<MapBloc>(context),
              ),
              BlocProvider<PoisBloc>(
                create: (context) => BlocProvider.of<PoisBloc>(context),
              ),
            ],
            child: MapPage(),
          ),
        '/create-poi': (context) =>
          MultiBlocProvider(
            providers: [
              BlocProvider<MapBloc>(
                create: (context) => BlocProvider.of<MapBloc>(context),
              ),
              BlocProvider<PoisBloc>(
                create: (context) => BlocProvider.of<PoisBloc>(context),
              ),
            ],
            child: CreatePoimPage(),
          ),
      }
    );
  }
}

并使用它https://gitlab.kaleidos.net/piweek/betover/betover-mobile/blob/7b9368288743be602f37014f7a49ed4ef943afc1/lib/ui/widgets/map.dart

class _MapState extends State<Map> {
  MapBloc _mapBloc;
  PoisBloc _poisBloc;


@override
  void initState() {
    super.initState();

    _mapBloc = BlocProvider.of<MapBloc>(context);
    _poisBloc = BlocProvider.of<PoisBloc>(context);

...
    List<Marker> _generateMarkers () {
    if (!(_mapBloc.state is MapViewboxChanged && _poisBloc.state is PoisLoaded))
      return [];

【讨论】:

  • 是的,我已经检查了felangel 文档,但这不能解决问题:如果我有其他块使用令牌对 REST API 进行查询(通过存储库),并且令牌无效,应通知 auth_bloc 并产生“need_auth_state”以重新加载登录页面。但是要通知 auth_bloc,所有 bloc 都必须依赖 auth_bloc。我在 flutter_bloc 的 github 上问了同样的问题,他们回答了我几个替代方案,但如果你能提出另一个建议,我将不胜感激。
  • 如何使用 App Level State。示例代码gist.github.com/mcany/2dffd93e20d860d62f83dcd363cc7a31
  • 能否分享您提出问题的 github 链接?
猜你喜欢
  • 1970-01-01
  • 2023-03-07
  • 2022-07-27
  • 2019-03-31
  • 2019-09-08
  • 2023-03-22
  • 2017-11-13
  • 1970-01-01
  • 2021-09-16
相关资源
最近更新 更多