├── demo ├── linux │ ├── .gitignore │ ├── main.cc │ ├── flutter │ │ ├── generated_plugin_registrant.h │ │ ├── generated_plugins.cmake │ │ ├── generated_plugin_registrant.cc │ │ └── CMakeLists.txt │ └── my_application.h ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── .gitignore │ └── Podfile ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ └── manifest.json ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── de │ │ │ │ │ │ └── skycoder42 │ │ │ │ │ │ └── flutter_data_demo │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── settings.gradle │ └── build.gradle ├── macos │ ├── Runner │ │ ├── Configs │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ ├── Warnings.xcconfig │ │ │ └── AppInfo.xcconfig │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ ├── app_icon_64.png │ │ │ │ ├── app_icon_1024.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Release.entitlements │ │ ├── MainFlutterWindow.swift │ │ ├── DebugProfile.entitlements │ │ └── Info.plist │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Podfile ├── windows │ ├── runner │ │ ├── resources │ │ │ └── app_icon.ico │ │ ├── resource.h │ │ ├── CMakeLists.txt │ │ ├── utils.h │ │ ├── runner.exe.manifest │ │ ├── flutter_window.h │ │ ├── main.cpp │ │ ├── utils.cpp │ │ └── flutter_window.cpp │ ├── .gitignore │ └── flutter │ │ ├── generated_plugin_registrant.h │ │ ├── generated_plugin_registrant.cc │ │ └── generated_plugins.cmake ├── .metadata ├── lib │ ├── src │ │ ├── auth │ │ │ ├── firebase_account.dart │ │ │ ├── firebase_auth.dart │ │ │ └── google_auth.dart │ │ ├── ui │ │ │ ├── task_list_item.dart │ │ │ ├── task_list.dart │ │ │ └── task_page.dart │ │ ├── models │ │ │ └── task.dart │ │ ├── setup │ │ │ ├── providers.dart │ │ │ ├── defines.dart │ │ │ └── application_adapter.dart │ │ └── password │ │ │ ├── password_controller.dart │ │ │ ├── application_key_manager.dart │ │ │ └── password_scope.dart │ └── main.dart ├── README.md ├── .gitignore ├── pubspec.yaml └── analysis_options.yaml ├── packages ├── flutter_data_sync │ ├── .gitignore │ ├── example │ │ └── flutter_data_sync_example.dart │ ├── lib │ │ ├── flutter_data_sync.dart │ │ └── src │ │ │ ├── sync_controller.dart │ │ │ └── semaphore.dart │ ├── analysis_options.yaml │ ├── CHANGELOG.md │ ├── pubspec.yaml │ ├── LICENSE │ ├── README.md │ └── doc │ │ └── retry_state_machine.drawio ├── flutter_data_sodium │ ├── test │ │ ├── .gitignore │ │ ├── unit │ │ │ ├── coverage_test.dart │ │ │ ├── util │ │ │ │ ├── uint8list_converter_test.dart │ │ │ │ └── sodium_uuid_test.dart │ │ │ └── hive │ │ │ │ └── configure_repository_local_storage_test.dart │ │ └── integration │ │ │ ├── repositories │ │ │ ├── test_adapter.dart │ │ │ ├── test_repository.dart │ │ │ └── test.data.dart │ │ │ └── setup │ │ │ ├── sodium_setup_js.dart │ │ │ ├── test_key_manager.dart │ │ │ ├── http_setup.dart │ │ │ ├── adapter_setup.dart │ │ │ ├── setup.dart │ │ │ └── sodium_setup_vm.dart │ ├── lib │ │ ├── src │ │ │ ├── .gitignore │ │ │ ├── util │ │ │ │ ├── uint8list_converter.dart │ │ │ │ └── sodium_uuid.dart │ │ │ ├── key_management │ │ │ │ └── key_info.dart │ │ │ ├── encryption │ │ │ │ ├── encrypted_data.dart │ │ │ │ └── data_cipher.dart │ │ │ ├── hive │ │ │ │ ├── configure_repository_local_storage.dart │ │ │ │ └── sodium_hive_local_storage.dart │ │ │ └── sodium_remote_adapter.dart │ │ └── flutter_data_sodium.dart │ ├── example │ │ └── flutter_data_sodium_example.dart │ ├── .gitignore │ ├── analysis_options.yaml │ ├── CHANGELOG.md │ ├── tool │ │ ├── download_sodium_web.dart │ │ └── download_sodium_windows.dart │ ├── pubspec.yaml │ └── LICENSE └── flutter_data_firebase_database │ ├── test │ ├── .gitignore │ ├── unit │ │ ├── coverage_test.dart │ │ ├── test_data_model.dart │ │ └── queries │ │ │ ├── request_config_test.dart │ │ │ ├── filter_test.dart │ │ │ └── timeout_test.dart │ └── integration │ │ ├── repositories │ │ ├── test_adapter.dart │ │ ├── test_repository.dart │ │ ├── values_repository.dart │ │ └── test.data.dart │ │ ├── setup │ │ ├── setup.dart │ │ ├── repo_setup.dart │ │ ├── models │ │ │ └── account.dart │ │ ├── test_repository_setup.dart │ │ ├── di_setup.dart │ │ ├── config_setup.dart │ │ ├── database_setup.dart │ │ └── account_setup.dart │ │ └── transaction_test.dart │ ├── lib │ ├── src │ │ ├── .gitignore │ │ ├── stream │ │ │ ├── event_stream │ │ │ │ ├── database_event_stream.dart │ │ │ │ ├── http_database_event_stream.dart │ │ │ │ └── web_database_event_stream.dart │ │ │ ├── errors │ │ │ │ ├── authentication_revoked.dart │ │ │ │ └── remote_cancellation.dart │ │ │ ├── database_event.dart │ │ │ ├── stream_one_controller.dart │ │ │ └── stream_controller_base.dart │ │ ├── queries │ │ │ ├── format_mode.dart │ │ │ ├── write_size_limit.dart │ │ │ ├── request_config.dart │ │ │ └── timeout.dart │ │ ├── transactions │ │ │ ├── etag_constants.dart │ │ │ └── transaction_rejected.dart │ │ └── serialization │ │ │ └── firebase_value_transformer.dart │ └── flutter_data_firebase_database.dart │ ├── example │ ├── lib │ │ ├── .gitignore │ │ └── src │ │ │ ├── example_model.dart │ │ │ └── example_remote_adapter.dart │ ├── analysis_options.yaml │ ├── .gitignore │ ├── README.md │ ├── pubspec.yaml │ └── bin │ │ └── flutter_data_firebase_database_example.dart │ ├── analysis_options.yaml │ ├── CHANGELOG.md │ ├── pubspec.yaml │ └── LICENSE ├── README.md ├── .vscode ├── settings.json ├── extensions.json └── launch.json ├── .github └── workflows │ ├── flutter_data_sync.yaml │ ├── flutter_data_firebase_database.yaml │ └── flutter_data_sodium.yaml ├── melos.yaml ├── .gitignore └── LICENSE /demo/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/.gitignore: -------------------------------------------------------------------------------- 1 | lib/main.data.dart 2 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/.gitignore: -------------------------------------------------------------------------------- 1 | *.freezed.dart 2 | *.g.dart 3 | -------------------------------------------------------------------------------- /demo/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/.gitignore: -------------------------------------------------------------------------------- 1 | *.freezed.dart 2 | *.g.dart 3 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/.gitignore: -------------------------------------------------------------------------------- 1 | *.freezed.dart 2 | *.g.dart 3 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/example/flutter_data_sodium_example.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/example/flutter_data_sync_example.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/.gitignore: -------------------------------------------------------------------------------- 1 | *.freezed.dart 2 | *.g.dart 3 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/.gitignore: -------------------------------------------------------------------------------- 1 | lib/main.data.dart 2 | test/integration/binaries 3 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/lib/flutter_data_sync.dart: -------------------------------------------------------------------------------- 1 | export 'src/sync_controller.dart'; 2 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/lib/.gitignore: -------------------------------------------------------------------------------- 1 | main.data.dart 2 | *.g.dart 3 | -------------------------------------------------------------------------------- /demo/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/web/favicon.png -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | exclude: 3 | - bin 4 | -------------------------------------------------------------------------------- /demo/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /demo/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /demo/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/web/icons/Icon-192.png -------------------------------------------------------------------------------- /demo/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/web/icons/Icon-512.png -------------------------------------------------------------------------------- /demo/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /demo/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /demo/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_data_extensions 2 | A collection of packages that extend functionally of flutter_data. Namely firebase, encryption and sync. 3 | -------------------------------------------------------------------------------- /demo/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /demo/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /demo/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dart.runPubGetOnPubspecChanges": false, 3 | "cSpell.words": [ 4 | "pwhash", 5 | "randombytes", 6 | "subkey" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /demo/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | 9 | Podfile.lock 10 | -------------------------------------------------------------------------------- /demo/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /demo/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:dart_test_tools/analysis_options_package.yaml 2 | 3 | linter: 4 | rules: 5 | public_member_api_docs: false 6 | -------------------------------------------------------------------------------- /demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub. 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build output. 6 | build/ 7 | -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/event_stream/database_event_stream.dart: -------------------------------------------------------------------------------- 1 | export 'http_database_event_stream.dart' 2 | if (dart.library.html) 'web_database_event_stream.dart'; 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "blaugold.melos-code", 4 | "dart-code.dart-code", 5 | "dart-code.flutter", 6 | "me-dutour-mathieu.vscode-github-actions" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:dart_test_tools/analysis_options_package.yaml 2 | 3 | analyzer: 4 | exclude: 5 | - "**.freezed.dart" 6 | - "**.g.dart" 7 | -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skycoder42/flutter_data_extensions/main/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /demo/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/queries/format_mode.dart: -------------------------------------------------------------------------------- 1 | /// Specifies how the server should format the response 2 | enum FormatMode { 3 | /// Include priority information in the response. 4 | export, 5 | } 6 | -------------------------------------------------------------------------------- /demo/android/app/src/main/kotlin/de/skycoder42/flutter_data_demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package de.skycoder42.flutter_data_demo 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:dart_test_tools/analysis_options_package.yaml 2 | 3 | analyzer: 4 | exclude: 5 | - "**.freezed.dart" 6 | - "**.g.dart" 7 | - lib/main.data.dart 8 | - test/integration/setup/sodium_setup_js.dart 9 | -------------------------------------------------------------------------------- /demo/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/unit/coverage_test.dart: -------------------------------------------------------------------------------- 1 | // ignore: test_library_import 2 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | void main() { 6 | test('ensure coverage', () { 7 | expect(SodiumRemoteAdapter, anything); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /demo/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/unit/coverage_test.dart: -------------------------------------------------------------------------------- 1 | // ignore: test_library_import 2 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | void main() { 6 | test('ensure coverage', () { 7 | expect(FirebaseDatabaseAdapter, anything); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /demo/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5464c5bac742001448fe4fc0597be939379f88ea 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /demo/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /demo/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /demo/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_data_firebase_database Example 2 | Check out the `bin/flutter_data_firebase_database_example.dart` file as well as the other files below `lib/src` for 3 | an example on how to use the package. 4 | 5 | Direct Link to the example on github: https://github.com/Skycoder42/flutter_data_extensions/tree/main/packages/flutter_data_firebase_database/example 6 | -------------------------------------------------------------------------------- /demo/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /demo/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 0.0.0 8 | ### Added 9 | ### Changed 10 | ### Deprecated 11 | ### Removed 12 | ### Fixed 13 | ### Security 14 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/errors/authentication_revoked.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | 3 | /// An [Exception] indicating that a the credential used by the stream has 4 | /// expired. This event is sent when the supplied auth parameter is no longer 5 | /// valid. 6 | class AuthenticationRevoked implements Exception { 7 | @override 8 | String toString() => 'The authentication token has expired or was revoked.'; 9 | } 10 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 1.0.0 8 | - initial release 9 | 10 | ## 0.0.0 11 | ### Added 12 | ### Changed 13 | ### Deprecated 14 | ### Removed 15 | ### Fixed 16 | ### Security 17 | -------------------------------------------------------------------------------- /demo/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /demo/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/queries/write_size_limit.dart: -------------------------------------------------------------------------------- 1 | /// The Realtime Database estimates the size of each write request and aborts 2 | /// requests that will take longer than the target time. 3 | enum WriteSizeLimit { 4 | /// target=1s 5 | tiny, 6 | 7 | /// target=10s 8 | small, 9 | 10 | /// target=30s 11 | medium, 12 | 13 | /// target=60s 14 | large, 15 | 16 | /// Exceptionally large writes (with up to 256MB payload) are allowed 17 | unlimited, 18 | } 19 | -------------------------------------------------------------------------------- /demo/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | com.apple.security.network.client 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_data_firebase_database_example 2 | description: A simple command-line application. 3 | version: 0.0.1 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=2.16.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter_data: ^1.3.4 11 | flutter_data_firebase_database: 12 | path: .. 13 | json_annotation: ^4.4.0 14 | meta: ^1.7.0 15 | riverpod: ^1.0.3 16 | 17 | dev_dependencies: 18 | build_runner: ^2.1.8 19 | json_serializable: ^6.1.5 20 | -------------------------------------------------------------------------------- /demo/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /demo/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/flutter_data_sodium.dart: -------------------------------------------------------------------------------- 1 | export 'src/hive/configure_repository_local_storage.dart'; 2 | export 'src/hive/sodium_hive_cipher.dart'; 3 | export 'src/hive/sodium_hive_local_storage.dart'; 4 | export 'src/key_management/key_info.dart'; 5 | export 'src/key_management/key_manager.dart'; 6 | export 'src/key_management/parallel_master_key_computation.dart'; 7 | export 'src/key_management/passphrase_based_key_manager.dart'; 8 | export 'src/sodium_remote_adapter.dart'; 9 | export 'src/util/sodium_uuid.dart'; 10 | -------------------------------------------------------------------------------- /demo/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /demo/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo/lib/src/auth/firebase_account.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'firebase_account.g.dart'; 4 | 5 | @JsonSerializable() 6 | class FirebaseAccount { 7 | final String idToken; 8 | final String localId; 9 | 10 | const FirebaseAccount({ 11 | required this.idToken, 12 | required this.localId, 13 | }); 14 | 15 | factory FirebaseAccount.fromJson(Map json) => 16 | _$FirebaseAccountFromJson(json); 17 | 18 | Map toJson() => _$FirebaseAccountToJson(this); 19 | } 20 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/util/uint8list_converter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:json_annotation/json_annotation.dart'; 5 | import 'package:meta/meta.dart'; 6 | 7 | /// @nodoc 8 | @internal 9 | class Uint8ListConverter implements JsonConverter { 10 | /// @nodoc 11 | const Uint8ListConverter(); 12 | 13 | @override 14 | Uint8List fromJson(String json) => base64.decode(json); 15 | 16 | @override 17 | String toJson(Uint8List object) => base64.encode(object); 18 | } 19 | -------------------------------------------------------------------------------- /demo/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void RegisterPlugins(flutter::PluginRegistry* registry) { 13 | SodiumLibsPluginRegisterWithRegistrar( 14 | registry->GetRegistrarForPlugin("SodiumLibsPlugin")); 15 | UrlLauncherWindowsRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 17 | } 18 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 1.0.1 8 | ### Changed 9 | - Small fixups required for a better score on pub.dev 10 | 11 | ## 1.0.0 12 | ### Added 13 | - Initial release 14 | 15 | ## 0.0.0 16 | ### Added 17 | ### Changed 18 | ### Deprecated 19 | ### Removed 20 | ### Fixed 21 | ### Security 22 | -------------------------------------------------------------------------------- /demo/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import path_provider_macos 9 | import sodium_libs 10 | import url_launcher_macos 11 | 12 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 13 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 14 | SodiumLibsPlugin.register(with: registry.registrar(forPlugin: "SodiumLibsPlugin")) 15 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 16 | } 17 | -------------------------------------------------------------------------------- /demo/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | sodium_libs 7 | url_launcher_linux 8 | ) 9 | 10 | set(PLUGIN_BUNDLED_LIBRARIES) 11 | 12 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 13 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 14 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 17 | endforeach(plugin) 18 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/transactions/etag_constants.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | 3 | // ignore_for_file: public_member_api_docs 4 | 5 | import 'package:meta/meta.dart'; 6 | 7 | @internal 8 | abstract class ETagConstants { 9 | const ETagConstants._(); // coverage:ignore-line 10 | 11 | static const requestETagHeaders = {'X-Firebase-ETag': 'true'}; 12 | 13 | static const eTagHeaderName = 'ETag'; 14 | 15 | static const ifMatchHeaderName = 'if-match'; 16 | 17 | static const statusCodeETagMismatch = 412; 18 | 19 | static const nullETag = 'null_etag'; 20 | } 21 | -------------------------------------------------------------------------------- /demo/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | sodium_libs 7 | url_launcher_windows 8 | ) 9 | 10 | set(PLUGIN_BUNDLED_LIBRARIES) 11 | 12 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 13 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 14 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 17 | endforeach(plugin) 18 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Run Web", 9 | "request": "launch", 10 | "type": "dart", 11 | "codeLens": { 12 | "for": [ 13 | "run-test", 14 | "run-test-file" 15 | ] 16 | }, 17 | "toolArgs": [ 18 | "-p", 19 | "chrome" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/flutter_data_firebase_database.dart: -------------------------------------------------------------------------------- 1 | export 'src/firebase_database_adapter.dart'; 2 | export 'src/queries/filter.dart'; 3 | export 'src/queries/format_mode.dart'; 4 | export 'src/queries/request_config.dart'; 5 | export 'src/queries/timeout.dart'; 6 | export 'src/queries/write_size_limit.dart'; 7 | export 'src/server_values/server_incrementable.dart'; 8 | export 'src/server_values/server_timestamp.dart'; 9 | export 'src/stream/errors/authentication_revoked.dart'; 10 | export 'src/stream/errors/remote_cancellation.dart'; 11 | export 'src/transactions/transaction_rejected.dart'; 12 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # flutter_data_demo 2 | 3 | Demo app for all the flutter_data extensions 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /demo/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /demo/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "utils.cpp" 8 | "win32_window.cpp" 9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 10 | "Runner.rc" 11 | "runner.exe.manifest" 12 | ) 13 | apply_standard_settings(${BINARY_NAME}) 14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 17 | add_dependencies(${BINARY_NAME} flutter_assemble) 18 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_data_sync 2 | description: An extension package to flutter_data that adds a helper class for offline synchronization 3 | version: 1.0.0 4 | homepage: https://github.com/Skycoder42/flutter_data_extensions 5 | 6 | environment: 7 | sdk: ">=2.16.0 <3.0.0" 8 | 9 | dependencies: 10 | bloc_concurrency: ^0.2.0 11 | flutter_data: 1.3.4 12 | freezed_annotation: ^1.1.0 13 | meta: ^1.7.0 14 | state_machine_bloc: ^0.0.1 15 | 16 | dev_dependencies: 17 | bloc_test: ^9.0.3 18 | build_runner: ^2.1.10 19 | dart_pre_commit: ^3.0.0 20 | dart_test_tools: ^3.3.1 21 | freezed: ^1.1.1 22 | mocktail: ^0.3.0 23 | test: ^1.21.1 24 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/unit/test_data_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | 3 | class TestDataModel extends DataModel { 4 | @override 5 | final String? id; 6 | final int? data; 7 | 8 | TestDataModel({ 9 | this.id, 10 | this.data, 11 | }); 12 | 13 | @override 14 | bool operator ==(Object other) { 15 | if (other is! TestDataModel) { 16 | return false; 17 | } 18 | 19 | return id == other.id && data == other.data; 20 | } 21 | 22 | @override 23 | int get hashCode => id.hashCode ^ data.hashCode; 24 | 25 | @override 26 | String toString() => 'TestDataModel($id, $data)'; 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/flutter_data_sync.yaml: -------------------------------------------------------------------------------- 1 | name: CI/CD for flutter_data_sync 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "*" 8 | paths: 9 | - "packages/flutter_data_sync/**" 10 | - ".github/workflows/flutter_data_sync.yaml" 11 | pull_request: 12 | branches: 13 | - "*" 14 | paths: 15 | - "packages/flutter_data_sync/**" 16 | - ".github/workflows/flutter_data_sync.yaml" 17 | 18 | jobs: 19 | ci: 20 | name: CI 21 | uses: Skycoder42/dart_test_tools/.github/workflows/dart.yml@main 22 | with: 23 | workingDirectory: packages/flutter_data_sync 24 | unitTestPaths: test/unit 25 | integrationTestPaths: test/integration 26 | -------------------------------------------------------------------------------- /demo/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = flutter_data_demo 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = de.skycoder42.flutterDataDemo 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2022 de.skycoder42. All rights reserved. 15 | -------------------------------------------------------------------------------- /demo/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /demo/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/lib/src/example_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 3 | import 'package:json_annotation/json_annotation.dart'; 4 | import 'package:meta/meta.dart'; 5 | 6 | import 'example_remote_adapter.dart'; 7 | 8 | part 'example_model.g.dart'; 9 | 10 | @internal 11 | @JsonSerializable() 12 | @DataRepository([FirebaseDatabaseAdapter, ExampleRemoteAdapter]) 13 | class ExampleModel with DataModel { 14 | @override 15 | final String? id; 16 | 17 | final String title; 18 | final bool completed; 19 | 20 | ExampleModel({this.id, required this.title, this.completed = false}); 21 | } 22 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/repositories/test_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | // ignore: test_library_import 3 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 4 | 5 | import '../setup/account_setup.dart'; 6 | import '../setup/config_setup.dart'; 7 | import '../setup/database_setup.dart'; 8 | 9 | mixin TestAdapter> on FirebaseDatabaseAdapter { 10 | @override 11 | String get baseUrl => Uri.https( 12 | read(ConfigSetup.databaseHostProvider), 13 | read(DatabaseSetup.databasePathProvider), 14 | ).toString(); 15 | 16 | @override 17 | String get idToken => read(AccountSetup.accountProvider).idToken; 18 | } 19 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/setup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:test/test.dart' as test; 4 | 5 | abstract class Setup { 6 | @protected 7 | @mustCallSuper 8 | Future setUpAll() => Future.value(); 9 | 10 | @protected 11 | @mustCallSuper 12 | Future tearDownAll() => Future.value(); 13 | 14 | @protected 15 | @mustCallSuper 16 | Future setUp() => Future.value(); 17 | 18 | @protected 19 | @mustCallSuper 20 | Future tearDown() => Future.value(); 21 | 22 | void call() { 23 | test.setUpAll(setUpAll); 24 | test.tearDownAll(tearDownAll); 25 | 26 | test.setUp(setUp); 27 | test.tearDown(tearDown); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demo/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/repo_setup.dart: -------------------------------------------------------------------------------- 1 | // ignore: test_library_import 2 | import 'package:flutter_data/flutter_data.dart'; 3 | // ignore: test_library_import 4 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 5 | 6 | import '../repositories/test.data.dart'; 7 | import 'database_setup.dart'; 8 | 9 | mixin RepoSetup on DatabaseSetup { 10 | @override 11 | Future setUpAll() async { 12 | await super.setUpAll(); 13 | 14 | di.read(hiveLocalStorageProvider).hive 15 | ..registerAdapter(const ServerTimestampHiveAdapter()) 16 | ..registerAdapter(const ServerIncrementableHiveAdapter()); 17 | 18 | await di.read(repositoryInitializerProvider(verbose: true).future); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/unit/util/uint8list_converter_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter_data_sodium/src/util/uint8list_converter.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | void main() { 7 | group('Uint8ListConverter', () { 8 | final binaryData = Uint8List.fromList(List.generate(14, (index) => index)); 9 | const base64Data = 'AAECAwQFBgcICQoLDA0='; 10 | 11 | const sut = Uint8ListConverter(); 12 | 13 | test('toJson creates base64 encoded data', () { 14 | final result = sut.toJson(binaryData); 15 | expect(result, base64Data); 16 | }); 17 | 18 | test('fromJson decodes base64 data', () { 19 | final result = sut.fromJson(base64Data); 20 | expect(result, binaryData); 21 | }); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /demo/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void fl_register_plugins(FlPluginRegistry* registry) { 13 | g_autoptr(FlPluginRegistrar) sodium_libs_registrar = 14 | fl_plugin_registry_get_registrar_for_plugin(registry, "SodiumLibsPlugin"); 15 | sodium_libs_plugin_register_with_registrar(sodium_libs_registrar); 16 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 17 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 18 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 19 | } 20 | -------------------------------------------------------------------------------- /melos.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_data_extensions 2 | repository: https://github.com/Skycoder42/flutter_data_extensions 3 | 4 | packages: 5 | - packages/** 6 | 7 | scripts: 8 | get: dart pub global run melos exec -- dart pub get 9 | 10 | upgrade: dart pub global run melos exec -- dart pub upgrade 11 | 12 | build: dart pub global run melos exec -- dart run build_runner build 13 | 14 | build:watch: dart pub global run melos exec -- dart run build_runner watch 15 | 16 | hooks:init: >- 17 | bash -c "echo -e 18 | '#!/bin/sh\nexec dart pub global run melos run hooks:run' 19 | > .git/hooks/pre-commit 20 | && chmod a+x .git/hooks/pre-commit" 21 | 22 | hooks:run: >- 23 | dart pub global run melos exec 24 | --depends-on=dart_pre_commit -c1 -- 25 | dart run dart_pre_commit 26 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/lib/src/example_remote_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | @internal 6 | late final baseUrlProvider = StateProvider((ref) => ''); 7 | 8 | @internal 9 | late final idTokenProvider = StateProvider((ref) => ''); 10 | 11 | @internal 12 | mixin ExampleRemoteAdapter> 13 | on FirebaseDatabaseAdapter { 14 | // you can get the base url of your firebase application from the `databaseURL` value in the firebase config 15 | @override 16 | String get baseUrl => read(baseUrlProvider); 17 | 18 | @override 19 | String? get idToken => read(idTokenProvider); 20 | } 21 | -------------------------------------------------------------------------------- /demo/lib/src/ui/task_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_data_demo/src/models/task.dart'; 3 | import 'package:flutter_data/flutter_data.dart'; 4 | 5 | class TaskListItem extends StatelessWidget { 6 | final Task task; 7 | 8 | const TaskListItem({ 9 | Key? key, 10 | required this.task, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) => ListTile( 15 | title: Text(task.title), 16 | subtitle: Text(task.id ?? 'saving...'), 17 | trailing: Checkbox( 18 | value: task.completed, 19 | onChanged: _toggleCompleted, 20 | ), 21 | ); 22 | 23 | Future _toggleCompleted(value) async { 24 | await task.copyWith(completed: value).was(task).save(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/key_management/key_info.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:sodium/sodium.dart'; 5 | 6 | part 'key_info.freezed.dart'; 7 | 8 | /// Provides a [keyId] and a [secureKey] pair. 9 | /// 10 | /// Used by [KeyManager.remoteKeyForType] to provide both, the current key 11 | /// and the id required for decryption. 12 | @freezed 13 | class KeyInfo with _$KeyInfo { 14 | /// Default constructor. 15 | const factory KeyInfo( 16 | /// The id of the [secureKey]. 17 | /// 18 | /// Can be used in conjunction with [KeyManager.remoteKeyForTypeAndId] to 19 | /// re-obtain the same key. 20 | int keyId, 21 | 22 | /// The actual key. 23 | SecureKey secureKey, 24 | ) = _KeyInfo; 25 | } 26 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/repositories/test_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | // ignore: test_library_import 3 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 4 | import 'package:sodium/sodium.dart'; 5 | 6 | mixin TestAdapter> on SodiumRemoteAdapter { 7 | static late final sodiumProvider = Provider( 8 | (ref) => throw UnimplementedError(), 9 | ); 10 | 11 | static late final keyManagerProvider = Provider( 12 | (ref) => throw UnimplementedError(), 13 | ); 14 | 15 | @override 16 | String get baseUrl => 'http://localhost'; 17 | 18 | @override 19 | Sodium get sodium => read(sodiumProvider); 20 | 21 | @override 22 | KeyManager get keyManager => read(keyManagerProvider); 23 | } 24 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/tool/download_sodium_web.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:http/http.dart' as http; 4 | 5 | const _sodiumJsVersion = '0.7.10'; 6 | 7 | Future main() async { 8 | final webTestDir = Directory('test/integration/binaries/web'); 9 | await webTestDir.create(recursive: true); 10 | 11 | final baseUri = Uri.https( 12 | 'raw.githubusercontent.com', 13 | '/jedisct1/libsodium.js/$_sodiumJsVersion/dist/browsers/sodium.js', 14 | ); 15 | final data = await http.get(baseUri); 16 | final jsDartFileSink = 17 | File.fromUri(webTestDir.uri.resolve('sodium.js.dart')).openWrite() 18 | ..writeln("const sodiumJsSrc = r'''") 19 | ..write(data.body) 20 | ..writeln("''';"); 21 | await jsDartFileSink.flush(); 22 | await jsDartFileSink.close(); 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | # If you don't generate documentation locally you can remove this line. 12 | doc/api/ 13 | 14 | # test coverage 15 | coverage 16 | 17 | # Avoid committing generated Javascript files: 18 | *.dart.js 19 | *.info.json # Produced by the --dump-info flag. 20 | *.js # When generated by dart2js. Don't specify *.js if your 21 | # project includes source files written in JavaScript. 22 | *.js_ 23 | *.js.deps 24 | *.js.map 25 | 26 | # melos files 27 | *.iml 28 | .idea 29 | 30 | #env files 31 | .env 32 | -------------------------------------------------------------------------------- /demo/lib/src/models/task.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 3 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | 6 | import '../setup/application_adapter.dart'; 7 | 8 | part 'task.freezed.dart'; 9 | part 'task.g.dart'; 10 | 11 | @freezed 12 | @DataRepository([ 13 | FirebaseDatabaseAdapter, 14 | SodiumRemoteAdapter, 15 | ApplicationAdapter, 16 | ]) 17 | class Task with DataModel, _$Task { 18 | Task._(); 19 | 20 | factory Task({ 21 | String? id, 22 | required String title, 23 | @Default(false) bool completed, 24 | }) = _Task; 25 | 26 | factory Task.fromJson(Map json) => _$TaskFromJson(json); 27 | } 28 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_data_firebase_database 2 | description: An extension package to flutter_data that adds a remote adapter to connect with the firebase realtime database. 3 | version: 1.0.1 4 | homepage: https://github.com/Skycoder42/flutter_data_extensions 5 | 6 | environment: 7 | sdk: ">=2.16.0 <3.0.0" 8 | 9 | dependencies: 10 | eventsource: ^0.4.0 11 | flutter_data: 1.3.4 12 | freezed_annotation: ^1.1.0 13 | hive: ^2.1.0 14 | http: ^0.13.4 15 | json_annotation: ^4.5.0 16 | meta: ^1.7.0 17 | rxdart: ^0.27.3 18 | 19 | dev_dependencies: 20 | build_runner: ^2.1.10 21 | dart_pre_commit: ^3.0.0 22 | dart_test_tools: ^3.3.1 23 | freezed: ^1.1.1 24 | json_serializable: ^6.2.0 25 | mocktail: ^0.3.0 26 | test: ^1.21.1 27 | tuple: ^2.0.0 28 | universal_io: ^2.0.4 29 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_data_sodium 2 | description: An extension package to flutter_data that adds End-To-End-Encryption to all requests 3 | version: 1.0.0 4 | homepage: https://github.com/Skycoder42/flutter_data_extensions 5 | 6 | environment: 7 | sdk: ">=2.16.0 <3.0.0" 8 | 9 | dependencies: 10 | clock: ^1.1.0 11 | flutter_data: 1.3.4 12 | freezed_annotation: ^1.1.0 13 | hive: ^2.1.0 14 | json_annotation: ^4.5.0 15 | meta: ^1.7.0 16 | sodium: ^1.2.0+2 17 | uuid: ^3.0.6 18 | 19 | dev_dependencies: 20 | build_runner: ^2.1.10 21 | dart_pre_commit: ^3.0.0 22 | dart_test_tools: ^3.3.1 23 | freezed: ^1.1.1 24 | http: ^0.13.4 25 | js: ^0.6.4 26 | json_serializable: ^6.2.0 27 | mocktail: ^0.3.0 28 | stream_channel: ^2.1.0 29 | test: ^1.21.1 30 | tuple: ^2.0.0 31 | universal_io: ^2.0.4 32 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/encryption/encrypted_data.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | 3 | import 'dart:typed_data'; 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | import '../util/uint8list_converter.dart'; 8 | 9 | part 'encrypted_data.freezed.dart'; 10 | part 'encrypted_data.g.dart'; 11 | 12 | /// @nodoc 13 | @internal 14 | @freezed 15 | class EncryptedData with _$EncryptedData { 16 | /// @nodoc 17 | @Uint8ListConverter() 18 | const factory EncryptedData({ 19 | required Object? id, 20 | required Uint8List cipherText, 21 | required Uint8List mac, 22 | required Uint8List nonce, 23 | required bool hasAd, 24 | required int keyId, 25 | }) = _EncryptedData; 26 | 27 | /// @nodoc 28 | factory EncryptedData.fromJson(Map json) => 29 | _$EncryptedDataFromJson(json); 30 | } 31 | -------------------------------------------------------------------------------- /demo/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/models/account.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'account.g.dart'; 4 | 5 | @JsonSerializable() 6 | class Account { 7 | final String idToken; 8 | final String localId; 9 | 10 | const Account({ 11 | required this.idToken, 12 | required this.localId, 13 | }); 14 | 15 | factory Account.fromJson(Map json) => 16 | _$AccountFromJson(json); 17 | 18 | Map toJson() => _$AccountToJson(this); 19 | } 20 | 21 | @JsonSerializable() 22 | class DeleteAccountPostModel { 23 | final String idToken; 24 | 25 | DeleteAccountPostModel(this.idToken); 26 | 27 | // ignore: unused_element 28 | factory DeleteAccountPostModel.fromJson(Map json) => 29 | _$DeleteAccountPostModelFromJson(json); 30 | 31 | Map toJson() => _$DeleteAccountPostModelToJson(this); 32 | } 33 | -------------------------------------------------------------------------------- /demo/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/test_repository_setup.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | // ignore: test_library_import 3 | 4 | import '../repositories/test_repository.dart'; 5 | import 'account_setup.dart'; 6 | import 'config_setup.dart'; 7 | import 'database_setup.dart'; 8 | import 'di_setup.dart'; 9 | import 'repo_setup.dart'; 10 | import 'setup.dart'; 11 | 12 | class TestRepositorySetup extends Setup 13 | with DiSetup, ConfigSetup, AccountSetup, DatabaseSetup, RepoSetup { 14 | late Repository repository; 15 | 16 | @override 17 | Future setUpAll() async { 18 | await super.setUpAll(); 19 | 20 | repository = di.read(testModelsRepositoryProvider); 21 | } 22 | 23 | @override 24 | Future tearDownAll() async { 25 | try { 26 | await repository.clear(); 27 | repository.dispose(); 28 | } finally { 29 | await super.tearDownAll(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/repositories/test_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | // ignore: test_library_import 3 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 4 | import 'package:json_annotation/json_annotation.dart'; 5 | 6 | import 'test_adapter.dart'; 7 | 8 | part 'test_repository.g.dart'; 9 | 10 | @JsonSerializable() 11 | @DataRepository([SodiumRemoteAdapter, TestAdapter]) 12 | class TestModel extends DataModel { 13 | @override 14 | final String? id; 15 | final String name; 16 | 17 | TestModel({this.id, required this.name}); 18 | 19 | @override 20 | bool operator ==(Object other) { 21 | if (other is! TestModel) { 22 | return false; 23 | } 24 | 25 | return id == other.id && name == other.name; 26 | } 27 | 28 | @override 29 | int get hashCode => id.hashCode ^ name.hashCode; 30 | 31 | @override 32 | String toString() => 'TestModel($id, $name)'; 33 | } 34 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/util/sodium_uuid.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:sodium/sodium.dart'; 4 | import 'package:uuid/uuid.dart'; 5 | 6 | /// A callback that will generate 16 bytes of random data. 7 | typedef UuidGrngFn = Uint8List Function(); 8 | 9 | /// An extension on [Sodium] that provides a [Uuid] instances that uses 10 | /// [Randombytes] to securely generate truly random UUIDs. 11 | extension SodiumUuid on Sodium { 12 | static late final _uuids = Expando(); 13 | 14 | /// Creates a callback that returns 16 bytes of random data from a 15 | /// [randombytes] instance. 16 | static UuidGrngFn grng(Randombytes randombytes) => () => randombytes.buf(16); 17 | 18 | /// Returns a [Uuid] instance that uses [Randombytes] as random generator to 19 | /// generate random UUIDs. 20 | Uuid get uuid => _uuids[this] ??= Uuid( 21 | options: { 22 | 'grng': grng(randombytes), 23 | }, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | # Generated files 49 | *.g.dart 50 | *.freezed.dart 51 | main.data.dart 52 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/repositories/test_repository.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_mixin 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | // ignore: test_library_import 5 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 6 | import 'package:json_annotation/json_annotation.dart'; 7 | 8 | import 'test_adapter.dart'; 9 | 10 | part 'test_repository.g.dart'; 11 | 12 | @JsonSerializable() 13 | @DataRepository([FirebaseDatabaseAdapter, TestAdapter]) 14 | class TestModel with DataModel { 15 | @override 16 | final String? id; 17 | final String name; 18 | 19 | TestModel({this.id, required this.name}); 20 | 21 | @override 22 | bool operator ==(Object other) { 23 | if (other is! TestModel) { 24 | return false; 25 | } 26 | 27 | return id == other.id && name == other.name; 28 | } 29 | 30 | @override 31 | int get hashCode => id.hashCode ^ name.hashCode; 32 | 33 | @override 34 | String toString() => 'TestModel($id, $name)'; 35 | } 36 | -------------------------------------------------------------------------------- /demo/lib/src/ui/task_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_data_demo/main.data.dart'; 3 | import 'package:flutter_data_demo/src/ui/task_list_item.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | class TaskList extends ConsumerWidget { 7 | final GlobalKey refreshIndicatorKey; 8 | 9 | const TaskList({ 10 | Key? key, 11 | required this.refreshIndicatorKey, 12 | }) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context, WidgetRef ref) { 16 | final dataState = ref.tasks.watchAll(syncLocal: false); 17 | return RefreshIndicator( 18 | key: refreshIndicatorKey, 19 | onRefresh: () => ref.tasks.findAll(syncLocal: true), 20 | child: ListView.builder( 21 | physics: const AlwaysScrollableScrollPhysics(), 22 | itemCount: dataState.model.length, 23 | itemBuilder: (context, index) => TaskListItem( 24 | task: dataState.model[index], 25 | ), 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/errors/remote_cancellation.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | 3 | /// Some unexpected errors can send a `cancel` event and terminate the 4 | /// connection. The cause is described in the [reason] provided for this 5 | /// [Exception]. Some potential causes are as follows: 6 | /// 7 | /// 1. The Firebase Realtime Database Rules no longer allow a read at the 8 | /// requested location. The [reason] description for this cause is "Permission 9 | /// denied." 10 | /// 2. A write triggered an event streamer that sent a large JSON tree that 11 | /// exceeds our limit, 512MB. The [reason] for this cause is "The specified 12 | /// payload is too large, please request a location with less data." 13 | class RemoteCancellation implements Exception { 14 | /// The reason why the stream has been canceled. 15 | final String reason; 16 | 17 | /// Default constructor. 18 | RemoteCancellation(this.reason); 19 | 20 | @override 21 | String toString() => 22 | 'The connection was cancelled by the server. Reason: $reason'; 23 | } 24 | -------------------------------------------------------------------------------- /demo/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /demo/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /demo/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /demo/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_data_demo 2 | description: Demo app for all the flutter_data extensions 3 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 4 | 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: ">=2.16.0 <3.0.0" 9 | 10 | dependencies: 11 | dotenv: ^4.0.1 12 | flutter: 13 | sdk: flutter 14 | flutter_data: ^1.3.4 15 | flutter_data_firebase_database: ^1.0.1 16 | flutter_data_sodium: ^1.0.0 17 | flutter_riverpod: ^1.0.3 18 | freezed_annotation: ^1.1.0 19 | json_annotation: ^4.5.0 20 | oauth2: ^2.0.0 21 | path_provider: ^2.0.9 22 | sodium_libs: ^1.2.2 23 | url_launcher: ^6.1.0 24 | 25 | dev_dependencies: 26 | flutter_test: 27 | sdk: flutter 28 | flutter_lints: ^1.0.0 29 | build_runner: ^2.1.10 30 | json_serializable: ^6.2.0 31 | freezed: ^1.1.1 32 | 33 | dependency_overrides: 34 | flutter_data_firebase_database: 35 | path: ../packages/flutter_data_firebase_database 36 | flutter_data_sodium: 37 | path: ../packages/flutter_data_sodium 38 | 39 | flutter: 40 | uses-material-design: true 41 | assets: 42 | - .env 43 | -------------------------------------------------------------------------------- /demo/lib/src/setup/providers.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | import 'package:flutter_data_demo/src/setup/defines.dart'; 5 | import 'package:sodium_libs/sodium_libs.dart'; 6 | 7 | import '../../main.data.dart'; 8 | 9 | final localIdProvider = StateProvider((ref) => ''); 10 | 11 | final sodiumInitProvider = FutureProvider( 12 | (ref) => SodiumInit.init(), 13 | ); 14 | final sodiumProvider = Provider( 15 | (ref) => ref.watch(sodiumInitProvider).value!, 16 | ); 17 | 18 | final localEncryptionKeyProvider = Provider((ref) { 19 | ref.onDispose(() => ref.state.dispose()); 20 | var sodium = ref.watch(sodiumProvider); 21 | return sodium.secureCopy( 22 | Uint8List.fromList( 23 | List.generate( 24 | sodium.crypto.secretBox.keyBytes, 25 | (index) => index, 26 | ), 27 | ), 28 | ); 29 | }); 30 | 31 | final initAllProvider = FutureProvider((ref) async { 32 | await ref.watch(definesInitProvider.future); 33 | await ref.watch(sodiumInitProvider.future); 34 | await ref.watch(repositoryInitializerProvider().future); 35 | }); 36 | -------------------------------------------------------------------------------- /demo/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_data_demo", 3 | "short_name": "flutter_data_demo", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "Demo app for all the flutter_data extensions", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /demo/lib/src/setup/defines.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart'; 4 | // ignore: implementation_imports 5 | import 'package:dotenv/src/parser.dart'; 6 | import 'package:flutter_data/flutter_data.dart'; 7 | 8 | late final definesInitProvider = FutureProvider( 9 | (ref) => Defines.loadFromBundle(), 10 | ); 11 | late final definesProvider = Provider( 12 | (ref) => ref.watch(definesInitProvider).value!, 13 | ); 14 | 15 | class Defines { 16 | final Map _dotenvData; 17 | 18 | Defines(this._dotenvData); 19 | 20 | static Future loadFromBundle([AssetBundle? assetBundle]) async => 21 | (assetBundle ?? rootBundle).loadStructuredData( 22 | '.env', 23 | (value) async => Defines( 24 | const Parser().parse( 25 | const LineSplitter().convert(value), 26 | ), 27 | ), 28 | ); 29 | 30 | String get apiKey => _dotenvData['FIREBASE_API_KEY']!; 31 | 32 | String get googleClientId => _dotenvData['GOOGLE_CLIENT_ID']!; 33 | 34 | String get googleClientSecret => _dotenvData['GOOGLE_CLIENT_SECRET']!; 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Felix Barz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/setup/sodium_setup_js.dart: -------------------------------------------------------------------------------- 1 | @JS() 2 | library sodium_setup_js; 3 | 4 | import 'dart:async'; 5 | import 'dart:html'; 6 | 7 | import 'package:js/js.dart'; 8 | import 'package:js/js_util.dart'; 9 | import 'package:meta/meta.dart'; 10 | import 'package:sodium/sodium.dart' show Sodium; 11 | import 'package:sodium/sodium.js.dart'; 12 | 13 | import '../binaries/web/sodium.js.dart'; 14 | 15 | @JS() 16 | @anonymous 17 | class SodiumBrowserInit { 18 | external void Function(LibSodiumJS sodium) get onload; 19 | 20 | external factory SodiumBrowserInit({ 21 | void Function(LibSodiumJS sodium) onload, 22 | }); 23 | } 24 | 25 | mixin SodiumSetup { 26 | @protected 27 | Future loadSodium() async { 28 | final completer = Completer(); 29 | 30 | setProperty( 31 | window, 32 | 'sodium', 33 | SodiumBrowserInit( 34 | onload: allowInterop(completer.complete), 35 | ), 36 | ); 37 | 38 | final script = ScriptElement()..text = sodiumJsSrc; 39 | document.head!.append(script); 40 | 41 | return SodiumInit.init(await completer.future); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Felix Barz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Felix Barz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Felix Barz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/di_setup.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:universal_io/io.dart'; 4 | 5 | import '../repositories/test.data.dart'; 6 | import 'setup.dart'; 7 | 8 | const _kIsWeb = identical(0, 0.0); 9 | 10 | mixin DiSetup on Setup { 11 | late final Directory? _testDir; 12 | 13 | late final ProviderContainer di; 14 | 15 | @override 16 | @mustCallSuper 17 | Future setUpAll() async { 18 | await super.setUpAll(); 19 | 20 | _testDir = _kIsWeb ? null : await Directory.systemTemp.createTemp(); 21 | di = ProviderContainer( 22 | overrides: [ 23 | configureRepositoryLocalStorage( 24 | baseDirFn: () => _testDir?.path ?? '', 25 | clear: true, 26 | ), 27 | ], 28 | ); 29 | } 30 | 31 | @override 32 | @mustCallSuper 33 | Future tearDownAll() async { 34 | try { 35 | await di.read(hiveLocalStorageProvider).hive.close(); 36 | di.dispose(); 37 | await di.pump(); 38 | 39 | await _testDir?.delete(recursive: true); 40 | } finally { 41 | await super.tearDownAll(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/config_setup.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_test_tools/dart_test_tools.dart'; 2 | import 'package:flutter_data/flutter_data.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | import 'di_setup.dart'; 6 | 7 | mixin ConfigSetup on DiSetup { 8 | static late final databaseHostProvider = StateProvider( 9 | (ref) => 10 | // ignore: lines_longer_than_80_chars 11 | 'flutter-data-extensions-default-rtdb.europe-west1.firebasedatabase.app', 12 | ); 13 | 14 | static late final apiKeyProvider = StateProvider((ref) => ''); 15 | 16 | @override 17 | @mustCallSuper 18 | Future setUpAll() async { 19 | await super.setUpAll(); 20 | 21 | final env = await TestEnv.load(); 22 | 23 | // read api key 24 | final apiKey = env['FIREBASE_API_KEY']; 25 | if (apiKey == null) { 26 | throw ArgumentError( 27 | 'environment or dart variable must be set', 28 | 'FIREBASE_API_KEY', 29 | ); 30 | } 31 | di.read(apiKeyProvider.notifier).state = apiKey; 32 | 33 | // read host 34 | final databaseHost = env['FIREBASE_DATABASE_HOST']; 35 | if (databaseHost != null) { 36 | di.read(databaseHostProvider.notifier).state = databaseHost; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/setup/test_key_manager.dart: -------------------------------------------------------------------------------- 1 | // ignore: test_library_import 2 | import 'dart:typed_data'; 3 | 4 | import 'package:clock/clock.dart'; 5 | // ignore: test_library_import 6 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 7 | import 'package:sodium/sodium.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | class _TestKeyManager extends KeyManager { 11 | final SecureKey masterKey; 12 | 13 | _TestKeyManager({ 14 | required Sodium sodium, 15 | required this.masterKey, 16 | Clock? clock, 17 | }) : super( 18 | sodium: sodium, 19 | clock: clock, 20 | ); 21 | 22 | @override 23 | Future loadRemoteMasterKey(int keyLength) { 24 | expect(keyLength, masterKey.length); 25 | return Future.value(masterKey); 26 | } 27 | } 28 | 29 | mixin KeyManagerSetup { 30 | Future loadKeyManager( 31 | Sodium sodium, 32 | Uint8List masterKeyBytes, [ 33 | Clock? clock, 34 | ]) async { 35 | final keyManager = _TestKeyManager( 36 | sodium: sodium, 37 | masterKey: SecureKey.fromList(sodium, masterKeyBytes), 38 | clock: clock, 39 | ); 40 | await keyManager.initialize(); 41 | addTearDown(keyManager.dispose); 42 | return keyManager; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/database_setup.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:http/http.dart' as http; 3 | import 'package:meta/meta.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | import 'account_setup.dart'; 7 | import 'config_setup.dart'; 8 | 9 | mixin DatabaseSetup on AccountSetup { 10 | static late final databasePathProvider = StateProvider( 11 | (ref) => '', 12 | ); 13 | 14 | @override 15 | @mustCallSuper 16 | Future setUpAll() async { 17 | await super.setUpAll(); 18 | 19 | final account = di.read(AccountSetup.accountProvider); 20 | di.read(databasePathProvider.notifier).state = '/${account.localId}'; 21 | } 22 | 23 | @override 24 | @mustCallSuper 25 | Future tearDownAll() async { 26 | try { 27 | final account = di.read(AccountSetup.accountProvider); 28 | final response = await http.delete( 29 | Uri.https( 30 | di.read(ConfigSetup.databaseHostProvider), 31 | '/${account.localId}.json', 32 | { 33 | 'auth': account.idToken, 34 | }, 35 | ), 36 | ); 37 | 38 | printOnFailure(response.body); 39 | expect(response.statusCode, 200); 40 | } finally { 41 | await super.tearDownAll(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/setup/http_setup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:collection'; 3 | import 'dart:convert'; 4 | 5 | import 'package:flutter_data/flutter_data.dart'; 6 | import 'package:http/http.dart'; 7 | import 'package:http/testing.dart'; 8 | import 'package:test/test.dart'; 9 | import 'package:universal_io/io.dart'; 10 | 11 | typedef HttpHandler = FutureOr Function(Request request); 12 | 13 | Response jsonResponse( 14 | Object? jsonData, { 15 | int statusCode = HttpStatus.ok, 16 | Map headers = const {}, 17 | }) => 18 | Response( 19 | json.encode(jsonData), 20 | statusCode, 21 | ); 22 | 23 | extension JsonRequestX on Request { 24 | Object? get jsonBody => body.isNotEmpty ? json.decode(body) : null; 25 | } 26 | 27 | mixin HttpSetup { 28 | final _httpHandlers = Queue(); 29 | 30 | void prepareHandler(HttpHandler handler) { 31 | _httpHandlers.add(handler); 32 | } 33 | 34 | Override createHttpOverride() { 35 | addTearDown( 36 | () => expect(_httpHandlers, isEmpty), 37 | ); 38 | 39 | return httpClientProvider.overrideWithValue( 40 | MockClient( 41 | (request) async { 42 | expect(_httpHandlers, isNotEmpty); 43 | return _httpHandlers.removeFirst()(request); 44 | }, 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/repositories/values_repository.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_mixin 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | // ignore: test_library_import 5 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 6 | import 'package:json_annotation/json_annotation.dart'; 7 | 8 | import 'test_adapter.dart'; 9 | 10 | part 'values_repository.g.dart'; 11 | 12 | @JsonSerializable() 13 | @DataRepository([FirebaseDatabaseAdapter, TestAdapter]) 14 | class ValuesModel with DataModel { 15 | @override 16 | final String? id; 17 | final ServerTimestamp? serverTimestamp; 18 | final ServerIncrementable? serverIncrementable; 19 | 20 | ValuesModel({ 21 | this.id, 22 | this.serverTimestamp, 23 | this.serverIncrementable, 24 | }); 25 | 26 | @override 27 | bool operator ==(Object other) { 28 | if (other is! ValuesModel) { 29 | return false; 30 | } 31 | 32 | return id == other.id && 33 | serverTimestamp == other.serverTimestamp && 34 | serverIncrementable == other.serverIncrementable; 35 | } 36 | 37 | @override 38 | int get hashCode => 39 | id.hashCode ^ serverTimestamp.hashCode ^ serverIncrementable.hashCode; 40 | 41 | @override 42 | String toString() => 'TestModel($id, $serverTimestamp, $serverIncrementable)'; 43 | } 44 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/example/bin/flutter_data_firebase_database_example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | import 'package:flutter_data_firebase_database_example/main.data.dart'; 5 | import 'package:flutter_data_firebase_database_example/src/example_model.dart'; 6 | import 'package:flutter_data_firebase_database_example/src/example_remote_adapter.dart'; 7 | 8 | Future main(List arguments) async { 9 | final ref = ProviderContainer( 10 | overrides: [ 11 | configureRepositoryLocalStorage( 12 | baseDirFn: () => 13 | Directory.systemTemp.createTemp().then((dir) => dir.path), 14 | ), 15 | ], 16 | ); 17 | 18 | ref.read(baseUrlProvider.notifier).state = arguments[0]; 19 | ref.read(idTokenProvider.notifier).state = 20 | arguments.length >= 2 ? arguments[1] : null; 21 | 22 | await ref.read(repositoryInitializerProvider().future); 23 | 24 | final repository = ref.read(exampleModelsRepositoryProvider); 25 | 26 | print('Find all:'); 27 | print(await repository.findAll(syncLocal: true)); 28 | 29 | print('Find transaction:'); 30 | print(await repository.firebaseDatabaseAdapter.transaction( 31 | 'test', 32 | (id, data) => ExampleModel(id: id, title: 'new example'), 33 | )); 34 | 35 | print('Stream all:'); 36 | repository.firebaseDatabaseAdapter.streamAll().listen(print); 37 | } 38 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/README.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | TODO: Put a short description of the package here that helps potential users 15 | know whether this package might be useful for them. 16 | 17 | ## Features 18 | 19 | TODO: List what your package can do. Maybe include images, gifs, or videos. 20 | 21 | ## Getting started 22 | 23 | TODO: List prerequisites and provide or point to information on how to 24 | start using the package. 25 | 26 | ## Usage 27 | 28 | TODO: Include short and useful examples for package users. Add longer examples 29 | to `/example` folder. 30 | 31 | ```dart 32 | const like = 'sample'; 33 | ``` 34 | 35 | ## Additional information 36 | 37 | TODO: Tell users more about the package: where to find more information, how to 38 | contribute to the package, how to file issues, what response they can expect 39 | from the package authors, and more. 40 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/database_event.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:convert'; 4 | 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'database_event.freezed.dart'; 8 | part 'database_event.g.dart'; 9 | 10 | @internal 11 | @freezed 12 | class DatabaseEventData with _$DatabaseEventData { 13 | const factory DatabaseEventData({ 14 | required String path, 15 | required Object? data, 16 | }) = _DatabaseEventData; 17 | 18 | factory DatabaseEventData.fromJson(Map json) => 19 | _$DatabaseEventDataFromJson(json); 20 | 21 | factory DatabaseEventData.fromRawJson(String rawJson) => 22 | DatabaseEventData.fromJson( 23 | json.decode(rawJson) as Map, 24 | ); 25 | } 26 | 27 | @internal 28 | @freezed 29 | class DatabaseEvent with _$DatabaseEvent { 30 | static const putEvent = 'put'; 31 | static const patchEvent = 'patch'; 32 | static const keepAliveEvent = 'keep-alive'; 33 | static const cancelEvent = 'cancel'; 34 | static const authRevokedEvent = 'auth_revoked'; 35 | 36 | const factory DatabaseEvent.put(DatabaseEventData data) = _Put; 37 | const factory DatabaseEvent.patch(DatabaseEventData data) = _Patch; 38 | const factory DatabaseEvent.keepAlive() = _KeepAlive; 39 | const factory DatabaseEvent.cancel(String reason) = _Cancel; 40 | const factory DatabaseEvent.authRevoked() = _AuthRevoked; 41 | } 42 | -------------------------------------------------------------------------------- /demo/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.CreateAndShow(L"flutter_data_demo", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /demo/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.11' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/serialization/firebase_value_transformer.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:convert'; 4 | 5 | import 'package:meta/meta.dart'; 6 | 7 | @internal 8 | abstract class FirebaseValueTransformer { 9 | FirebaseValueTransformer._(); 10 | 11 | static Object? transformAll(Object? rawData) { 12 | if (rawData is Map) { 13 | return [ 14 | for (final entry in rawData.entries) 15 | if (entry.value is Map) 16 | {...entry.value, 'id': entry.key} 17 | else if (entry.value != null) 18 | entry.value, 19 | ]; 20 | } else { 21 | return rawData; 22 | } 23 | } 24 | 25 | static Object? transformOne(Object? rawData, String? id) { 26 | if (rawData is Map) { 27 | return { 28 | ...rawData, 29 | 'id': id, 30 | }; 31 | } else { 32 | return rawData; 33 | } 34 | } 35 | 36 | static Object? transformCreated(Object? rawData, String requestBody) { 37 | if (rawData is Map && 38 | rawData.keys.length == 1 && 39 | rawData.keys.contains('name')) { 40 | final jsonRequest = json.decode(requestBody) as Map; 41 | return { 42 | ...jsonRequest, 43 | 'id': rawData['name'], 44 | }; 45 | } else { 46 | return rawData; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /demo/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/tool/download_sodium_windows.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:http/http.dart' as http; 4 | 5 | const _sodiumVersion = '1.0.18'; 6 | const _vsVersion = 'v142'; 7 | 8 | Future main() async { 9 | final baseUri = Uri.https( 10 | 'download.libsodium.org', 11 | '/libsodium/releases/libsodium-$_sodiumVersion-stable-msvc.zip', 12 | ); 13 | 14 | final tmpDir = await Directory.systemTemp.createTemp(); 15 | try { 16 | final data = await http.get(baseUri); 17 | final zipFile = File.fromUri(tmpDir.uri.resolve('libsodium.zip')); 18 | await zipFile.writeAsBytes(data.bodyBytes); 19 | final proc7z = await Process.start( 20 | '7z', 21 | ['x', '-y', '-o${tmpDir.path}', zipFile.path], 22 | mode: ProcessStartMode.inheritStdio, 23 | ); 24 | final exitCode = await proc7z.exitCode; 25 | if (exitCode != 0) { 26 | throw Exception('7z failed with exit code $exitCode'); 27 | } 28 | 29 | final libsodiumDll = File.fromUri( 30 | tmpDir.uri 31 | .resolve('libsodium/x64/Release/$_vsVersion/dynamic/libsodium.dll'), 32 | ); 33 | if (!await libsodiumDll.exists()) { 34 | throw Exception('$libsodiumDll does not exist'); 35 | } 36 | 37 | final winTestDir = Directory('test/integration/binaries/win'); 38 | await winTestDir.create(recursive: true); 39 | await libsodiumDll.copy( 40 | winTestDir.uri.resolve('libsodium.dll').toFilePath(), 41 | ); 42 | } finally { 43 | await tmpDir.delete(recursive: true); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/setup/adapter_setup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | // ignore: test_library_import 5 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 6 | import 'package:sodium/sodium.dart'; 7 | import 'package:test/test.dart'; 8 | import 'package:universal_io/io.dart'; 9 | 10 | import '../repositories/test.data.dart'; 11 | import '../repositories/test_adapter.dart'; 12 | import '../repositories/test_repository.dart'; 13 | 14 | mixin AdapterSetup { 15 | late Repository testDataRepository; 16 | 17 | bool get keepDataOnce; 18 | 19 | Override createLocalStorageOverride( 20 | Directory? testDir, 21 | Sodium sodium, 22 | Uint8List keyBytes, 23 | ) => 24 | configureRepositoryLocalStorageSodium( 25 | sodium: (ref) => ref.watch(TestAdapter.sodiumProvider), 26 | encryptionKey: (ref) => SecureKey.fromList( 27 | ref.read(TestAdapter.sodiumProvider), 28 | keyBytes, 29 | ), 30 | baseDirFn: () => testDir?.path ?? '', 31 | ); 32 | 33 | Future initRepositories(ProviderContainer providerContainer) async { 34 | await providerContainer 35 | .read(repositoryInitializerProvider(verbose: true).future); 36 | 37 | testDataRepository = providerContainer.read(testModelsRepositoryProvider); 38 | addTearDown(() async { 39 | if (!keepDataOnce) { 40 | await testDataRepository.clear(); 41 | } 42 | testDataRepository.dispose(); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/transactions/transaction_rejected.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | 3 | /// A base class for [Exception]s that can be thrown when committing a 4 | /// transaction. 5 | abstract class TransactionException implements Exception { 6 | /// The id of the dataset for which the transaction failed. 7 | final String id; 8 | 9 | /// The error message of this exception. 10 | final String message; 11 | 12 | TransactionException._(this.id, this.message); 13 | 14 | @override 15 | String toString() => message; 16 | } 17 | 18 | /// An [Exception] thrown if a transaction failed to commit because the data has 19 | /// been changed on the server since the beginning of the transaction and was 20 | /// thus rejected. 21 | class TransactionRejected extends TransactionException { 22 | /// Default Constructor. 23 | TransactionRejected(String id) 24 | : super._( 25 | id, 26 | 'Transaction on data with id "$id" was rejected by the server.', 27 | ); 28 | } 29 | 30 | /// An [Exception] thrown if a transaction failed to commit because the data 31 | /// returned by the transaction function is invalid. This can for example 32 | /// happen, if the id of the returned data does not match the transaction id. 33 | class TransactionInvalid extends TransactionException { 34 | /// Creates an [TransactionInvalid] exception for transaction with invalid id. 35 | TransactionInvalid.invalidId(String id) 36 | : super._( 37 | id, 38 | 'The updated data must have the same id as the transaction ($id).', 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/unit/util/sodium_uuid_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter_data_sodium/src/util/sodium_uuid.dart'; 4 | import 'package:mocktail/mocktail.dart'; 5 | import 'package:sodium/sodium.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | class MockSodium extends Mock implements Sodium {} 9 | 10 | class MockRandombytes extends Mock implements Randombytes {} 11 | 12 | void main() { 13 | group('SodiumUuid', () { 14 | final mockSodium = MockSodium(); 15 | final mockRandombytes = MockRandombytes(); 16 | 17 | setUp(() { 18 | reset(mockSodium); 19 | reset(mockRandombytes); 20 | 21 | when(() => mockSodium.randombytes).thenReturn(mockRandombytes); 22 | }); 23 | 24 | test('grng returns function that generates random 16 byte blocks', () { 25 | final testData = Uint8List.fromList(List.generate(10, (index) => index)); 26 | 27 | when(() => mockRandombytes.buf(any())).thenReturn(testData); 28 | 29 | final grng = SodiumUuid.grng(mockRandombytes); 30 | 31 | expect(grng(), testData); 32 | 33 | verify(() => mockRandombytes.buf(16)); 34 | }); 35 | 36 | test('uuid creates a Uuid instance with randombytes randomness', () { 37 | when(() => mockRandombytes.buf(any())).thenAnswer( 38 | (i) => Uint8List.fromList( 39 | List.filled(i.positionalArguments[0] as int, 42), 40 | ), 41 | ); 42 | 43 | final id = mockSodium.uuid.v4(); 44 | 45 | expect(id, '2a2a2a2a-2a2a-4a2a-aa2a-2a2a2a2a2a2a'); 46 | 47 | verify(() => mockRandombytes.buf(16)); 48 | }); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /demo/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | analyzer: 13 | exclude: 14 | - lib/generated_plugin_registrant.dart 15 | 16 | linter: 17 | # The lint rules applied to this project can be customized in the 18 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 19 | # included above or to enable additional rules. A list of all available lints 20 | # and their documentation is published at 21 | # https://dart-lang.github.io/linter/lints/index.html. 22 | # 23 | # Instead of disabling a lint rule for the entire project in the 24 | # section below, it can also be suppressed for a single line of code 25 | # or a specific dart file by using the `// ignore: name_of_lint` and 26 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 27 | # producing the lint. 28 | rules: 29 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 30 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 31 | # Additional information about this file can be found at 32 | # https://dart.dev/guides/language/analysis-options 33 | -------------------------------------------------------------------------------- /demo/lib/src/password/password_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'password_controller.freezed.dart'; 5 | 6 | late final passwordControllerProvider = 7 | StateNotifierProvider( 8 | (ref) => PasswordController(), 9 | ); 10 | 11 | @freezed 12 | class PasswordState with _$PasswordState { 13 | const factory PasswordState.unset() = _Unset; 14 | const factory PasswordState.requested() = _Requested; 15 | const factory PasswordState.defined(String password) = _Defined; 16 | const factory PasswordState.consumed() = _Consumed; 17 | } 18 | 19 | class PasswordController extends StateNotifier { 20 | PasswordController() : super(const PasswordState.unset()); 21 | 22 | Future requestPassword() async { 23 | final isUnset = state.maybeWhen( 24 | unset: () => true, 25 | orElse: () => false, 26 | ); 27 | if (!isUnset) { 28 | throw StateError('Can only request password once'); 29 | } 30 | 31 | state = const PasswordState.requested(); 32 | final password = await stream 33 | .map( 34 | (state) => state.whenOrNull( 35 | defined: (password) => password, 36 | ), 37 | ) 38 | .where((state) => state != null) 39 | .cast() 40 | .first; 41 | state = const PasswordState.consumed(); 42 | return password; 43 | } 44 | 45 | void setPassword(String password) => state.maybeWhen( 46 | requested: () => state = PasswordState.defined(password), 47 | orElse: () {}, 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/stream_one_controller.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | import 'package:meta/meta.dart'; 5 | 6 | import '../firebase_database_adapter.dart'; 7 | import '../serialization/firebase_value_transformer.dart'; 8 | import 'database_event.dart'; 9 | import 'stream_controller_base.dart'; 10 | 11 | @internal 12 | class StreamOneController> 13 | extends StreamControllerBase { 14 | final String id; 15 | 16 | StreamOneController({ 17 | required this.id, 18 | required Future> Function() createStream, 19 | required FirebaseDatabaseAdapter adapter, 20 | bool autoRenew = true, 21 | UnsupportedEventCb? onUnsupportedEvent, 22 | }) : super( 23 | createStream: createStream, 24 | adapter: adapter, 25 | autoRenew: autoRenew, 26 | onUnsupportedEvent: onUnsupportedEvent, 27 | ); 28 | 29 | @override 30 | Future put(DatabaseEventData data) async { 31 | if (data.path != StreamControllerBase.rootPath) { 32 | onUnsupportedEvent?.call('put', data.path); 33 | return; 34 | } 35 | 36 | if (data.data != null) { 37 | final deserialized = adapter.deserialize( 38 | FirebaseValueTransformer.transformOne(data.data, id), 39 | ); 40 | sink.add(deserialized.model); 41 | } else { 42 | await adapter.delete(id, remote: false); 43 | sink.add(null); 44 | } 45 | } 46 | 47 | @override 48 | void patch(DatabaseEventData data) => 49 | onUnsupportedEvent?.call('patch', data.path); 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/flutter_data_firebase_database.yaml: -------------------------------------------------------------------------------- 1 | name: CI/CD for flutter_data_firebase_database 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "*" 8 | paths: 9 | - "packages/flutter_data_firebase_database/**" 10 | - ".github/workflows/flutter_data_firebase_database.yaml" 11 | pull_request: 12 | branches: 13 | - "*" 14 | paths: 15 | - "packages/flutter_data_firebase_database/**" 16 | - ".github/workflows/flutter_data_firebase_database.yaml" 17 | 18 | jobs: 19 | ci: 20 | name: CI 21 | uses: Skycoder42/dart_test_tools/.github/workflows/dart.yml@main 22 | with: 23 | workingDirectory: packages/flutter_data_firebase_database 24 | buildRunner: true 25 | publishExclude: >- 26 | [ 27 | "lib/src/.gitignore", 28 | "example/lib/.gitignore" 29 | ] 30 | unitTestPaths: test/unit 31 | coverageExclude: >- 32 | "**/*.freezed.dart" 33 | "**/*.g.dart" 34 | integrationTestPaths: test/integration 35 | secrets: 36 | integrationTestEnvVars: | 37 | FIREBASE_API_KEY=${{ secrets.FIREBASE_API_KEY }} 38 | 39 | cd: 40 | name: CD 41 | uses: Skycoder42/dart_test_tools/.github/workflows/publish.yml@main 42 | needs: 43 | - ci 44 | with: 45 | workingDirectory: packages/flutter_data_firebase_database 46 | buildRunner: true 47 | publishExclude: >- 48 | [ 49 | "lib/src/.gitignore", 50 | "example/lib/.gitignore" 51 | ] 52 | tagPrefix: flutter_data_firebase_database/v 53 | secrets: 54 | PUB_DEV_CREDENTIALS: ${{ secrets.PUB_DEV_CREDENTIALS }} 55 | -------------------------------------------------------------------------------- /demo/lib/src/setup/application_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_data/flutter_data.dart'; 3 | import 'package:flutter_data_demo/src/auth/firebase_auth.dart'; 4 | import 'package:flutter_data_demo/src/auth/google_auth.dart'; 5 | import 'package:flutter_data_demo/src/setup/providers.dart'; 6 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 7 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 8 | import 'package:sodium_libs/sodium_libs.dart'; 9 | 10 | import '../password/application_key_manager.dart'; 11 | 12 | mixin ApplicationAdapter> 13 | on RemoteAdapter, FirebaseDatabaseAdapter, SodiumRemoteAdapter { 14 | @override 15 | String get baseUrl => 16 | 'https://flutter-data-extensions-default-rtdb.europe-west1.firebasedatabase.app/${read(localIdProvider)}/demo'; 17 | 18 | @override 19 | late final String idToken; 20 | 21 | @override 22 | Sodium get sodium => read(sodiumProvider); 23 | 24 | @override 25 | KeyManager get keyManager => read(applicationKeyManagerProvider); 26 | 27 | @override 28 | @mustCallSuper 29 | Future onInitialized() async { 30 | await super.onInitialized(); 31 | await _authenticate(); 32 | await keyManager.initialize(); 33 | } 34 | 35 | Future _authenticate() async { 36 | final googleCredentials = await read(googleAuthProvider).authorize(); 37 | final firebaseAccount = 38 | await read(firebaseAuthProvider).loginWithGoogle(googleCredentials); 39 | 40 | read(localIdProvider.notifier).state = firebaseAccount.localId; 41 | idToken = firebaseAccount.idToken; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/repositories/test.data.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: directives_ordering, top_level_function_literal_block 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | 5 | import 'test_repository.dart'; 6 | 7 | // ignore: prefer_function_declarations_over_variables 8 | RepositoryInitializerProvider repositoryInitializerProvider = 9 | ({bool? remote, bool? verbose}) => _repositoryInitializerProviderFamily( 10 | RepositoryInitializerArgs(remote, verbose), 11 | ); 12 | 13 | final repositoryProviders = >>{ 14 | 'testModels': testModelsRepositoryProvider, 15 | }; 16 | 17 | final _repositoryInitializerProviderFamily = 18 | FutureProvider.family( 19 | (ref, args) async { 20 | final adapters = { 21 | 'testModels': ref.watch(testModelsRemoteAdapterProvider), 22 | }; 23 | final remotes = { 24 | 'testModels': true, 25 | }; 26 | 27 | await ref.watch(graphNotifierProvider).initialize(); 28 | 29 | final _repoMap = { 30 | for (final type in repositoryProviders.keys) 31 | type: ref.watch(repositoryProviders[type]!) 32 | }; 33 | 34 | for (final type in _repoMap.keys) { 35 | final repository = _repoMap[type]!..dispose(); 36 | await repository.initialize( 37 | remote: args.remote ?? remotes[type], 38 | verbose: args.verbose, 39 | adapters: adapters, 40 | ); 41 | } 42 | 43 | ref.onDispose(() { 44 | for (final repository in _repoMap.values) { 45 | repository.dispose(); 46 | } 47 | }); 48 | 49 | return RepositoryInitializer(); 50 | }); 51 | -------------------------------------------------------------------------------- /.github/workflows/flutter_data_sodium.yaml: -------------------------------------------------------------------------------- 1 | name: CI/CD for flutter_data_sodium 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "*" 8 | paths: 9 | - "packages/flutter_data_sodium/**" 10 | - ".github/workflows/flutter_data_sodium.yaml" 11 | pull_request: 12 | branches: 13 | - "*" 14 | paths: 15 | - "packages/flutter_data_sodium/**" 16 | - ".github/workflows/flutter_data_sodium.yaml" 17 | 18 | jobs: 19 | ci: 20 | name: CI 21 | uses: Skycoder42/dart_test_tools/.github/workflows/dart.yml@main 22 | with: 23 | workingDirectory: packages/flutter_data_sodium 24 | buildRunner: true 25 | publishExclude: >- 26 | [ 27 | "lib/src/.gitignore" 28 | ] 29 | unitTestPaths: test/unit 30 | coverageExclude: >- 31 | "**/*.freezed.dart" 32 | "**/*.g.dart" 33 | integrationTestPaths: test/integration 34 | integrationTestSetup: >- 35 | { 36 | "linux": "sudo apt-get -qq install libsodium-dev", 37 | "windows": "dart run tool/download_sodium_windows.dart", 38 | "macos": "brew install libsodium", 39 | "web": "dart run tool/download_sodium_web.dart" 40 | } 41 | 42 | cd: 43 | name: CD 44 | uses: Skycoder42/dart_test_tools/.github/workflows/publish.yml@main 45 | needs: 46 | - ci 47 | with: 48 | workingDirectory: packages/flutter_data_sodium 49 | buildRunner: true 50 | publishExclude: >- 51 | [ 52 | "lib/src/.gitignore" 53 | ] 54 | tagPrefix: flutter_data_sodium/v 55 | secrets: 56 | PUB_DEV_CREDENTIALS: ${{ secrets.PUB_DEV_CREDENTIALS }} 57 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/setup/account_setup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | import 'package:http/http.dart' as http; 5 | import 'package:meta/meta.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'config_setup.dart'; 9 | import 'models/account.dart'; 10 | 11 | mixin AccountSetup on ConfigSetup { 12 | static late final accountProvider = StateProvider( 13 | (ref) => const Account(idToken: '', localId: ''), 14 | ); 15 | 16 | @override 17 | @mustCallSuper 18 | Future setUpAll() async { 19 | await super.setUpAll(); 20 | 21 | final accountResponse = await http.post(_createUri('signUp')); 22 | printOnFailure(accountResponse.body); 23 | expect(accountResponse.statusCode, 200); 24 | 25 | di.read(accountProvider.notifier).state = Account.fromJson( 26 | json.decode(accountResponse.body) as Map, 27 | ); 28 | } 29 | 30 | @override 31 | @mustCallSuper 32 | Future tearDownAll() async { 33 | try { 34 | final deleteResponse = await http.post( 35 | _createUri('delete'), 36 | body: json 37 | .encode(DeleteAccountPostModel(di.read(accountProvider).idToken)), 38 | ); 39 | 40 | printOnFailure(deleteResponse.body); 41 | expect(deleteResponse.statusCode, 200); 42 | } finally { 43 | await super.tearDownAll(); 44 | } 45 | } 46 | 47 | Uri _createUri(String method) => Uri.https( 48 | 'identitytoolkit.googleapis.com', 49 | '/v1/accounts:$method', 50 | { 51 | 'key': di.read(ConfigSetup.apiKeyProvider), 52 | }, 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /demo/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /demo/lib/src/ui/task_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_data_demo/src/models/task.dart'; 3 | import 'package:flutter_data_demo/src/ui/task_list.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | 6 | class TaskPage extends ConsumerWidget { 7 | final _refreshKey = GlobalKey( 8 | debugLabel: 'KittenPage.refresh', 9 | ); 10 | 11 | TaskPage({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context, WidgetRef ref) { 15 | return Scaffold( 16 | appBar: AppBar( 17 | title: const Text('Flutter Data Extensions Demo'), 18 | actions: [ 19 | IconButton( 20 | onPressed: () => _refreshKey.currentState?.show(), 21 | icon: const Icon(Icons.refresh), 22 | ), 23 | IconButton( 24 | onPressed: () => _clearAll(ref), 25 | icon: const Icon(Icons.delete_sweep), 26 | ), 27 | ], 28 | ), 29 | floatingActionButton: FloatingActionButton( 30 | onPressed: () => _addNew(ref), 31 | child: const Icon(Icons.add), 32 | ), 33 | body: TaskList( 34 | refreshIndicatorKey: _refreshKey, 35 | ), 36 | ); 37 | } 38 | 39 | Future _clearAll(WidgetRef ref) async { 40 | final tasksRepo = ref.read(tasksRepositoryProvider); 41 | final tasks = await tasksRepo.findAll(remote: false); 42 | for (final t in tasks) { 43 | tasksRepo.delete(t); 44 | } 45 | } 46 | 47 | Future _addNew(WidgetRef ref) { 48 | return ref 49 | .read(tasksRepositoryProvider) 50 | .save(Task(title: 'New Task - ${DateTime.now()}')); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/hive/configure_repository_local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:sodium/sodium.dart'; 3 | 4 | import 'sodium_hive_local_storage.dart'; 5 | 6 | /// A generic function that creates an instance of [T] using a [ref]. 7 | typedef CreateFn = T Function(Ref ref); 8 | 9 | /// Creates an override for [hiveLocalStorageProvider] using sodium encryption. 10 | /// 11 | /// This method does the same as the auto-generated variant in `main.data.dart`, 12 | /// but instead of using the standard (non audited) encryption of hive, [sodium] 13 | /// is used to encrypt data locally. 14 | /// 15 | /// Both [sodium] and the [encryptionKey] are required as this method does not 16 | /// make sense if encryption is not used. The length of the key must be 17 | /// [SodiumHiveCipher.keyBytes]. 18 | /// 19 | /// [baseDirFn] is also required, as unlike the auto-generated variant, this 20 | /// here is not aware of a possibly installed path_provider. If you want to 21 | /// initially clear all opened repositories, you can set [clear] to true. 22 | /// 23 | /// Internally, this method overrides the provider with a 24 | /// [SodiumHiveLocalStorage]. 25 | Override configureRepositoryLocalStorageSodium({ 26 | required CreateFn sodium, 27 | required CreateFn encryptionKey, 28 | required FutureFn baseDirFn, 29 | bool? clear, 30 | }) => 31 | hiveLocalStorageProvider.overrideWithProvider( 32 | Provider( 33 | (ref) => SodiumHiveLocalStorage( 34 | hive: ref.read(hiveProvider), 35 | sodium: sodium(ref), 36 | encryptionKey: encryptionKey(ref), 37 | baseDirFn: baseDirFn, 38 | clear: clear, 39 | ), 40 | ), 41 | ); 42 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/unit/queries/request_config_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_test_tools/test.dart'; 2 | import 'package:flutter_data_firebase_database/src/queries/format_mode.dart'; 3 | import 'package:flutter_data_firebase_database/src/queries/request_config.dart'; 4 | import 'package:flutter_data_firebase_database/src/queries/timeout.dart'; 5 | import 'package:flutter_data_firebase_database/src/queries/write_size_limit.dart'; 6 | import 'package:test/test.dart' hide Timeout; 7 | import 'package:tuple/tuple.dart'; 8 | 9 | void main() { 10 | group('RequestConfig', () { 11 | testData>>( 12 | 'generates correct map representation from config', 13 | const [ 14 | Tuple2(RequestConfig(), {}), 15 | Tuple2(RequestConfig(shallow: false), {'shallow': 'false'}), 16 | Tuple2(RequestConfig(format: FormatMode.export), {'format': 'export'}), 17 | Tuple2(RequestConfig(timeout: Timeout.s(5)), {'timeout': '5s'}), 18 | Tuple2( 19 | RequestConfig(writeSizeLimit: WriteSizeLimit.medium), 20 | {'writeSizeLimit': 'medium'}, 21 | ), 22 | Tuple2( 23 | RequestConfig( 24 | shallow: true, 25 | format: FormatMode.export, 26 | timeout: Timeout.min(10), 27 | writeSizeLimit: WriteSizeLimit.unlimited, 28 | ), 29 | { 30 | 'shallow': 'true', 31 | 'format': 'export', 32 | 'timeout': '10min', 33 | 'writeSizeLimit': 'unlimited', 34 | }, 35 | ), 36 | ], 37 | (fixture) { 38 | expect(fixture.item1 == fixture.item2, isFalse); 39 | expect(fixture.item1, fixture.item2); 40 | }, 41 | ); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /demo/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Flutter Data Demo 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_data_demo 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_data_demo/src/password/password_scope.dart'; 3 | import 'package:flutter_data_demo/src/setup/providers.dart'; 4 | import 'package:flutter_data_demo/src/ui/task_page.dart'; 5 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | import 'package:path_provider/path_provider.dart'; 8 | 9 | Future main() async { 10 | runApp(ProviderScope( 11 | overrides: [ 12 | configureRepositoryLocalStorageSodium( 13 | sodium: (ref) => ref.watch(sodiumProvider), 14 | encryptionKey: (ref) => ref.watch(localEncryptionKeyProvider), 15 | baseDirFn: () => 16 | getApplicationSupportDirectory().then((dir) => dir.path), 17 | ), 18 | ], 19 | child: const App(), 20 | )); 21 | } 22 | 23 | class App extends ConsumerWidget { 24 | const App({Key? key}) : super(key: key); 25 | 26 | @override 27 | Widget build(BuildContext context, WidgetRef ref) { 28 | return MaterialApp( 29 | title: 'Flutter Data Extensions Demo', 30 | theme: ThemeData(primarySwatch: Colors.blue), 31 | home: ref.watch(initAllProvider).when( 32 | data: (_) => TaskPage(), 33 | error: _buildError, 34 | loading: _buildLoading, 35 | ), 36 | ); 37 | } 38 | 39 | Widget _buildLoading() => const Scaffold( 40 | body: PasswordScope( 41 | child: Center( 42 | child: CircularProgressIndicator(), 43 | ), 44 | ), 45 | ); 46 | 47 | Widget _buildError(Object error, StackTrace? stackTrace) => Scaffold( 48 | body: SingleChildScrollView( 49 | child: Text( 50 | '$error\n$stackTrace', 51 | textAlign: TextAlign.center, 52 | ), 53 | ), 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /demo/lib/src/auth/firebase_auth.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter_data_demo/src/auth/firebase_account.dart'; 5 | import 'package:flutter_data_demo/src/auth/google_auth.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | import 'package:oauth2/oauth2.dart'; 8 | import 'package:http/http.dart' as http; 9 | 10 | import '../setup/defines.dart'; 11 | 12 | late final firebaseAuthProvider = Provider( 13 | (ref) => FirebaseAuth( 14 | ref.watch(definesProvider), 15 | ), 16 | ); 17 | 18 | class FirebaseAuth { 19 | final Defines _defines; 20 | 21 | FirebaseAuth(this._defines); 22 | 23 | Future loginWithGoogle(Credentials googleCredentials) async { 24 | final client = http.Client(); 25 | try { 26 | final response = await client.post( 27 | Uri.https( 28 | 'identitytoolkit.googleapis.com', 29 | '/v1/accounts:signInWithIdp', 30 | { 31 | 'key': _defines.apiKey, 32 | }, 33 | ), 34 | headers: { 35 | HttpHeaders.contentTypeHeader: ContentType.json.toString(), 36 | }, 37 | body: json.encode({ 38 | 'requestUri': GoogleAuth.redirectUrl.toString(), 39 | 'postBody': Uri(queryParameters: { 40 | 'id_token': '${googleCredentials.idToken}', 41 | 'providerId': 'google.com', 42 | }).query, 43 | 'returnSecureToken': true, 44 | 'returnIdpCredential': true, 45 | }), 46 | ); 47 | 48 | if (response.statusCode != 200) { 49 | throw Exception( 50 | 'Login failed with status code ${response.statusCode} ' 51 | 'and body: ${response.body}', 52 | ); 53 | } 54 | 55 | return FirebaseAccount.fromJson(json.decode(response.body)); 56 | } finally { 57 | client.close(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /demo/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /demo/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | } 59 | 60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 61 | } 62 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/transaction_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_test_tools/dart_test_tools.dart'; 2 | // ignore: test_library_import 3 | import 'package:flutter_data_firebase_database/flutter_data_firebase_database.dart'; 4 | import 'package:test/test.dart'; 5 | import 'package:tuple/tuple.dart'; 6 | 7 | import 'repositories/test_repository.dart'; 8 | import 'setup/test_repository_setup.dart'; 9 | 10 | void main() { 11 | final setup = TestRepositorySetup()..call(); 12 | 13 | group('transactions', () { 14 | late FirebaseDatabaseAdapter sut; 15 | 16 | setUp(() { 17 | sut = setup.repository.firebaseDatabaseAdapter; 18 | }); 19 | 20 | testData>( 21 | 'correctly runs simple non conflicting transactions', 22 | [ 23 | const Tuple2(null, null), 24 | Tuple2(null, TestModel(id: '_1', name: 'new-data')), 25 | Tuple2( 26 | TestModel(id: '_1', name: 'new-data'), 27 | TestModel(id: '_1', name: 'updated-data'), 28 | ), 29 | Tuple2( 30 | TestModel(id: '_1', name: 'updated-data'), 31 | null, 32 | ), 33 | ], 34 | (fixture) async { 35 | final result = await sut.transaction( 36 | '_1', 37 | (id, data) { 38 | expect(id, '_1'); 39 | expect(data, fixture.item1); 40 | return fixture.item2; 41 | }, 42 | ); 43 | expect(result, fixture.item2); 44 | }, 45 | ); 46 | 47 | test('fails if data is modified between read and commit', () async { 48 | const testId = '_2'; 49 | expect( 50 | () => sut.transaction( 51 | testId, 52 | (id, data) async { 53 | expect(id, testId); 54 | expect(data, isNull); 55 | await sut.save(TestModel(id: testId, name: 'external-modify')); 56 | return TestModel(id: testId, name: 'commit'); 57 | }, 58 | ), 59 | throwsA(isA()), 60 | ); 61 | }); 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/lib/src/sync_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | 5 | import 'retry_state_machine.dart'; 6 | 7 | late final syncControllerProvider = Provider.family( 8 | (ref, Map> repositoryProviders) => 9 | SyncController( 10 | read: ref.read, 11 | pendingOfflineTypesNotifier: 12 | ref.watch(pendingOfflineTypesProvider.notifier), 13 | repositoryProviders: repositoryProviders, 14 | retryStateMachine: ref.watch(retryStateMachineProvider), 15 | ), 16 | ); 17 | 18 | typedef ReadRepositoryFn = Repository Function( 19 | ProviderBase provider, 20 | ); 21 | 22 | class SyncController { 23 | final ReadRepositoryFn read; 24 | final DelayedStateNotifier> pendingOfflineTypesNotifier; 25 | final Map> repositoryProviders; 26 | final RetryStateMachine retryStateMachine; 27 | 28 | late RemoveListener _removeUpdateOfflineOperationsListener; 29 | 30 | SyncController({ 31 | required this.read, 32 | required this.pendingOfflineTypesNotifier, 33 | required this.repositoryProviders, 34 | required this.retryStateMachine, 35 | }) { 36 | _removeUpdateOfflineOperationsListener = 37 | pendingOfflineTypesNotifier.addListener(_updateOfflineOperations); 38 | } 39 | 40 | RemoveErrorListenerCb addErrorListener(OnErrorCb onError) => 41 | retryStateMachine.addErrorListener(onError); 42 | 43 | bool get enabled => retryStateMachine.enabled; 44 | set enabled(bool enabled) => retryStateMachine.enabled = enabled; 45 | 46 | Future dispose() async { 47 | _removeUpdateOfflineOperationsListener(); 48 | await retryStateMachine.close(); 49 | } 50 | 51 | void _updateOfflineOperations(Set types) { 52 | final offlineOperations = types 53 | .map((type) => repositoryProviders[type]) 54 | .whereType>() 55 | .expand((provider) => read(provider).offlineOperations) 56 | .toSet(); 57 | retryStateMachine.updatePendingOfflineOperations(offlineOperations); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/setup/setup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:clock/clock.dart'; 4 | import 'package:flutter_data/flutter_data.dart'; 5 | import 'package:sodium/sodium.dart'; 6 | import 'package:test/test.dart'; 7 | import 'package:universal_io/io.dart'; 8 | 9 | import '../repositories/test_adapter.dart'; 10 | import 'adapter_setup.dart'; 11 | import 'http_setup.dart'; 12 | import 'sodium_setup_vm.dart' if (dart.library.js) 'sodium_setup_js.dart'; 13 | import 'test_key_manager.dart'; 14 | 15 | const _kIsWeb = identical(0, 0.0); 16 | 17 | class Setup with HttpSetup, SodiumSetup, KeyManagerSetup, AdapterSetup { 18 | late final Sodium _sodium; 19 | late Directory? _testDir; 20 | 21 | late ProviderContainer providerContainer; 22 | 23 | @override 24 | bool keepDataOnce = false; 25 | 26 | void call(Uint8List masterKeyBytes, [Clock? clock]) { 27 | setUpAll(() async { 28 | _sodium = await loadSodium(); 29 | }); 30 | 31 | setUp(() async { 32 | if (keepDataOnce) { 33 | // ignore: avoid_print 34 | print('> using data of previous test'); 35 | keepDataOnce = false; 36 | } else { 37 | _testDir = _kIsWeb ? null : await Directory.systemTemp.createTemp(); 38 | } 39 | 40 | final keyManager = await loadKeyManager(_sodium, masterKeyBytes, clock); 41 | 42 | providerContainer = ProviderContainer( 43 | overrides: [ 44 | TestAdapter.sodiumProvider.overrideWithValue(_sodium), 45 | TestAdapter.keyManagerProvider.overrideWithValue(keyManager), 46 | createHttpOverride(), 47 | createLocalStorageOverride(_testDir, _sodium, masterKeyBytes), 48 | ], 49 | ); 50 | 51 | await initRepositories(providerContainer); 52 | }); 53 | 54 | tearDown(() async { 55 | await providerContainer.read(hiveLocalStorageProvider).hive.close(); 56 | providerContainer.dispose(); 57 | 58 | if (keepDataOnce) { 59 | // ignore: avoid_print 60 | print('> keeping data for next test'); 61 | } else { 62 | await _testDir?.delete(recursive: true); 63 | } 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/integration/setup/sodium_setup_vm.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | import 'dart:io'; 3 | 4 | import 'package:meta/meta.dart'; 5 | import 'package:sodium/sodium.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | mixin SodiumSetup { 9 | @protected 10 | Future loadSodium({ 11 | void Function(dynamic, Matcher) expect = expect, 12 | void Function(String) printOnFailure = printOnFailure, 13 | }) async { 14 | String libSodiumPath; 15 | if (Platform.isLinux) { 16 | final ldConfigRes = await Process.run('ldconfig', const ['-p']); 17 | printOnFailure('stderr: ${ldConfigRes.stderr}'); 18 | expect(ldConfigRes.exitCode, equals(0)); 19 | printOnFailure('stdout: ${ldConfigRes.stdout}'); 20 | libSodiumPath = (ldConfigRes.stdout as String) 21 | .split('\n') 22 | .map((e) => e.split('=>').map((e) => e.trim()).toList()) 23 | .where((e) => e.length == 2) 24 | .where((e) => e[0].contains('x86-64')) 25 | .map((e) => MapEntry(e[0].split(' ').first, e[1])) 26 | .where((e) => e.key.contains('libsodium.so')) 27 | .map((e) => e.value) 28 | .first; 29 | } else if (Platform.isWindows) { 30 | libSodiumPath = Directory.current.uri 31 | .resolve('test/integration/binaries/win/libsodium.dll') 32 | .toFilePath(); 33 | } else if (Platform.isMacOS) { 34 | final libDir = Directory('/usr/local/Cellar/libsodium'); 35 | final subDirs = await libDir 36 | .list() 37 | .where((e) => e is Directory) 38 | .cast() 39 | .toList(); 40 | expect(subDirs, isNotEmpty); 41 | subDirs.sort((lhs, rhs) => lhs.path.compareTo(rhs.path)); 42 | libSodiumPath = '${subDirs.last.path}/lib/libsodium.dylib'; 43 | } else { 44 | throw UnsupportedError( 45 | 'Operating system ${Platform.operatingSystem} not supported', 46 | ); 47 | } 48 | 49 | expect(await File(libSodiumPath).exists(), isTrue); 50 | // ignore: avoid_print 51 | print('Found libsodium at: $libSodiumPath'); 52 | final dyLib = DynamicLibrary.open(libSodiumPath); 53 | return SodiumInit.init(dyLib); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/sodium_remote_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:sodium/sodium.dart'; 4 | 5 | import 'encryption/data_cipher.dart'; 6 | import 'encryption/encrypted_data.dart'; 7 | import 'key_management/key_manager.dart'; 8 | 9 | /// A special [RemoteAdapter] that adds an End-To-End-Encryption layer to all 10 | /// your requests. 11 | /// 12 | /// The adapter will use the [keyManager] and [sodium] to encrypt all outgoing 13 | /// request data and to decrypt all incoming request data. The [Aead] algorithm 14 | /// of [Sodium] is used to archive this. Unique keys are transparently generate 15 | /// from a master key and rotated every 30 days to ensure a maximum security. 16 | mixin SodiumRemoteAdapter> on RemoteAdapter { 17 | /// The [Sodium] instance used to perform all cryptographic operations. 18 | Sodium get sodium; 19 | 20 | /// A [KeyManager] that is used to obtain the encryption keys. 21 | KeyManager get keyManager; 22 | 23 | /// @nodoc 24 | @visibleForTesting 25 | late final DataCipher cipher = DataCipher( 26 | sodium: sodium, 27 | keyManager: keyManager, 28 | ); 29 | 30 | @override 31 | Map serialize(T model) => 32 | cipher.encrypt(type, super.serialize(model)).toJson(); 33 | 34 | @override 35 | DeserializedData deserialize(Object? data, {String? key}) { 36 | if (data == null || data == '') { 37 | return super.deserialize(null, key: key); 38 | } else if (data is Iterable) { 39 | return super.deserialize( 40 | data.map( 41 | (dynamic e) => cipher.decrypt(type, _formatCast(e)), 42 | ), 43 | key: key, 44 | ); 45 | } else { 46 | return super.deserialize( 47 | cipher.decrypt(type, _formatCast(data)), 48 | key: key, 49 | ); 50 | } 51 | } 52 | 53 | EncryptedData _formatCast(Object? data) { 54 | if (data is! Map) { 55 | throw FormatException( 56 | 'Invalid JSON-data. Must be an EncryptedData object', 57 | data.toString(), 58 | ); 59 | } 60 | return EncryptedData.fromJson(data); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /demo/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "de.skycoder42.flutter_data_demo" 47 | minSdkVersion flutter.minSdkVersion 48 | targetSdkVersion flutter.targetSdkVersion 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/event_stream/http_database_event_stream.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:async'; 4 | 5 | import 'package:eventsource/eventsource.dart'; 6 | import 'package:http/http.dart'; 7 | import 'package:meta/meta.dart'; 8 | import 'package:rxdart/rxdart.dart'; 9 | 10 | import '../database_event.dart'; 11 | 12 | @internal 13 | class DatabaseEventStream extends Stream { 14 | final Uri uri; 15 | final Map? headers; 16 | final Client? client; 17 | 18 | DatabaseEventStream({ 19 | required this.uri, 20 | this.headers, 21 | this.client, 22 | }); 23 | 24 | @override 25 | StreamSubscription listen( 26 | void Function(DatabaseEvent event)? onData, { 27 | Function? onError, 28 | void Function()? onDone, 29 | bool? cancelOnError, 30 | }) => 31 | Stream.fromFuture(createEventSource()) 32 | .doOnCancel(() => client?.close()) 33 | .asyncExpand((eventSource) => eventSource) 34 | .map(_mapEventToDatabaseEvent) 35 | .listen( 36 | onData, 37 | onError: onError, 38 | onDone: onDone, 39 | cancelOnError: cancelOnError, 40 | ); 41 | 42 | @visibleForTesting 43 | Future> createEventSource() => EventSource.connect( 44 | uri, 45 | headers: headers, 46 | client: client, 47 | ); 48 | 49 | static DatabaseEvent _mapEventToDatabaseEvent(Event event) { 50 | switch (event.event) { 51 | case 'put': 52 | return DatabaseEvent.put( 53 | DatabaseEventData.fromRawJson(event.data!), 54 | ); 55 | case 'patch': 56 | return DatabaseEvent.patch( 57 | DatabaseEventData.fromRawJson(event.data!), 58 | ); 59 | case 'keep-alive': 60 | return const DatabaseEvent.keepAlive(); 61 | case 'cancel': 62 | return DatabaseEvent.cancel(event.data ?? ''); 63 | case 'auth_revoked': 64 | return const DatabaseEvent.authRevoked(); 65 | default: 66 | throw ArgumentError.value( 67 | event, 68 | 'event', 69 | 'Unknown firebase event type', 70 | ); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/doc/retry_state_machine.drawio: -------------------------------------------------------------------------------- 1 | 7Vxbd6M2EP41fowPIG5+jC+77TnpabrpOc0+YqPYtBi5Qk7s/fUVIC5CBmQ7trC3T0GDJEDfzHwzIzkDMFnvvmJvs/oN+TAcGJq/G4DpwDB007Dpn0SyzyTOyMkESxz4rFMpeAl+QCbUmHQb+DDmOhKEQhJseOECRRFcEE7mYYw++G5vKOSfuvGWUBC8LLxQlP4V+GSVSV3DKeW/wGC5yp+s26PsztrLO7MviVeejz4qIjAbgAlGiGRX690Ehsni5euSjfvScLd4MQwjIjNg9+/4Zf5lFJvoeRnqf4wD7R/0wF42Jvv8g6FPv581ESYrtESRF85K6RijbeTDZFaNtso+TwhtqFCnwr8hIXsGprcliIpWZB2yu/SF8f6VjU8b35PG0Mqb01315nSft3YBec3noNeVUbRVDkoa+Zjs+5KPalw2JorRFi9gy1qZFtM/Dy8haeloFOhSs4BoDelH0HEYhh4J3vkX8Zh+Lot+JYT0gqF4BKI6m/jdC7fsUX4Qe3MK3SGon7w5tVgOHi8MlhG9XtAVgpgK3iEmATWJR3ZjHfh+pgkwDn6kU2eLvUFBRNIPssYDa1osfzIB3A0O2CsbXFpJFZhmjRUXl83+oA01y2HPYC7ngTWl15/N/px8TaULenuLKe51gIqXOAMzQ8AMJ1P+voGYvjKKYiVmysxNG7oWZ3GjdpMrzXvoWJyFS9j3M8QBXc5E75TZL+hSMav3CgUOOYENig87gWv5e31wkr/XBv3296aj1OGbajl8eLSNv3I4fq/CeGlMDVlIrTMhTYc+YuztKx0YP4oegbkWG2gcc+laLaSr9QdWa396kb3B5/oWW/AtMMriC43yfoQSaGDkB9GSXqGSwqzpfUQgmcE18gONQHTX5XHpPV84rZgeA+fVgpKq/9Bb/UcT/zhdBKTA8TTEHleiErdXYUMBtSPPFRVecnlioolBFzclrQsGoNLkc248cRr52BbntNx27tFtraX7ZahHTJOmWWrrC/p5DJuQRCEr+poOmsco3BL4iBdMQVNp2Up0w/fiVaHhjUQjqEojdVi1NdVzTvooS0+6yWSrStkpl32+R1BTIDpY6DGk006Ds3y9w+w/0cKBpIUrrRDlSnWvFSK9vUSkDU3X4MzM6Ht4VuifIv41TuJfvca/I9X8K2udZyd/55mnyHIbjBYwjn+WBKuDJR+oBffeYsUCHAdiLzMq54SU6tgarwpz7tKm3pdzDTUlvoPlV/korJ5/3Y7/V1rPNaxD4VljEn573r2rfJbvht1KdCa6+ufMu38r9OHmM9M8vS9AcVVnpoZYhm7LYvqdsfKO0rhiXdKU9IlqQ2KlVYhT8x9lVQhZTJXWmoFYhWARMvWcUxTdC9t1nlcxbdvhfGvvd4vAwSNGSYQi1mH7viV05Pa1Ait1VVqpKSCdBjVp6nqPcY1pKo9rmrdi/68NSBuX7NEdWykFipW+XvhRLoZ1JGPY2t76NWNYWbCVelIg5isS8U7Pt9gdVSHumUexb6j2J1aDJl60oAMTtbnr3e8ecPHB8zDqyfjG8lFb0liV1hhA45Hp2yfjax50kwVbLRk7KjE99VhLfUNd+YE2WazVnKZ2DJ5PLKf9RFu9v91x+FrTW7pf5gAcEAmpHkT+HEcEQMMWXRnY2YZd+xlY/zeS+lTmls3x+88055a5T3I+1og/TWvXf51b618EsSf2B/oV3I/ZXbM/9nDL7XmezoK+bhq85znT8eQvnv/wpubPLu+XxMpFmYLOojvOQIu2sgwU9Ojkj3ScqirPdGXDUaXne4BY0bnHjU/QeezOAHnAcysbn2L0PU1LBHe7HabeAZqitfzqH6i/xStvk1xu1+EL8QgU1rPROAR0pFY+f84ksw8a4iRT0Eaqjd/g2yxKdW08z97iaZ5rwwVgUv87IVPctZw2Vc9OhyreeAtqa3+mNKZ9HlSUxy6ElHk9oGiz/GdCmccr/yUTmP0H -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/queries/request_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | import 'format_mode.dart'; 6 | import 'timeout.dart'; 7 | import 'write_size_limit.dart'; 8 | 9 | part 'request_config.freezed.dart'; 10 | 11 | /// The standard request configuration for firebase requests. Allows to 12 | /// configure standard realtime database parameters used by all firebase 13 | /// requests. 14 | @freezed 15 | class RequestConfig 16 | with 17 | MapMixin, 18 | // ignore: prefer_mixin 19 | UnmodifiableMapMixin, 20 | _$RequestConfig { 21 | const RequestConfig._(); 22 | 23 | /// Default constructor. 24 | // ignore: sort_unnamed_constructors_first 25 | const factory RequestConfig({ 26 | /// This is an advanced feature, designed to help you work with large 27 | /// datasets without needing to download everything. Set this to true to 28 | /// limit the depth of the data returned at a location. If the data at the 29 | /// location is a JSON primitive (string, number or boolean), its value will 30 | /// simply be returned. If the data snapshot at the location is a JSON 31 | /// object, the values for each key will be truncated to true. 32 | bool? shallow, 33 | 34 | /// The format mode to be used. See [FormatMode]. 35 | FormatMode? format, 36 | 37 | /// The timeout for read requests. See [Timeout]. 38 | Timeout? timeout, 39 | 40 | // The timeout estimation for write requests. See [WriteSizeLimit]. 41 | WriteSizeLimit? writeSizeLimit, 42 | }) = _RequestConfig; 43 | 44 | @override 45 | String? operator [](Object? key) { 46 | switch (key) { 47 | case 'shallow': 48 | return shallow?.toString(); 49 | case 'format': 50 | return format?.name; 51 | case 'timeout': 52 | return timeout?.toString(); 53 | case 'writeSizeLimit': 54 | return writeSizeLimit?.name; 55 | default: 56 | return null; 57 | } 58 | } 59 | 60 | @override 61 | Iterable get keys => [ 62 | if (shallow != null) 'shallow', 63 | if (format != null) 'format', 64 | if (timeout != null) 'timeout', 65 | if (writeSizeLimit != null) 'writeSizeLimit', 66 | ]; 67 | } 68 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/unit/queries/filter_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data_firebase_database/src/queries/filter.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Filter', () { 6 | group('order', () { 7 | test('property sets orderBy to name', () { 8 | final filters = Filter.property('name').build(); 9 | 10 | expect(filters, const { 11 | 'orderBy': '"name"', 12 | }); 13 | }); 14 | 15 | test(r'key sets orderBy to $key', () { 16 | final filters = Filter.key().build(); 17 | 18 | expect(filters, const { 19 | 'orderBy': r'"$key"', 20 | }); 21 | }); 22 | 23 | test(r'value sets orderBy to $value', () { 24 | final filters = Filter.value().build(); 25 | 26 | expect(filters, const { 27 | 'orderBy': r'"$value"', 28 | }); 29 | }); 30 | }); 31 | 32 | group('filter', () { 33 | test('limitToFirst sets value as query parameter', () { 34 | final filters = Filter.key().limitToFirst(10).build(); 35 | 36 | expect(filters, const { 37 | 'orderBy': r'"$key"', 38 | 'limitToFirst': '10', 39 | }); 40 | }); 41 | 42 | test('limitToLast sets value as query parameter', () { 43 | final filters = Filter.key().limitToLast(10).build(); 44 | 45 | expect(filters, const { 46 | 'orderBy': r'"$key"', 47 | 'limitToLast': '10', 48 | }); 49 | }); 50 | 51 | test('startAt sets value as query parameter', () { 52 | final filters = Filter.key().startAt('A').build(); 53 | 54 | expect(filters, const { 55 | 'orderBy': r'"$key"', 56 | 'startAt': '"A"', 57 | }); 58 | }); 59 | 60 | test('endAt sets value as query parameter', () { 61 | final filters = Filter.key().endAt('A').build(); 62 | 63 | expect(filters, const { 64 | 'orderBy': r'"$key"', 65 | 'endAt': '"A"', 66 | }); 67 | }); 68 | 69 | test('equalTo sets value as query parameter', () { 70 | final filters = Filter.key().equalTo('A').build(); 71 | 72 | expect(filters, const { 73 | 'orderBy': r'"$key"', 74 | 'equalTo': '"A"', 75 | }); 76 | }); 77 | }); 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /demo/lib/src/auth/google_auth.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_data_demo/src/setup/defines.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:oauth2/oauth2.dart'; 6 | import 'package:url_launcher/url_launcher.dart'; 7 | 8 | late final googleAuthProvider = Provider( 9 | (ref) => GoogleAuth( 10 | ref.watch(definesProvider), 11 | ), 12 | ); 13 | 14 | class GoogleAuth { 15 | static final redirectUrl = Uri.parse('http://localhost:5000/__/auth/handler'); 16 | 17 | static final _authorizationEndpoint = 18 | Uri.parse('https://accounts.google.com/o/oauth2/auth'); 19 | static final _tokenEndpoint = 20 | Uri.parse('https://oauth2.googleapis.com/token'); 21 | static const _scopes = ['openid']; 22 | 23 | final Defines _defines; 24 | 25 | GoogleAuth(this._defines); 26 | 27 | Future authorize() async { 28 | final grant = AuthorizationCodeGrant( 29 | _defines.googleClientId, 30 | _authorizationEndpoint, 31 | _tokenEndpoint, 32 | secret: _defines.googleClientSecret, 33 | ); 34 | try { 35 | final authorizationUrl = grant.getAuthorizationUrl( 36 | redirectUrl, 37 | scopes: _scopes, 38 | ); 39 | 40 | final responseUrlFuture = _listen(redirectUrl); 41 | await _redirect(authorizationUrl); 42 | final responseUrl = await responseUrlFuture; 43 | 44 | final client = await grant.handleAuthorizationResponse( 45 | responseUrl.queryParameters, 46 | ); 47 | return client.credentials; 48 | } finally { 49 | grant.close(); 50 | } 51 | } 52 | 53 | Future _redirect(Uri authorizationUrl) async { 54 | await launchUrl( 55 | authorizationUrl, 56 | mode: LaunchMode.inAppWebView, 57 | ); 58 | } 59 | 60 | Future _listen(Uri redirectUrl) async { 61 | final server = await HttpServer.bind(redirectUrl.host, redirectUrl.port); 62 | try { 63 | await for (final request in server) { 64 | if (request.uri.path == redirectUrl.path) { 65 | request.response.writeln( 66 | "Authentication successful. You can close this window now", 67 | ); 68 | await request.response.close(); 69 | return request.uri; 70 | } 71 | } 72 | 73 | throw StateError('Server terminated without authentication'); 74 | } finally { 75 | await server.close(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/integration/repositories/test.data.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: directives_ordering, top_level_function_literal_block 2 | 3 | import 'package:flutter_data/flutter_data.dart'; 4 | import 'package:hive/hive.dart'; 5 | 6 | import 'test_repository.dart'; 7 | import 'values_repository.dart'; 8 | 9 | // ignore: prefer_function_declarations_over_variables 10 | ConfigureRepositoryLocalStorage configureRepositoryLocalStorage = ({ 11 | FutureFn? baseDirFn, 12 | List? encryptionKey, 13 | bool? clear, 14 | }) => 15 | hiveLocalStorageProvider.overrideWithProvider( 16 | Provider( 17 | (_) => HiveLocalStorage( 18 | hive: Hive, 19 | baseDirFn: baseDirFn, 20 | encryptionKey: encryptionKey, 21 | clear: clear, 22 | ), 23 | ), 24 | ); 25 | 26 | // ignore: prefer_function_declarations_over_variables 27 | RepositoryInitializerProvider repositoryInitializerProvider = 28 | ({bool? remote, bool? verbose}) => _repositoryInitializerProviderFamily( 29 | RepositoryInitializerArgs(remote, verbose), 30 | ); 31 | 32 | final repositoryProviders = >>{ 33 | 'testModels': testModelsRepositoryProvider, 34 | 'valuesModels': valuesModelsRepositoryProvider, 35 | }; 36 | 37 | final _repositoryInitializerProviderFamily = 38 | FutureProvider.family( 39 | (ref, args) async { 40 | final adapters = { 41 | 'testModels': ref.watch(testModelsRemoteAdapterProvider), 42 | 'valuesModels': ref.watch(valuesModelsRemoteAdapterProvider), 43 | }; 44 | final remotes = { 45 | 'testModels': true, 46 | 'valuesModels': true, 47 | }; 48 | 49 | await ref.watch(graphNotifierProvider).initialize(); 50 | 51 | final _repoMap = { 52 | for (final type in repositoryProviders.keys) 53 | type: ref.watch(repositoryProviders[type]!) 54 | }; 55 | 56 | for (final type in _repoMap.keys) { 57 | final repository = _repoMap[type]!..dispose(); 58 | await repository.initialize( 59 | remote: args.remote ?? remotes[type], 60 | verbose: args.verbose, 61 | adapters: adapters, 62 | ); 63 | } 64 | 65 | ref.onDispose(() { 66 | for (final repository in _repoMap.values) { 67 | repository.dispose(); 68 | } 69 | }); 70 | 71 | return RepositoryInitializer(); 72 | }); 73 | -------------------------------------------------------------------------------- /demo/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/test/unit/queries/timeout_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_const_constructors 2 | import 'package:dart_test_tools/test.dart'; 3 | import 'package:flutter_data_firebase_database/src/queries/timeout.dart'; 4 | import 'package:test/test.dart' hide Timeout; 5 | import 'package:tuple/tuple.dart'; 6 | 7 | void main() { 8 | group('Timeout', () { 9 | testData>( 10 | 'constructs correct timeouts from unit constructors', const [ 11 | Tuple3(Timeout.ms(10), Duration(milliseconds: 10), '10ms'), 12 | Tuple3(Timeout.ms(20000), Duration(seconds: 20), '20000ms'), 13 | Tuple3(Timeout.s(10), Duration(seconds: 10), '10s'), 14 | Tuple3(Timeout.s(180), Duration(minutes: 3), '180s'), 15 | Tuple3(Timeout.min(10), Duration(minutes: 10), '10min'), 16 | ], (fixture) { 17 | expect(fixture.item1.duration, fixture.item2); 18 | expect(fixture.item1.toString(), fixture.item3); 19 | }); 20 | 21 | testData>( 22 | 'fromDuration converts to correct timeout', const [ 23 | Tuple2(Duration(milliseconds: 60), Timeout.ms(60)), 24 | Tuple2(Duration(milliseconds: 6000), Timeout.s(6)), 25 | Tuple2(Duration(milliseconds: 6500), Timeout.ms(6500)), 26 | Tuple2(Duration(milliseconds: 60000), Timeout.min(1)), 27 | Tuple2(Duration(milliseconds: 63000), Timeout.s(63)), 28 | Tuple2(Duration(milliseconds: 63500), Timeout.ms(63500)), 29 | ], (fixture) { 30 | final t = Timeout.fromDuration(fixture.item1); 31 | expect(t, fixture.item2); 32 | expect(t.duration, fixture.item1); 33 | }); 34 | 35 | test( 36 | 'Limits Timeouts to positive times up to 15 minutes', 37 | () { 38 | expect(() => Timeout.ms(-5), throwsA(isA())); 39 | expect( 40 | () => Timeout.ms(15 * 60 * 1000 + 1), 41 | throwsA(isA()), 42 | ); 43 | expect(() => Timeout.s(-5), throwsA(isA())); 44 | expect(() => Timeout.s(15 * 60 + 1), throwsA(isA())); 45 | expect(() => Timeout.min(-5), throwsA(isA())); 46 | expect(() => Timeout.min(15 + 1), throwsA(isA())); 47 | expect( 48 | () => Timeout.fromDuration(const Duration(microseconds: 10)), 49 | throwsA(isA()), 50 | ); 51 | expect( 52 | () => Timeout.fromDuration(const Duration(minutes: 20)), 53 | throwsA(isA()), 54 | ); 55 | }, 56 | ); 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /packages/flutter_data_sync/lib/src/semaphore.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'dart:collection'; 4 | 5 | class SemaphoreDisposed implements Exception { 6 | @override 7 | String toString() => 'The semaphore has been disposed while waiting on it'; 8 | } 9 | 10 | class Semaphore { 11 | static const defaultMaxCount = 10; 12 | 13 | final int _maxCount; 14 | 15 | final _waitingConsumers = Queue>(); 16 | 17 | Completer? _disposeCompleter; 18 | int _currentCount; 19 | 20 | Semaphore([this._maxCount = defaultMaxCount]) : _currentCount = _maxCount; 21 | 22 | Future acquire() { 23 | assert(_currentCount >= 0); 24 | assert(_currentCount <= _maxCount); 25 | 26 | if (_disposeCompleter != null) { 27 | throw StateError('Semaphore has already been disposed'); 28 | } 29 | 30 | if (_currentCount == 0) { 31 | final completer = Completer(); 32 | _waitingConsumers.add(completer); 33 | return completer.future; 34 | } else { 35 | _currentCount--; 36 | return Future.value(); 37 | } 38 | } 39 | 40 | void release() { 41 | assert(_currentCount >= 0); 42 | assert(_currentCount <= _maxCount); 43 | 44 | if (_currentCount == _maxCount) { 45 | throw StateError('Cannot release any more resources'); 46 | } 47 | 48 | _currentCount++; 49 | 50 | if (_disposeCompleter != null) { 51 | // ignore: invariant_booleans 52 | if (_currentCount == _maxCount) { 53 | assert(!_disposeCompleter!.isCompleted); 54 | _disposeCompleter!.complete(); 55 | } 56 | return; 57 | } 58 | 59 | while (_currentCount > 0 && _waitingConsumers.isNotEmpty) { 60 | assert(!_waitingConsumers.first.isCompleted); 61 | 62 | _currentCount--; 63 | _waitingConsumers.removeFirst().complete(); 64 | } 65 | } 66 | 67 | Future dispose() async { 68 | if (_disposeCompleter != null) { 69 | throw StateError('Semaphore has already been disposed'); 70 | } 71 | 72 | // mark as disposed 73 | _disposeCompleter = Completer(); 74 | 75 | // clear all pending consumers 76 | for (final consumer in _waitingConsumers) { 77 | assert(!consumer.isCompleted); 78 | consumer.completeError(SemaphoreDisposed(), StackTrace.current); 79 | } 80 | _waitingConsumers.clear(); 81 | 82 | // wait until resources have been released 83 | if (_currentCount == _maxCount) { 84 | _disposeCompleter!.complete(); 85 | return; 86 | } else { 87 | await _disposeCompleter!.future; 88 | assert(_currentCount == _maxCount); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/hive/sodium_hive_local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:flutter_data/flutter_data.dart'; 5 | import 'package:hive/hive.dart'; 6 | import 'package:sodium/sodium.dart'; 7 | 8 | import '../../flutter_data_sodium.dart'; 9 | 10 | class _FakeAesSodiumHiveCipher extends SodiumHiveCipher 11 | implements HiveAesCipher { 12 | _FakeAesSodiumHiveCipher({ 13 | required Sodium sodium, 14 | required SecureKey encryptionKey, 15 | }) : super( 16 | sodium: sodium, 17 | encryptionKey: encryptionKey, 18 | ); 19 | 20 | @override 21 | Uint8List generateIv() => sodium.randombytes.buf( 22 | sodium.crypto.secretBox.nonceBytes, 23 | ); 24 | } 25 | 26 | /// A customization of [HiveLocalStorage] that uses [SodiumHiveCipher] instead 27 | /// of [HiveAesCipher] for local encryption. 28 | /// 29 | /// Unlike the normal [HiveLocalStorage] this class always requires an 30 | /// encryption key, as it does not make sense without one. Internally, a fake 31 | /// implementation of a [HiveAesCipher] that wraps [SodiumHiveCipher] is used, 32 | /// to not break the API of the base class. 33 | class SodiumHiveLocalStorage extends HiveLocalStorage { 34 | final _FakeAesSodiumHiveCipher _sodiumHiveCipher; 35 | 36 | /// Constructor. 37 | /// 38 | /// Both [sodium] and [encryptionKey] are required to initialize the storage, 39 | /// as the storage will always be encrypted. You can use 40 | /// [SodiumHiveCipher.keyBytes] to create a [SecureKey] of the correct length. 41 | /// 42 | /// The [hive], [baseDirFn] and [clear] are simply forwarded to the 43 | /// [HiveLocalStorage.new] constructor. 44 | SodiumHiveLocalStorage({ 45 | required HiveInterface hive, 46 | required Sodium sodium, 47 | required SecureKey encryptionKey, 48 | FutureOr Function()? baseDirFn, 49 | bool? clear, 50 | }) : _sodiumHiveCipher = _FakeAesSodiumHiveCipher( 51 | sodium: sodium, 52 | encryptionKey: encryptionKey, 53 | ), 54 | super( 55 | hive: hive, 56 | baseDirFn: baseDirFn, 57 | clear: clear, 58 | ); 59 | 60 | /// The internally used [SodiumHiveCipher] instance. 61 | /// 62 | /// This will be the same object as [encryptionCipher], as internally a 63 | /// wrapper class is used that implements both interfaces, but it is in fact 64 | /// a [SodiumHiveCipher] regarding the actual implementation. 65 | SodiumHiveCipher get sodiumHiveCipher => _sodiumHiveCipher; 66 | 67 | @override 68 | HiveAesCipher get encryptionCipher => _sodiumHiveCipher; 69 | } 70 | -------------------------------------------------------------------------------- /demo/lib/src/password/application_key_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:flutter/foundation.dart' as ff; 6 | import 'package:flutter_data/flutter_data.dart'; 7 | import 'package:flutter_data_demo/src/password/password_controller.dart'; 8 | import 'package:flutter_data_demo/src/setup/providers.dart'; 9 | import 'package:sodium_libs/sodium_libs.dart'; 10 | import 'package:flutter_data_sodium/flutter_data_sodium.dart'; 11 | 12 | // ignore: implementation_imports 13 | import 'package:sodium_libs/src/platforms/platforms.ffi.dart'; 14 | 15 | final applicationKeyManagerProvider = Provider( 16 | (ref) => ApplicationKeyManager( 17 | sodium: ref.watch(sodiumProvider), 18 | passwordController: ref.watch(passwordControllerProvider.notifier), 19 | getLocalId: () => ref.read(localIdProvider), 20 | ), 21 | ); 22 | 23 | class ApplicationKeyManager extends PassphraseBasedKeyManager 24 | with ParallelMasterKeyComputation { 25 | final PasswordController passwordController; 26 | final String Function() getLocalId; 27 | 28 | ApplicationKeyManager({ 29 | required Sodium sodium, 30 | required this.passwordController, 31 | required this.getLocalId, 32 | }) : super(sodium: sodium); 33 | 34 | @override 35 | FutureOr loadMasterKeyComponents(int saltLength) async { 36 | final password = await _letUserInputPassword(); 37 | final salt = _generateSaltForUser(saltLength); 38 | return MasterKeyComponents(password: password, salt: salt); 39 | } 40 | 41 | Future _letUserInputPassword() => 42 | passwordController.requestPassword(); 43 | 44 | Uint8List _generateSaltForUser(int saltLength) => sodium.crypto.genericHash( 45 | message: getLocalId().toCharArray().unsignedView(), 46 | outLen: saltLength, 47 | ); 48 | 49 | @override 50 | CreateSodiumFn get sodiumFactory => _sodiumFactory; 51 | 52 | @override 53 | Future compute(ComputeCallback callback, Q message) => 54 | ff.compute(callback, message); 55 | 56 | static Future _sodiumFactory() { 57 | if (Platform.isAndroid) { 58 | SodiumAndroid.registerWith(); 59 | } else if (Platform.isIOS) { 60 | SodiumIos.registerWith(); 61 | } else if (Platform.isLinux) { 62 | SodiumLinux.registerWith(); 63 | } else if (Platform.isWindows) { 64 | SodiumWindows.registerWith(); 65 | } else if (Platform.isMacOS) { 66 | SodiumMacos.registerWith(); 67 | } else { 68 | throw UnsupportedError( 69 | 'Platform ${Platform.operatingSystem} is not supported by sodium_libs', 70 | ); 71 | } 72 | 73 | return SodiumPlatform.instance.loadSodium(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/event_stream/web_database_event_stream.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:async'; 4 | import 'dart:html'; 5 | 6 | import 'package:http/http.dart' as http; 7 | import 'package:meta/meta.dart'; 8 | import 'package:rxdart/rxdart.dart'; 9 | 10 | import '../database_event.dart'; 11 | 12 | @internal 13 | class DatabaseEventStream extends Stream { 14 | final Uri uri; 15 | final Map? headers; 16 | 17 | DatabaseEventStream({ 18 | required this.uri, 19 | this.headers, 20 | http.Client? client, 21 | }) { 22 | client?.close(); 23 | } 24 | 25 | @override 26 | StreamSubscription listen( 27 | void Function(DatabaseEvent event)? onData, { 28 | Function? onError, 29 | void Function()? onDone, 30 | bool? cancelOnError, 31 | }) { 32 | final eventSource = createEventSource(); 33 | 34 | final putStream = 35 | eventSource.on[DatabaseEvent.putEvent].cast().map( 36 | (event) => DatabaseEvent.put( 37 | DatabaseEventData.fromRawJson(event.data as String), 38 | ), 39 | ); 40 | 41 | final patchStream = 42 | eventSource.on[DatabaseEvent.patchEvent].cast().map( 43 | (event) => DatabaseEvent.patch( 44 | DatabaseEventData.fromRawJson(event.data as String), 45 | ), 46 | ); 47 | 48 | final keepAliveStream = eventSource.on[DatabaseEvent.keepAliveEvent] 49 | .cast() 50 | .map((event) => const DatabaseEvent.keepAlive()); 51 | 52 | final cancelStream = eventSource.on[DatabaseEvent.cancelEvent] 53 | .cast() 54 | .map((event) => DatabaseEvent.cancel(event.data as String? ?? '')); 55 | 56 | final authRevokedStream = eventSource.on[DatabaseEvent.authRevokedEvent] 57 | .cast() 58 | .map((event) => const DatabaseEvent.authRevoked()); 59 | 60 | final errorStream = 61 | eventSource.onError.cast().map( 62 | // ignore: only_throw_errors 63 | (event) => throw event.error ?? Exception('Unknown Error'), 64 | ); 65 | 66 | return MergeStream([ 67 | putStream, 68 | patchStream, 69 | keepAliveStream, 70 | cancelStream, 71 | authRevokedStream, 72 | errorStream, 73 | ]).doOnCancel(() => eventSource.close()).listen( 74 | onData, 75 | onError: onError, 76 | onDone: onDone, 77 | cancelOnError: cancelOnError, 78 | ); 79 | } 80 | 81 | @visibleForTesting 82 | EventSource createEventSource() => EventSource(uri.toString()); 83 | } 84 | -------------------------------------------------------------------------------- /demo/lib/src/password/password_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_data_demo/src/password/password_controller.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | class PasswordScope extends ConsumerWidget { 6 | final Widget child; 7 | 8 | const PasswordScope({ 9 | Key? key, 10 | required this.child, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context, WidgetRef ref) { 15 | ref.listen( 16 | passwordControllerProvider, 17 | (previous, next) => next.whenOrNull( 18 | requested: () => _showPasswordDialog(context), 19 | ), 20 | ); 21 | 22 | return child; 23 | } 24 | 25 | void _showPasswordDialog(BuildContext context) => showDialog( 26 | context: context, 27 | barrierDismissible: false, 28 | useRootNavigator: true, 29 | builder: (context) => const _PasswordDialog(), 30 | ); 31 | } 32 | 33 | class _PasswordDialog extends ConsumerStatefulWidget { 34 | const _PasswordDialog({Key? key}) : super(key: key); 35 | 36 | @override 37 | ConsumerState<_PasswordDialog> createState() => _PasswordDialogState(); 38 | } 39 | 40 | class _PasswordDialogState extends ConsumerState<_PasswordDialog> { 41 | late final TextEditingController _controller; 42 | var _completed = false; 43 | 44 | @override 45 | void initState() { 46 | super.initState(); 47 | _controller = TextEditingController(); 48 | } 49 | 50 | @override 51 | void dispose() { 52 | _controller.dispose(); 53 | super.dispose(); 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) => WillPopScope( 58 | onWillPop: () => Future.value(_completed), 59 | child: AlertDialog( 60 | title: const Text('Enter passphrase'), 61 | content: TextField( 62 | controller: _controller, 63 | autocorrect: false, 64 | enableIMEPersonalizedLearning: false, 65 | enableSuggestions: false, 66 | keyboardType: TextInputType.visiblePassword, 67 | obscureText: true, 68 | decoration: const InputDecoration( 69 | label: Text('Password'), 70 | ), 71 | onSubmitted: (_) => _setPassword(context), 72 | ), 73 | actions: [ 74 | TextButton( 75 | onPressed: () => _setPassword(context), 76 | child: const Text('OK'), 77 | ), 78 | ], 79 | ), 80 | ); 81 | 82 | void _setPassword(BuildContext context) { 83 | ref.read(passwordControllerProvider.notifier).setPassword(_controller.text); 84 | setState(() { 85 | _completed = true; 86 | }); 87 | Navigator.of(context).pop(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/test/unit/hive/configure_repository_local_storage_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_data/flutter_data.dart'; 2 | import 'package:flutter_data_sodium/src/hive/configure_repository_local_storage.dart'; 3 | import 'package:flutter_data_sodium/src/hive/sodium_hive_local_storage.dart'; 4 | import 'package:hive/hive.dart'; 5 | import 'package:mocktail/mocktail.dart'; 6 | import 'package:sodium/sodium.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | class MockHive extends Mock implements HiveInterface {} 10 | 11 | class MockSodium extends Mock implements Sodium {} 12 | 13 | class MockCrypto extends Mock implements Crypto {} 14 | 15 | class MockSecretBox extends Mock implements SecretBox {} 16 | 17 | class FakeSecureKey extends Fake implements SecureKey { 18 | @override 19 | final int length; 20 | 21 | FakeSecureKey(this.length); 22 | } 23 | 24 | void main() { 25 | group('configureRepositoryLocalStorageSodium', () { 26 | const keyLength = 11; 27 | final fakeEncryptionKey = FakeSecureKey(keyLength); 28 | // ignore: prefer_function_declarations_over_variables 29 | final baseDirFn = () => ''; 30 | final mockHive = MockHive(); 31 | final mockSodium = MockSodium(); 32 | final mockCrypto = MockCrypto(); 33 | final mockSecretBox = MockSecretBox(); 34 | 35 | setUp(() { 36 | reset(mockHive); 37 | reset(mockSodium); 38 | reset(mockCrypto); 39 | reset(mockSecretBox); 40 | 41 | when(() => mockSodium.crypto).thenReturn(mockCrypto); 42 | when(() => mockCrypto.secretBox).thenReturn(mockSecretBox); 43 | when(() => mockSecretBox.keyBytes).thenReturn(keyLength); 44 | }); 45 | 46 | test('creates override with SodiumHiveLocalStorage provider', () { 47 | final sutOverride = configureRepositoryLocalStorageSodium( 48 | sodium: (_) => mockSodium, 49 | encryptionKey: (_) => fakeEncryptionKey, 50 | baseDirFn: baseDirFn, 51 | ); 52 | 53 | final providerContainer = ProviderContainer( 54 | overrides: [ 55 | hiveProvider.overrideWithValue(mockHive), 56 | sutOverride, 57 | ], 58 | ); 59 | 60 | final instance = providerContainer.read(hiveLocalStorageProvider); 61 | 62 | expect( 63 | instance, 64 | isA() 65 | .having((sut) => sut.hive, 'hive', mockHive) 66 | .having((sut) => sut.baseDirFn, 'baseDirFn', baseDirFn) 67 | .having( 68 | (sut) => sut.sodiumHiveCipher.sodium, 69 | 'sodiumHiveCipher.sodium', 70 | mockSodium, 71 | ) 72 | .having( 73 | (sut) => sut.sodiumHiveCipher.encryptionKey, 74 | 'sodiumHiveCipher.encryptionKey', 75 | fakeEncryptionKey, 76 | ), 77 | ); 78 | }); 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/queries/timeout.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'timeout.freezed.dart'; 4 | 5 | /// Specifies timeout for read requests. 6 | /// 7 | /// Use this to limit how long the read takes on the server side. If a read 8 | /// request doesn't finish within the allotted time, it terminates with an HTTP 9 | /// 400 error. This is particularly useful when you expect a small data transfer 10 | /// and don't want to wait too long to fetch a potentially huge subtree. Actual 11 | /// read time might vary based on data size and caching. 12 | /// 13 | /// **Note:** The maximum timeout is 15 minutes. 14 | @freezed 15 | class Timeout with _$Timeout { 16 | const Timeout._(); 17 | 18 | /// Creates a timeout with a milliseconds resolution for [value] 19 | @Assert('value > 0', 'value must be a positive integer') 20 | @Assert('value <= 900000', 'value must be at most 15 min (900000 ms)') 21 | const factory Timeout.ms(int value) = _TimeoutMs; 22 | 23 | /// Creates a timeout with a seconds resolution for [value] 24 | @Assert('value > 0', 'value must be a positive integer') 25 | @Assert('value <= 900', 'value must be at most 15 min (900 s)') 26 | const factory Timeout.s(int value) = _TimeoutS; 27 | 28 | /// Creates a timeout with a minutes resolution for [value] 29 | @Assert('value > 0', 'value must be a positive integer') 30 | @Assert('value <= 15', 'value must be at most 15 min') 31 | const factory Timeout.min(int value) = _TimeoutMin; 32 | 33 | /// Creates a timeout from a [Duration] object. 34 | /// 35 | /// The limit of max. 15 minutes still applies for [duration]. The resulting 36 | /// timeout will be either ms, s or min, depending on whether the [duration] 37 | /// fits into each without a remainder. For durations with parts smaller then 38 | /// milliseconds, those parts get ignored. 39 | factory Timeout.fromDuration(Duration duration) { 40 | if (duration.inMilliseconds % 1000 != 0) { 41 | return Timeout.ms(duration.inMilliseconds); 42 | } else if (duration.inSeconds % 60 != 0) { 43 | return Timeout.s(duration.inSeconds); 44 | } else { 45 | return Timeout.min(duration.inMinutes); 46 | } 47 | } 48 | 49 | /// The integer value of the timeout. 50 | /// 51 | /// Depending on the timeout, this can either be milliseconds, seconds or 52 | /// minutes. 53 | @override 54 | int get value; 55 | 56 | /// Converts the timeout to a [Duration] with the same time value. 57 | Duration get duration => when( 58 | ms: (value) => Duration(milliseconds: value), 59 | s: (value) => Duration(seconds: value), 60 | min: (value) => Duration(minutes: value), 61 | ); 62 | 63 | @override 64 | String toString() => when( 65 | ms: (value) => '${value}ms', 66 | s: (value) => '${value}s', 67 | min: (value) => '${value}min', 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /packages/flutter_data_sodium/lib/src/encryption/data_cipher.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:meta/meta.dart'; 5 | import 'package:sodium/sodium.dart'; 6 | 7 | import '../key_management/key_manager.dart'; 8 | import 'encrypted_data.dart'; 9 | 10 | /// @nodoc 11 | @internal 12 | class DataCipher { 13 | static const _idJsonKey = 'id'; 14 | 15 | /// @nodoc 16 | final Sodium sodium; 17 | 18 | /// @nodoc 19 | final KeyManager keyManager; 20 | 21 | /// @nodoc 22 | const DataCipher({ 23 | required this.sodium, 24 | required this.keyManager, 25 | }); 26 | 27 | /// @nodoc 28 | EncryptedData encrypt(String type, Map jsonData) { 29 | final id = jsonData[_idJsonKey] as Object?; 30 | final jsonDataWithoutId = { 31 | for (final entry in jsonData.entries) 32 | if (entry.key != _idJsonKey) entry.key: entry.value, 33 | }; 34 | 35 | final keyInfo = 36 | keyManager.remoteKeyForType(type, sodium.crypto.aead.keyBytes); 37 | final nonce = sodium.randombytes.buf(sodium.crypto.aead.nonceBytes); 38 | 39 | final cipherData = sodium.crypto.aead.encryptDetached( 40 | message: _jsonToBytes(jsonDataWithoutId), 41 | nonce: nonce, 42 | key: keyInfo.secureKey, 43 | additionalData: _getAdForId(id), 44 | ); 45 | 46 | return EncryptedData( 47 | id: id, 48 | cipherText: cipherData.cipherText, 49 | mac: cipherData.mac, 50 | nonce: nonce, 51 | hasAd: id != null, 52 | keyId: keyInfo.keyId, 53 | ); 54 | } 55 | 56 | /// @nodoc 57 | dynamic decrypt(String type, EncryptedData encryptedData) { 58 | final secureKey = keyManager.remoteKeyForTypeAndId( 59 | type, 60 | encryptedData.keyId, 61 | sodium.crypto.aead.keyBytes, 62 | ); 63 | 64 | final plainData = sodium.crypto.aead.decryptDetached( 65 | cipherText: encryptedData.cipherText, 66 | mac: encryptedData.mac, 67 | nonce: encryptedData.nonce, 68 | key: secureKey, 69 | additionalData: _getAdForId(encryptedData.id, encryptedData.hasAd), 70 | ); 71 | 72 | final dynamic plainJson = _bytesToJson(plainData); 73 | if (plainJson is Map) { 74 | return { 75 | ...plainJson, 76 | _idJsonKey: encryptedData.id, 77 | }; 78 | } else { 79 | return plainJson; 80 | } 81 | } 82 | 83 | Uint8List? _getAdForId(Object? id, [bool? hasAd]) { 84 | if (hasAd ?? id != null) { 85 | assert( 86 | id != null, 87 | 'Invalid cipher data. Expected an id but none was given.', 88 | ); 89 | 90 | return _jsonToBytes(id); 91 | } else { 92 | return null; 93 | } 94 | } 95 | 96 | Uint8List _jsonToBytes(dynamic jsonData) => 97 | json.encode(jsonData).toCharArray().unsignedView(); 98 | 99 | dynamic _bytesToJson(Uint8List bytes) => 100 | json.decode(bytes.signedView().toDartString()); 101 | } 102 | -------------------------------------------------------------------------------- /demo/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | 28 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 29 | 30 | # Published to parent scope for install step. 31 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 32 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 33 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 34 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 35 | 36 | list(APPEND FLUTTER_LIBRARY_HEADERS 37 | "fl_basic_message_channel.h" 38 | "fl_binary_codec.h" 39 | "fl_binary_messenger.h" 40 | "fl_dart_project.h" 41 | "fl_engine.h" 42 | "fl_json_message_codec.h" 43 | "fl_json_method_codec.h" 44 | "fl_message_codec.h" 45 | "fl_method_call.h" 46 | "fl_method_channel.h" 47 | "fl_method_codec.h" 48 | "fl_method_response.h" 49 | "fl_plugin_registrar.h" 50 | "fl_plugin_registry.h" 51 | "fl_standard_message_codec.h" 52 | "fl_standard_method_codec.h" 53 | "fl_string_codec.h" 54 | "fl_value.h" 55 | "fl_view.h" 56 | "flutter_linux.h" 57 | ) 58 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 59 | add_library(flutter INTERFACE) 60 | target_include_directories(flutter INTERFACE 61 | "${EPHEMERAL_DIR}" 62 | ) 63 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 64 | target_link_libraries(flutter INTERFACE 65 | PkgConfig::GTK 66 | PkgConfig::GLIB 67 | PkgConfig::GIO 68 | ) 69 | add_dependencies(flutter flutter_assemble) 70 | 71 | # === Flutter tool backend === 72 | # _phony_ is a non-existent file to force this command to run every time, 73 | # since currently there's no way to get a full input/output list from the 74 | # flutter tool. 75 | add_custom_command( 76 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 77 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 78 | COMMAND ${CMAKE_COMMAND} -E env 79 | ${FLUTTER_TOOL_ENVIRONMENT} 80 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 81 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 82 | VERBATIM 83 | ) 84 | add_custom_target(flutter_assemble DEPENDS 85 | "${FLUTTER_LIBRARY}" 86 | ${FLUTTER_LIBRARY_HEADERS} 87 | ) 88 | -------------------------------------------------------------------------------- /packages/flutter_data_firebase_database/lib/src/stream/stream_controller_base.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:async'; 4 | 5 | import 'package:flutter_data/flutter_data.dart'; 6 | import 'package:meta/meta.dart'; 7 | 8 | import '../firebase_database_adapter.dart'; 9 | import 'database_event.dart'; 10 | import 'errors/authentication_revoked.dart'; 11 | import 'errors/remote_cancellation.dart'; 12 | 13 | @internal 14 | abstract class StreamControllerBase, TStream> { 15 | static const rootPath = '/'; 16 | 17 | final Future> Function() createStream; 18 | final FirebaseDatabaseAdapter adapter; 19 | final bool autoRenew; 20 | final UnsupportedEventCb? onUnsupportedEvent; 21 | 22 | // ignore: cancel_subscriptions 23 | StreamSubscription? _databaseEventSub; 24 | late final _streamController = StreamController( 25 | onListen: _onListen, 26 | // do not pause event source, as it might be a broadcast and 27 | // we do not want to miss any events 28 | onCancel: _onCancel, 29 | ); 30 | 31 | StreamControllerBase({ 32 | required this.createStream, 33 | required this.adapter, 34 | this.autoRenew = true, 35 | this.onUnsupportedEvent, 36 | }); 37 | 38 | Stream get stream => _streamController.stream; 39 | 40 | @protected 41 | Sink get sink => _streamController.sink; 42 | 43 | void _onListen() { 44 | _databaseEventSub = Stream.fromFuture(createStream()) 45 | .asyncExpand((stream) => stream) 46 | .asyncMap(_onEvent) 47 | .listen( 48 | // use asyncMap to listen to ensure events are processed sequentially 49 | null, 50 | onError: _streamController.addError, 51 | onDone: _streamController.close, 52 | // If desired, errors will cancel via the controller 53 | cancelOnError: false, 54 | ); 55 | } 56 | 57 | Future _onCancel({bool closeController = true}) async { 58 | try { 59 | final sub = _databaseEventSub; 60 | _databaseEventSub = null; 61 | await sub?.cancel(); 62 | } finally { 63 | if (closeController && !_streamController.isClosed) { 64 | await _streamController.close(); 65 | } 66 | } 67 | } 68 | 69 | FutureOr _onEvent(DatabaseEvent event) => event.when( 70 | put: put, 71 | patch: patch, 72 | keepAlive: _keepAlive, 73 | cancel: _cancel, 74 | authRevoked: _authRevoked, 75 | ); 76 | 77 | @protected 78 | FutureOr put(DatabaseEventData data); 79 | 80 | @protected 81 | FutureOr patch(DatabaseEventData data); 82 | 83 | void _keepAlive() {} 84 | 85 | void _cancel(String reason) { 86 | _streamController.addError(RemoteCancellation(reason)); 87 | } 88 | 89 | void _authRevoked() { 90 | if (autoRenew) { 91 | _onCancel(closeController: false).catchError(_streamController.addError); 92 | _onListen(); 93 | } else { 94 | _streamController.addError(AuthenticationRevoked(), StackTrace.current); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | --------------------------------------------------------------------------------