A serverless Flutter framework designed to be a solid starting point for new casual applications, demonstrated with an example app.
-- Note: Under construction; breaking changes may occur.
Features
- GDPR and App Store Compliance
- Terms of Service & Privacy Policy: A complete flow for presenting and requiring user acceptance of legal terms, including versioning for updates.
- Account Deletion: A clear, user-accessible path for account deletion.
- Data Access: Users can copy and export their complete event streams.
- Service Management
- Service Status & Forced Upgrades: The ability to set a minimum required application version or disable the service for maintenance, and smoothly communicate this to users.
- Service Administrators: Manage the users who can maintain policies and service status.
- Beta/Authorized Users: Limit application access to a predefined set of regular users.
- App Appearance & Personalization
- Localization: Support for multiple languages.
- Theming: Dynamic light and dark mode support.
- Collaboration & Core Features
- Shared Site (Resource) Management: Supports the common application use case of members sharing a common resource, or "site" (such as a set of photo albums, a calendar, etc.). The framework manages adding and removing site members, as well as self-removal. Site members can be assigned administrative roles. Firebase rules ensure site data privacy.
- Clean Architecture
- Event Source Framework Replayed in the Client: The common practice of having separate models for UI forms, network marshalling, database storage, and business logic is eliminated. This reduces boilerplate code and removes the need for app logic on the server side. Events are persisted on the client, significantly reducing service queries. Note: Some cloud functions are included to assist with cleanup upon account deletion.
- Event and Replay Views: Service, account, and site events (and their resulting replays) can be viewed directly in the UI, assisting with debugging.
- Persistence-Agnostic: The framework is designed to support various storage providers (e.g., Firebase, Supabase) and allows switching between them at runtime. Built-in memory and local file storage are also supported out of the box.
Usage
The repository includes examples of using the library. Follow the instructions below.
Prerequisites
- Install Flutter with Chrome support.
- Run
flutter --versionto check your version. Ensure it is at least 3.32.1. - Run
flutter devicesto check that Chrome is listed as an available device.
Installation & Setup
git clone git@github.com:bjorge/hyttahub.git
cd hyttahub
flutter pub get
flutter test
Running the Application
cd example/template
flutter run -d chrome
Persistence Registration
HyttaHub is persistence-agnostic and uses a dynamic registration system.
Please see PERSISTENCE.md for details on how to register and use multiple storage providers.
FAQ
I want to make some changes to the language files, how do I compile them?
After updating the .arb files, run:
flutter gen-l10n
I want to run the example app using the Firebase Emulator, how do I do that?
- Install either Docker or Podman.
- Follow the instructions in the
READMEfound in thetool/firebase_emulatordirectory to set up and start the emulator (this will also start the cloud functions in the emulator). - In a separate terminal, navigate to the
example/templatefolder and run:flutter run -d chrome - In the app, select "Firebase" as the storage implementation.
How do I tag a new release?
git tag v0.1.9
git push origin --tags
How do I compile the protocol buffer files?
Protocol buffer files can be compiled for both Dart (Flutter) and TypeScript (Cloud Functions) using Podman or Docker. See the README in the tool/protobuf-compiler directory for instructions.
Is any server-side code required for this project?
The project requires no long-running server-side code. As far as Cloud Functions go, the intent is to use them sparingly—only when absolutely necessary to provide access to parts of the system that are inaccessible to the client due to Firebase security rules. For example, when a site admin removes a member, a Cloud Function will securely add a "remove" event to that member's account stream (which the admin cannot access directly). Cloud Functions are also used for housekeeping tasks, such as removing abandoned site data when the last member leaves a site.
How is shared member data kept private?
Firebase rules, defined in firestore.rules and storage.rules, are designed to ensure member data privacy. Only site members are permitted to access site data, and only site administrators can add new members. Likewise, only the owner of an account can modify their account settings, and only a service administrator can update the global service status.
Can I add social authentication, such as Google or Apple logins?
The current implementation strictly uses email addresses as keys in Firebase rules and site-member management. Any Firebase Auth options that reliably retain the user's email will work natively. For Apple sign-ins, since users can choose to anonymize their email via a relay, changes would need to be made to the framework to accommodate that specific case.
Libraries
- account_blocs/account_replay_bloc
- account_blocs/account_submit_bloc
- account_blocs/acount_replay
- account_widgets/account_events_display
- account_widgets/account_screen
- account_widgets/add_site_screen
- account_widgets/copy_site_confirm_screen
- account_widgets/copy_site_screen
- account_widgets/join_site_screen
- account_widgets/leave_site_screen
- account_widgets/manage_sites_screen
- account_widgets/remove_account_screen
- account_widgets/reorder_sites_screen
- account_widgets/update_terms_form
- auth_bloc/auth_bloc
- auth_bloc/auth_submit_bloc
- auth_bloc/base_hyttahub_auth
- auth_bloc/hydrated_hyttahub_auth
- auth_bloc/hyttahub_auth_factory
- auth_bloc/hyttahub_auth_user
- auth_bloc/in_memory_hyttahub_auth
- common_blocs/allowed_emails_bloc
- common_blocs/base_replay_bloc
- common_blocs/base_submit_bloc
- common_widgets/allowed_emails_display
- common_widgets/common_form
- common_widgets/common_submit_form_layout
- common_widgets/events_display
- common_widgets/hyttahub_app_bar_actions
- common_widgets/hyttahub_info_page
- common_widgets/layout
- common_widgets/unimplemented_screen
- firebase_paths
- functions/base_hyttahub_functions
- functions/hyttahub_functions_factory
- functions/in_memory_hyttahub_functions
- functions/site_cleanup
- hyttahub
- hyttahub_app
- hyttahub_initialization
- hyttahub_options
- l10n/intl_localizations
- l10n/intl_localizations_en
- l10n/intl_localizations_es
- l10n/intl_localizations_it
- l10n/intl_localizations_nb
- l10n/intl_localizations_nl
- preferences_cubits/language_cubit
- preferences_cubits/login_cubit
- preferences_cubits/platform_cubit
- preferences_cubits/theme_cubit
- proto/account_events.pb
- proto/account_events.pbenum
- proto/account_events.pbjson
- proto/account_replay_bloc.pb
- proto/account_replay_bloc.pbenum
- proto/account_replay_bloc.pbjson
- proto/allowed_emails_bloc.pb
- proto/allowed_emails_bloc.pbenum
- proto/allowed_emails_bloc.pbjson
- proto/app_wrapper.pb
- proto/app_wrapper.pbenum
- proto/app_wrapper.pbjson
- proto/auth_bloc.pb
- proto/auth_bloc.pbenum
- proto/auth_bloc.pbjson
- proto/bloom_filter.pb
- proto/bloom_filter.pbenum
- proto/bloom_filter.pbjson
- proto/cloud_functions.pb
- proto/cloud_functions.pbenum
- proto/cloud_functions.pbjson
- proto/common_blocs.pb
- proto/common_blocs.pbenum
- proto/common_blocs.pbjson
- proto/hyttahub_implementation.pb
- proto/hyttahub_implementation.pbenum
- proto/hyttahub_implementation.pbjson
- proto/service_events.pb
- proto/service_events.pbenum
- proto/service_events.pbjson
- proto/service_replay_bloc.pb
- proto/service_replay_bloc.pbenum
- proto/service_replay_bloc.pbjson
- proto/site_email.pb
- proto/site_email.pbenum
- proto/site_email.pbjson
- proto/site_events.pb
- proto/site_events.pbenum
- proto/site_events.pbjson
- proto/site_name_replay_bloc.pb
- proto/site_name_replay_bloc.pbenum
- proto/site_name_replay_bloc.pbjson
- proto/site_replay_bloc.pb
- proto/site_replay_bloc.pbenum
- proto/site_replay_bloc.pbjson
- routes/hyttahub_routes
- service_blocs/cloud_functions_bloc
- service_blocs/service_replay
- service_blocs/service_replay_bloc
- service_blocs/service_submit_bloc
- service_widgets/add_admin_screen
- service_widgets/email_login_form
- service_widgets/email_signup_form
- service_widgets/login
- service_widgets/open_source_licenses_screen
- service_widgets/remove_admin_screen
- service_widgets/restore_admin_screen
- service_widgets/service_admin_screen
- service_widgets/service_admins_screen
- service_widgets/service_down_page
- service_widgets/service_events_display
- service_widgets/service_network_error_page
- service_widgets/service_new_version_page
- service_widgets/service_privacy_display
- service_widgets/service_settings_form
- service_widgets/service_terms_display
- service_widgets/service_uninitialized_page
- service_widgets/update_admin_screen
- site_blocs/site_name_replay
- site_blocs/site_name_replay_bloc
- site_blocs/site_replay
- site_blocs/site_replay_bloc
- site_blocs/site_submit_bloc
- site_widgets/add_member_screen
- site_widgets/remove_member_screen
- site_widgets/rename_site_screen
- site_widgets/restore_member_screen
- site_widgets/site_edit_mode_cubit
- site_widgets/site_emails_display
- site_widgets/site_events_display
- site_widgets/site_info_screen
- site_widgets/site_members_screen
- site_widgets/site_name_widget
- site_widgets/site_settings_dialog_options
- site_widgets/update_member_screen
- storage/base_hyttahub_internal_storage
- storage/base_hyttahub_storage
- storage/hydrated_hyttahub_internal_storage
- storage/hydrated_hyttahub_storage
- storage/hyttahub_internal_storage_factory
- storage/hyttahub_storage_factory
- storage/in_memory_hyttahub_internal_storage
- storage/in_memory_hyttahub_storage
- utilities/app_wrapper_util
- utilities/bloom_filter
- utilities/common_error_handling
- utilities/constants
- utilities/ids
- utilities/pattern_utils
- utilities/persistence_registries
- utils/refresh_helper
- utils/refresh_helper_stub
- utils/refresh_helper_web