使用带有提供程序包的“模块”的Flutter应用程序体系结构

时间:2020-04-25 19:01:16

标签: flutter directory-structure flutter-provider

几周以来,我一直在Flutter中编写应用程序,然后开始思考几天前最好的架构是什么。

首先介绍一下上下文:

  • 这是一个使用Firebase作为后端的消息传递应用程序;
  • 在很大程度上依靠出色的Provider包来处理整个应用程序中的状态
  • 该计划将具有多种功能,这些功能可以彼此交互。
  • 我对Flutter相当陌生(主要是React / ReactNative背景),它可以解释我下面的奇怪方法。

我一直在经历不同的架构方法,并设法使一项工作似乎最终适合我。

由于我将具有多个功能,可在应用程序的不同位置重用,因此我想按功能(或模块)划分代码,然后可以在不同的屏幕中独立使用这些功能。 文件夹架构如下:

FlutterApp
|
|--> ios/
|--> android/
|--> lib/
      |
      |--> main.dart
      |--> screens/
      |       |
      |       |--> logged/
      |       |      |
      |       |      |--> profile.dart
      |       |      |--> settings.dart
      |       |      |--> ...
      |       |
      |       |--> notLogged/
      |       |      |
      |       |      |--> home.dart
      |       |      |--> loading.dart
      |       |      |--> ...
      |       
      |--> features/
              |
              |--> featureA/
              |       |
              |       |--> ui/
              |       |     |--> simpleUI.dart
              |       |     |--> complexUI.dart
              |       |--> provider/
              |       |     |-->featureAProvider.dart
              |       |--> models/
              |             |--> featureAModel1.dart
              |             |--> featureAModel2.dart
              |             |--> ...
              |
              |
              |--> featureB/
              |       |
              |       |--> ui/
              |       |     |--> simpleUI.dart
              |       |     |--> complexUI.dart
              |       |--> provider/
              |       |     |--> featureBProvider.dart
              |       |--> models/
              |             |--> featureBModel1.dart
              |             |--> featureBModel2.dart
              |             |--> ...
              |
             ...

理想情况下,每个功能都应遵循以下准则:

  • 每个功能都有一个逻辑部分(通常使用提供程序包);
  • 每个功能逻辑部件可以从另一个功能请求变量(ChangeNotifier类成员)
  • 每个功能都有一个(哑)UI部分,可以直接与“逻辑”部分进行交互(因此可能不那么愚蠢);
  • 每个功能都可以将其UI部分替换为自定义UI,但是自定义UI必须自行实现与逻辑部分的交互;
  • 每个功能,如果以后需要将它们存储在Firebase中,则可以具有代表功能资源的模型

我已经尝试过将这种方法与我的应用程序的一项功能(或两项功能取决于您的看法)一起使用,该功能可以记录/收听语音记录。 我发现它很有趣,因为您可以在一个地方录制,但可以在许多地方听录音:例如在录制之后,或者在向您发送录音时。

这是我想出的:

  • folder structure of the test 在这种情况下没有models/文件夹,因为它只是我在其他地方处理的文件
  • voiceRecorderNotifier处理文件(添加/删除)和录音(开始/结束)
  • voicePlayerNotifier要求实例化一个文件(命名为构造函数),然后处理音频文件的播放(播放,暂停,停止)。

在代码中,它有点冗长,但可以按预期工作,例如,在屏幕上,我可以请求voiceRecorder功能,如下所示:

class Screen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => VoiceRecorderNotifier(),
      child: Column(
        children: [
          AnUIWidget(),
          AnotherUIWidget(),
          ...,
          // The "dumb" feature UI widget from 'features/voiceRecorder/ui/simpleButton.dart' that can be overrided if you follow use the VoiceRecorderNotifier
          VoiceRecorderButtonSimple(), 
          ...,
        ]
      )
    );
  }
}

我也可以同时使用两个功能(voiceRecorder / voicePlayer),就像这样:

class Screen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => VoiceRecorderNotifier(),
      child: Column(
        children: [
          AnUIWidget(),
          AnotherUIWidget(),
          ...,
          VoiceRecorderButtonSimple(),
          ...,
          // adding the dependent voicePlayer feature (using the VoiceRecorderNotifier data);
          Consumer<VoiceRecorderNotifier>(
            builder: (_, _voiceRecorderNotifier, __) {
              if (_voiceRecorderNotifier.audioFile != null) {
                // We do have audio file, so we put the VoicePlayer feature
                return ChangeNotifierProvider<VoicePlayerNotifier>(
                  create: (_) => VoicePlayerNotifier.withFile(_voiceRecorderNotifier.audioFile),
                  child: VoicePlayerButtonSimple(),
                );
              } else {
                // We don't have audio file, so no voicePlayer needed
                return AnotherUIWidget(); 
              }
            }
          ),
          ...,
          AnotherUIWidget(),
        ]
      )
    );
  }
}

这是一个全新的测试,所以我认为有些缺点现在还看不到,但是我觉得其中的优点很少:

  • 更清洁的文件夹结构;
  • 一个地方来处理与该功能相关的“高级”逻辑,易于更新;
  • 在应用程序各处都可以轻松添加,移动或删除功能;
  • 提供了基本的UI,以使该功能能够按预期工作,例如简单
Text('hi')

但是我仍然可以“覆盖” UI以显示特定功能;

  • 我可以专注于UI和功能的使用,而不是创建许多有状态的组件以将功能的相同逻辑复制到不同的地方;

我看到的缺点:

  • 功能逻辑是“隐藏的”,每次我想对功能进行特定操作以记住功能的行为时,都需要遍历通知程序;
  • 在适当的地方实施通知程序会变得一团糟,如果UI小部件可以使用多种功能,那么我将需要多个FeatureNotifier(即使在这种情况下Multiprovider有用);

最后是以下问题:

  • 您认为这种方法是可扩展/推荐的,因此,如果我以后可以继续以这种方式创建功能而不会遇到麻烦的话?
  • 您还有其他缺点吗?

0 个答案:

没有答案