A WebDAV client application optimized for Meta Quest 3 VR headset. Browse your cloud storage (Nextcloud, ownCloud, or any WebDAV server), stream media, and download files for offline viewing - all in an immersive VR environment.
- Virtual Folders: Create multiple connections to different WebDAV servers or paths
- Secure Credentials: Passwords stored using platform-native encryption (Android EncryptedSharedPreferences)
- File Browser: Navigate directories with grid or list view
- Media Streaming: Stream images and videos directly from your server
- Offline Mode: Download files for offline viewing
- VR Optimized: UI designed for Quest 3 display (2064x2208 per eye, 90Hz)
- 360/VR Video: Support for panoramic and VR video content
- Image Viewer: Fullscreen viewing with pinch-to-zoom and pan
- Meta Quest 3 headset with Developer Mode enabled
- Flutter SDK 3.10.7 or higher
- Android SDK with API level 26+ support
- A WebDAV server (Nextcloud, ownCloud, etc.)
-
Install Flutter
# Follow instructions at https://docs.flutter.dev/get-started/install flutter doctor -
Clone the repository
git clone <repository-url> cd quest3_webdav_client
-
Install dependencies
flutter pub get
-
Generate database code
dart run build_runner build
-
Run on emulator or device
flutter run
-
Build debug APK
flutter build apk --debug
The APK will be at
build/app/outputs/flutter-apk/app-debug.apk -
Enable Developer Mode on Quest 3
- Go to Settings > System > Developer
- Enable Developer Mode (requires Meta developer account)
-
Install via ADB
# Connect Quest 3 via USB and authorize the connection adb devices adb install build/app/outputs/flutter-apk/app-debug.apk -
Find the app
- In Quest 3, go to App Library
- Select "Unknown Sources" from the dropdown
- Launch "Quest3 WebDAV"
- Launch the app and tap the + button
- Enter a name for your folder (e.g., "My Nextcloud")
- Enter the server URL (e.g.,
https://cloud.example.com) - Enter the base path (e.g.,
/remote.php/dav/files/username) - Enter your username and password
- Tap Test Connection to verify
- Save when the connection is successful
- Tap a virtual folder to open it
- Navigate directories by tapping folders
- Use breadcrumb navigation at the top to jump to parent directories
- Pull down to refresh the current directory
- Toggle between grid and list view with the view mode button
- Images: Tap to open fullscreen, pinch to zoom, swipe to navigate
- Videos: Tap to play with streaming, use controls overlay
- 360/VR Content: Automatically detected from filename patterns (360, vr, spherical)
- Tap the download button on any media file to save offline
- Access offline files from the home screen menu
- Swipe to delete individual offline files
- Use "Clear All" to remove all offline content
lib/
├── app.dart # Main app configuration
├── main.dart # Entry point
├── constants/
│ └── app_theme.dart # Theme and styling
├── database/
│ └── database.dart # Drift database setup
├── dialogs/
│ ├── delete_folder_dialog.dart
│ └── template_selection_dialog.dart
├── models/
│ ├── file_item.dart # File/directory model
│ ├── offline_file.dart # Offline file tracking
│ ├── server_credentials.dart
│ ├── virtual_folder.dart # Virtual folder model
│ └── webdav_exception.dart
├── providers/
│ ├── credential_provider.dart
│ ├── database_provider.dart
│ ├── file_browser_provider.dart
│ ├── media_viewer_provider.dart
│ ├── offline_files_provider.dart
│ ├── providers.dart # Barrel export
│ └── virtual_folder_provider.dart
├── screens/
│ ├── file_browser_screen.dart
│ ├── home_screen.dart
│ ├── image_viewer_screen.dart
│ ├── offline_files_screen.dart
│ ├── video_player_screen.dart
│ ├── virtual_folder_form_screen.dart
│ └── vr_video_player_screen.dart
├── services/
│ ├── credential_service.dart
│ ├── download_service.dart
│ ├── image_cache_service.dart
│ ├── media_type_detector.dart
│ ├── offline_file_service.dart
│ └── webdav_service.dart
├── utils/
│ ├── mime_type_helper.dart
│ └── quest3_display_helper.dart
└── widgets/
├── breadcrumb_navigation.dart
├── connection_status_indicator.dart
├── download_button.dart
├── download_progress_indicator.dart
├── empty_directory.dart
├── file_grid_item.dart
├── file_list_item.dart
├── folder_card.dart
├── image_viewer_overlay.dart
├── video_controls_overlay.dart
├── video_progress_bar.dart
└── viewer_mode_selector.dart
| Package | Version | Purpose |
|---|---|---|
| webdav_client | ^1.2.2 | WebDAV protocol implementation |
| dio | ^5.9.0 | HTTP client for WebDAV |
| flutter_secure_storage | ^9.2.4 | Encrypted credential storage |
| drift | ^2.27.0 | Type-safe SQLite database |
| sqlite3_flutter_libs | ^0.5.32 | SQLite native libraries |
| flutter_riverpod | ^2.6.1 | State management |
| video_player | ^2.9.5 | Video playback |
| vr_player | ^0.3.0 | 360/VR video support |
| photo_view | ^0.15.0 | Image viewing with zoom |
| flutter_cache_manager | ^3.4.1 | File caching |
| path_provider | ^2.1.5 | File system paths |
| path | ^1.9.0 | Path manipulation |
| uuid | ^4.5.1 | UUID generation |
| Package | Version | Purpose |
|---|---|---|
| flutter_test | SDK | Testing framework |
| flutter_lints | ^6.0.0 | Linting rules |
| drift_dev | ^2.27.0 | Database code generation |
| build_runner | ^2.4.15 | Code generation |
- Display: 2064x2208 pixels per eye at 90Hz (up to 120Hz)
- Field of View: 110° horizontal, 96° vertical
- Android API: Level 26 (Android 8.0 Oreo)
- UI Design: Large touch targets, high contrast, readable text at VR distances
- Performance: Optimized for 2D panel rendering in VR environment
Run all tests:
flutter testRun specific test file:
flutter test test/services/credential_service_test.dartRun static analysis:
flutter analyzeApply automatic fixes:
dart fix --applyThis project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
See LICENSE for the full license text.
Marcel Joachim Kloubert marcel@kloubert.dev
Contributions are welcome! Please ensure:
- All source files include the AGPL license header
- Code passes
flutter analyzewithout issues - New features include appropriate tests
- Documentation is updated for any API changes

