Proxync is a Flutter file-sharing app built for local network transfers. It uses Riverpod for state management, Bonsoir for device discovery, and a socket-based transfer flow for sending and receiving files between nearby devices.
The app is organized around three main user flows:
- Send files to nearby devices on the same network.
- Receive incoming transfer requests and save files locally.
- Review sent and received transfer history.
The current UI is split into three areas:
- Send
- Receive
- History
- Flutter SDK 3.10.8 or newer
- Dart 3.10.x
- Android Studio, VS Code, or another Flutter-compatible editor
- A physical Android or iOS device on the same Wi-Fi network for local transfer testing
Run the following command from the project root:
flutter pub getVerify that Flutter can see your device or emulator:
flutter devicesRun the application on the selected device:
flutter runIf you want to target a specific device, use the device id from flutter devices:
flutter run -d <device_id>For discovery and file access to work correctly on Android, grant the requested permissions when the app prompts you. Depending on your device and Android version, this may include:
- Location
- Nearby Wi-Fi devices
- Storage or file access permissions
- Manage external storage for saving files to the public Downloads folder
Both devices must be on the same local Wi-Fi network for discovery and transfer to work.
The app starts from lib/main.dart, where ProviderScope wraps the app so Riverpod providers can manage shared state.
Route generation is handled by AppRoutes.generateRoute, and the home route loads AppInitializer, which starts the background services before showing the main navigation shell.
The Send tab lets the user pick files through the file picker service and shows nearby devices discovered on the network.
When a device is selected, FileSenderService.sendFile():
- connects to the target IP and port,
- sends metadata first,
- waits for an accept or decline response,
- streams the file in chunks if accepted,
- reports progress back to the UI.
The Receive tab keeps the device discoverable and shows pending incoming requests.
When a transfer request arrives, TransferService:
- accepts the socket connection,
- decodes the metadata message,
- adds the request to the incoming transfer state,
- waits for the user to accept or decline,
- writes the file to storage when accepted,
- updates transfer history after completion or failure.
The History tab shows a grouped timeline of sent and received transfers. History entries are stored with SharedPreferences, so they persist across app restarts.
Proxync follows a feature-first architecture with a shared core layer.
The lib/core directory contains reusable app infrastructure:
route/for route generationservices/for network, transfer, file, and device utilitiesproviders/for shared Riverpod providersmodels/for data structures used across featureswidgets/for app-wide UI building blocks
Each feature lives under lib/features:
- send/ handles file selection and nearby device sending
- receive/ handles incoming transfer requests and acceptance
- history/ stores and renders transfer history
Riverpod is used to keep state isolated and reactive. Providers manage:
- nearby device discovery
- selected files
- incoming transfer requests
- device metadata
- transfer history
File: lib/core/services/device_info_service.dart
Responsibility: fetch the local device name and Wi-Fi IP address.
Key methods:
getDeviceName()returns a readable device label.getIpAddress()returns the current Wi-Fi IP address.
File: lib/core/services/network_discovery_services.dart
Responsibility: discover nearby devices using Bonsoir service broadcast and discovery.
Key methods:
startListening()initializes discovery and broadcasting._startBroadcast()advertises the current device on_proxync._tcp._startDiscovery()listens for Bonsoir discovery events._registerService()adds resolved devices to the local cache._removeService()removes devices that are no longer available.stopListening()stops discovery, clears the cache, and resets state.
File: lib/core/services/transfer_service.dart
Responsibility: accept incoming socket connections and save received files.
Key methods:
startServer()binds the local server socket on port45678._handleConnection()processes metadata and file payloads from a client.acceptTransfer()confirms the request, receives the file stream, and writes it to storage._finalizeFile()closes the sink, updates saved path, and records successful history.declineTransfer()sends a decline response and cleans up the session.stopServer()shuts down all active sessions and closes the socket server._getProxyncDirectory()resolves the save location for received files._tryGetPublicDownloadDir()attempts to save into the publicDownloads/Proxyncfolder on Android.
File: lib/core/services/file_sender_service.dart
Responsibility: send metadata and file bytes to a peer over a socket.
Key methods:
sendFile()connects to the target peer, sends metadata, waits for approval, and streams file data.cancel()aborts an in-progress transfer.
Responsibility: open the file picker and return the selected file.
Used by the file picker provider to build the Send tab file list.
File: lib/features/history/provider/history_provider.dart
Responsibility: track transfer history and persist it locally.
Key methods:
addSent()records a sent transfer.addReceived()records a received transfer.clearAll()removes all stored history.
File: lib/features/receive/provider/incoming_transfer_provider.dart
Responsibility: manage the queue of incoming transfer requests.
Key methods:
addRequest()adds a new incoming request.removeRequest()removes a request from the list.updateStatus()updates the transfer state.updateProgress()updates progress during file reception.setSavedPath()stores the final file path.
lib/
core/
models/
providers/
route/
services/
widgets/
features/
send/
receive/
history/
settings/
- The app is designed for local network sharing, not internet-based transfers.
- File reception is optimized for Android storage behavior and may fall back to app storage when public Downloads access is not available.
- Nearby device discovery depends on both devices being visible on the same network.
flutter pub get
flutter run
flutter test
flutter build apk