Flutter: Splitting large viewmodel class into smaller sub-classes while keeping single instance of super class

In my app, I have a long registration flow with several different screens. I have a RegisterPage that shows a non-scrollable carousel page view with the different registration screens.

Currently, I have a massive RegisterViewModel that does logic and validation for all the screens at once. This is problematic for maintainability reasons, and I would like to split it into several smaller view models.

The file structure, just for clarity’s sake, looks like this currently:

regisration/
- register.dart
- register_vm.dart
- pages/
  - account_type.dart
  - user_credentials.dart

And it will need to end up looking like this:

registration/
- register.dart
- main_register_vm.dart
- pages/
  - account_type/
    - account_type.dart
    - account_type_vm.dart
  - credentials/
    - user_credentials.dart
    - user_credentials_vm.dart

Each screen, when the user inputs into it, should send data back to the RegisterViewModel‘s registration data, where it will be stored until the user completes the register flow where it is then sent to the database.

Each screen has different validation logic, but for all the registration screens they share these functionalities:

  • Enable/disable the “Continue” button at the bottom of each page
  • Move to the next page in the carousel
  • Save inputted data into a RegistrationData class.

I have been using get_it to save an instance of the RegisterViewModel using registerLazySingleton to make sure that each registration has the most up to date information. Some of the UI on these screens depend on the data found in RegistrationData, which is why it is super important to have 1 instance of the view model.

My problem is that I can’t seem to design a decent architecture that ensures that all data is synced across each registration screen. Currently, the file with my registration view model has over 700 lines of code – it needs to be simplified.

I wrote this new view model:

class RegistrationViewModel extends BaseViewModel
    with
        RegistrationPageHandling,
        RegistrationDataHandling,
        ContinueActionHandling,
        RegisterActionHandling {
  

  @override
  void nextPage() {
    super.nextPage();
    notifyListeners();
  }

  @override
  void previousPage() {
    super.previousPage();
    notifyListeners();
  }

  /// Enables the continue button at the bottom of the current registration page.
  @override
  void enableContinue({bool rebuild = true}) {
    super.enableContinue();
    if (rebuild) notifyListeners();
  }

  /// Disables the continue button at the bottom of the current registration page.
  @override
  void disableContinue({bool rebuild = true}) {
    super.disableContinue();
    if (rebuild) notifyListeners();
  }

  }
}

mixin ContinueActionHandling {
  bool isContinueActionDisabled = true;

  void enableContinue() {
    isContinueActionDisabled = false;
  }

  void disableContinue() {
    isContinueActionDisabled = true;
  }
}

mixin RegistrationDataHandling {


  AccountType? accountType;

  RegistrationData registrationData = RegistrationData.blank() // create default values

  set registrationData(RegistrationData newData) => registrationData = newData;
}

mixin RegistrationPageHandling {
  int pageIndex = 0;
  PageController pageController = PageController();

  void nextPage() {
    pageIndex++;
    pageController.animateToPage(pageIndex,
        duration: const Duration(milliseconds: 300), curve: Curves.easeIn);
  }

  void previousPage() {
    pageIndex--;
    pageController.animateToPage(pageIndex,
        duration: const Duration(milliseconds: 300), curve: Curves.easeIn);
  }
}

mixin RegisterActionHandling {
  Future<void> register() async {} // send registration data to database to register the user
}

This was supposed to be my overarching, “super view model” if you will. Functionality here will be shared across different registration screens.

Option 1:
I tried the following for a registration page where the user sets their account type:

class AccountTypeViewModel extends RegistrationViewModel {
  void setAccountType(AccountType type) {
    accountType = type;
    registrationData.accountType = type;
    enableContinue();
  }
}

Something like this would be ideal due to how easy it is to use, but there’s a problem.

As far as I understand, instantiating AccountTypeViewModel will also instantiate a new RegistrationViewModel. For the other registration screens, when I instantiate their respective sub view models, they will also create a new RegistrationViewModel. As a result, registrationData will hold different values for each screen, when it needs to accumulate ALL of the data that has been entered so far.

Option 2:
So I came up with something else to try and fix this problem, and it looks like this:

abstract class SubViewModelRegistration extends BaseViewModel { 
  // no longer extends RegistrationViewModel 
  final RegistrationViewModel parentViewModel;
  // must point to the same instance of parent viewmodel!
  SubViewModelRegistration(this.parentViewModel);
}

class AccountTypeViewModel extends SubViewModelRegistration {
  AccountTypeViewModel(super.parentViewModel);

  void setAccountType(AccountType type) {
    parentViewModel.accountType = type;
    parentViewModel.registrationData.accountType = type;
    parentViewModel.enableContinue();
  }
}

This seems to work fine, I guess. I am using stacked architecture’s ViewModelBuilder.reactive to listen to state changes from a viewmodel, and I could have the view subscribe to a view model of type AccountTypeViewModel(locator<RegistrationViewModel>()). But I think this is annoying to use. typing parentViewModel over and over again is not fun. Especially when this app has about 12 different registration screens. And each of those screens have really complicated validation requirements, where each one has to check and compare data saved in previous registration steps.

What would be a good way to create all of these sub-viewmodels in such a way that adheres to SOLID principles? They need to all share a common data source, a single source of truth, if you will. I also really hate having to type parentViewModel in front of every use of a variable.

I like Option 1 much much better than Option 2. Is there a way to inherit from a specific instance of a super class? That is, all sub viewmodels, when created, all extend from 1 instance of the overarching viewmodel? That would fix the issue of data not being synced across registration screens.

What’s a good course of action here? Is there an Option 3 I haven’t thought of?

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật