nearby_service
निकटवर्ती सेवा फ्लटर प्लगइन को पी2पी नेटवर्क में कनेक्शन बनाने के लिए उपयोग किया जाता है। टेक्स्ट संदेश और फ़ाइलें भेजने का समर्थन करता है।
P2P नेटवर्क में कनेक्शन बनाने के लिए पास की सेवा Flutter प्लगइन।
{"sdk":"flutter"}^2.1.2^2.0.2{"sdk":"flutter"}^3.0.1यह अंग्रेज़ी मूल स्नैपशॉट है। नवीनतम सामग्री GitHub पर देखें।
logo
Xenikii Website LICENSE BSD Pub package Pub Likes
Nearby Service Flutter Plugin is used to create connections in a P2P network. The plugin supports sending text messages and files. With it, you can easily create any kind of information sharing application without Internet connection.
The package does not support communication between Android and IOS/macOS (Darwin) devices, the connection is available for Android-Android and Darwin-Darwin relations.
Your feedback and suggestions would be greatly appreciated! You can leave your opinion here
Watch a quick demonstration in MP4 format:
Or find the GIF demo at the end of the README:
A peer-to-peer (P2P) network is a decentralized network architecture in which each participant, called a peer, can act as both a client and a server. This means that peer-to-peer networks can exchange resources such as files, data or services directly with each other without needing a central authority or server.
For Android, Wi-fi Direct is used as a P2P network.
It is implemented through the android.net.wifi.p2p module.
It requires permissions for ACCESS_FINE_LOCATION and NEARBY_WIFI_DEVICES (for Android 13+).
It is also important that you need Wi-fi enabled on your device to use it.
To check permissions and Wi-fi status, the
nearby_service plugin includes a set of methods:
checkWifiService() (Android only): returns true if Wi-fi is enabledrequestPermissions() (Android only): requests permissions for location and nearby devices and returns true if
grantedopenServicesSettings(): opens Wi-fi settings for Android and general settings for iOS and macOSTesting the plugin is not possible on Android emulators, as they usually do not contain the Wi-fi Direct function in general! Use physical devices for that.
For IOS and macOS, the P2P connection is implemented through the Multipeer Connectivity framework.
This framework automatically selects the best network technology depending on the situation—using Wi-Fi if both devices are on the same network, or using peer-to-peer Wi-Fi or Bluetooth otherwise.
The module will work even with Wi-fi turned off (via Bluetooth), but you can still use openServicesSettings() from
the nearby_service plugin to open the settings and prompt the user to turn Wi-fi on.
Device Preparation
Connecting to the Device from a P2P Network
The Data Transmission Channel
Typed Data Exchange
All necessary Android permissions are already in the AndroidManifest.xml of the plugin, so you don't need to add anything to work with p2p network.
For IOS and macOS, you need to add the following values to Info.plist:
<key>NSBonjourServices</key>
<array>
<string>_mp-connection._tcp</string>
</array>
<key>UIRequiresPersistentWiFi</key>
<true/>
<key>NSLocalNetworkUsageDescription</key>
<string>[Your description here]</string>
For macOS, you must have the following in Release.entitlements and DebugProfile.entitlements:
...
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
...
NSBonjourServices
For the nearby_service plugin, you should add the value <string>_mp-connection._tcp</string>. This key is
required for Multipeer Connectivity. It defines the Bonjour services
that your application will look for. Bonjour is Apple's implementation of zero-configuration networking, which allows
devices on a LAN to discover each other and establish connections without requiring manual configuration.
UIRequiresPersistentWiFi
This key is not strictly required for Multipeer Connectivity to work. This key is used to indicate that your application requires a persistent Wi-Fi connection even when the screen is off. This can be useful for maintaining a network connection for continuous data transfer.
NSLocalNetworkUsageDescription
This key is needed to inform users why your application will use the local network. You must provide a string value that explains why your application needs LAN access.
Note that if you want to use the plugin to send files, you also need to follow the instructions about permissions of the filesystem management package you are using.
The full example demonstrating the functionality can be viewed at the link.
Important note: for functionality that is only available for one of the platforms, you should use the corresponding API element from
NearbyService.For Android:
final _nearbyService = NearbyService.getInstance(); _nearbyService.android..For IOS and macOS:
final _nearbyService = NearbyService.getInstance(); _nearbyService.darwin..
import 'package:nearby_service/nearby_service.dart';
// getInstance() returns an instance for the current platform.
final _nearbyService = NearbyService.getInstance();
// You can change the device name on a P2P network only for iOS.
// Optionally pass the [darwinDeviceName].
await _nearbyService.initialize(
data: NearbyInitializeData(darwinDeviceName: darwinDeviceName),
);
Extra step for Android: ask for permissions and make sure Wi-fi is enabled:
final granted = await _nearbyService.android?.requestPermissions();
if (granted ?? false) {
// go to the checking Wi-fi step
}
final isWifiEnabled = await _nearbyService.android?.checkWifiService();
if (isWifiEnabled ?? false) {
// go to the starting discovery step
}
Extra step for IOS and macOS: ask the user to choose whether they are a Browser or Advertiser:
In IOS and macOS Multipeer Connectivity, there are 2 roles for the discovery process and connection between devices: browser and advertiser.
Browser: This component discovers nearby devices that report their availability. It is responsible for finding peers that advertise themselves and inviting them to join the shared session.
Advertiser: This component advertises the availability of the device to nearby peers. It is used to let know the browser that the device is available for inviting and connecting.
The code for selecting a role:
_nearbyService.darwin?.setIsBrowser(value: isBrowser);
// go to the starting discovery step
final result = await _nearbyService.discover();
if (result) {
// go to the listening peers step
}
_nearbyService.getPeersStream().listen((event) => peers = event);
NearbyDevice and you can connect to it:Remember that when used on the Android platform, you can only pass
NearbyAndroidDeviceto theconnect()method. Similarly for iOS and macOS,NearbyDarwinDevice. Devices automatically come from the discovery state in the correct type, so you just need to use the received data.
final result = await _nearbyService.connect(device);
if (result) {
// go the the listening the device step
}
null comes from the
stream, it means the devices lost connection._connectedDeviceSubscription = _nearbyService.getConnectedDeviceStream(device).listen(
(event) async {
final wasConnected = connectedDevice?.status.isConnected ?? false;
final nowConnected = event?.status.isConnected ?? false;
if (wasConnected && !nowConnected) {
// return to the discovery state
}
connectedDevice = event;
},
);
NearbyService, for iOS and macOS it's a setup to listen to messages and
resources from the desired device. There is a method startCommunicationChannel() for this purpose. You should pass
to it listeners for messages and resources received from the connected device. There can only be one communication
channel, if you create a new one, the previous one will be cancelled.final messagesListener = NearbyServiceMessagesListener(
onData: (message) {
// handle the message from NearbyServiceMessagesListener
},
);
final filesListener = NearbyServiceFilesListener(
onData: (pack) async {
// handle the files pack from NearbyServiceFilesListener
},
);
await _nearbyService.startCommunicationChannel(
NearbyCommunicationChannelData(
connectedDevice.info.id,
messagesListener: messagesListener,
filesListener: filesListener,
),
);
send() for that:_nearbyService.send(
OutgoingNearbyMessage(
content: NearbyMessageTextRequest.create(value: message),
receiver: connectedDevice.info,
),
);
This is a very important topic for the nearby_service plugin because it provides unique functionality for sharing
typed data.
First, it's essential to say that when you send a message, you are required to pass the OutgoingNearbyMessage model.
This contains the receiver - the recipient to whom the message is addressed. The receiver is someone with whom you
already have an established communication channel. Also OutgoingNearbyMessage contains a content field, it can be
one of 4 types:
NearbyMessageTextRequestNearbyMessageTextResponseNearbyMessageFilesRequestNearbyMessageFilesResponseWe will talk more about them later.
Second, there is another type of message, ReceivedNearbyMessage. This is what comes to you from
the NearbyServiceMessagesListener. It contains a sender field so that you can identify who the message came from and
use that information.
Description of the operating logic:
send() method:// DEVICE A
_nearbyService.send(
OutgoingNearbyMessage(
content: NearbyMessageTextRequest.create(value: message),
receiver: connectedDevice.info,
),
);
ReceivedNearbyMessage with content cast as NearbyMessageTextRequest from the
NearbyServiceMessagesListener.// DEVICE B
final messagesListener = NearbyServiceMessagesListener(
onData: (message) {
// message is ReceivedNearbyMessage with content cast as NearbyMessageTextRequest here
},
);
NearbyMessageTextResponse. You
need to add the id from the received NearbyMessageTextRequest to it:// DEVICE B
_nearbyService.send(
OutgoingNearbyMessage(
receiver: connectedDevice.info,
content: NearbyMessageTextResponse(id: requestId),
),
);
NearbyMessageTextResponse from
their NearbyServiceMessagesListener:// DEVICE A
final messagesListener = NearbyServiceMessagesListener(
onData: (message) {
// message is ReceivedNearbyMessage with content cast as NearbyMessageTextResponse here
},
);
Throughout the process, you can use the
ids of the messages to identify them.
Description of the operating logic:
NearbyMessageFilesRequest using the send() method:// DEVICE A
_nearbyService.send(
OutgoingNearbyMessage(
// files here is List<NearbyFileInfo>, it can be easily created from the file path: NearbyFileInfo(path: file.path)
content: NearbyMessageFilesRequest.create(files: files),
receiver: connectedDevice.info,
),
);
ReceivedNearbyMessage with content cast as NearbyMessageFilesRequest from the
NearbyServiceMessagesListener.The file request comes from the
NearbyServiceMessagesListenerbecause it doesn't contain the transferred files, only the request to send them! For files, confirmation by the other party before starting the data transfer is required!
// DEVICE B
final messagesListener = NearbyServiceMessagesListener(
onData: (message) {
// message is ReceivedNearbyMessage with content cast as NearbyMessageFilesRequest here
},
);
NearbyMessageFilesResponse content. The NearbyMessageFilesResponse contains the isAccepted field,
which will determine whether the file sending will start or not.If you don't want to use confirmation logic to send files, just send the automatic positive responses to
NearbyMessageFilesRequestinNearbyServiceMessagesListener.
// DEVICE B
_nearbyService.send(
OutgoingNearbyMessage(
receiver: connectedDevice!.info,
content: NearbyMessageFilesResponse(
id: request.id,
isAccepted: isAccepted,
),
),
);
NearbyMessageFilesResponse in their NearbyServiceMessagesListener and can notify the
user.// DEVICE A
final messagesListener = NearbyServiceMessagesListener(
onData: (message) {
// message is ReceivedNearbyMessage with content cast as NearbyMessageFilesResponse here
},
);
At this time under the hood, the sending of files will begin and the recipient will receive ReceivedNearbyFilesPack in
the NearbyServiceFilesListener when sending is complete. ReceivedNearbyFilesPack holds the paths of the received
files already stored on the device's temporary directory. You can overwrite them to the desired location, delete them,
or do anything else you like.
// DEVICE B
final filesListener = NearbyServiceFilesListener(
onData: (pack) async {
// pack is ReceivedNearbyFilesPack here
},
);
NearbyService includes custom errors that you can catch in your implementation.
Common exceptions See here:
NearbyServiceUnsupportedPlatformException: Usage of the plugin on an unsupported platformNearbyServiceUnsupportedDecodingException: Error decoding messages from native platform to Dart (open an issue if
this happens)NearbyServiceInvalidMessageException: An attempt to send an invalid message on the sender's side. Add content
validation to your messagesExceptions that can be caught from the discover(), stopDiscovery(), connect(), and disconnect() methods for
the Android platform
See here:
NearbyServiceBusyException: The Wi-Fi P2P framework is currently busy. Usually this means that you have sent a
request to some device and now one of the peers is CONNECTINGNearbyServiceP2PPUnsupportedException: Wi-Fi P2P is not supported on this deviceNearbyServiceNoServiceRequestsException: No service discovery requests have been made. Ensure that you have
initiated a service discovery request before attempting to connectNearbyServiceGenericErrorException: A generic error occurred. This could be due to various reasons such as hardware
issues, Wi-Fi being turned off, or temporary issues with the Wi-Fi P2P frameworkNearbyServiceUnknownException: An unknown error occurred. Please check the device's Wi-Fi P2P settings and ensure
the device supports Wi-Fi P2PEach NearbyDevice contains NearbyDeviceInfo that presents different meanings depending on the platform. For
Android id is the MAC address of the device, and for iOS it is the unique identifier of its MCPeerID. In general, id
identifies the device
in its platform.
Use the getter communicationChannelState of NearbyService to know the state of the communication channel. It may
be:
enum CommunicationChannelState {
notConnected,
loading,
connected;
bool get isNotConnected => this == CommunicationChannelState.notConnected;
bool get isLoading => this == CommunicationChannelState.loading;
bool get isConnected => this == CommunicationChannelState.connected;
}
The getter is ValueListenable so you can listen to its state.
There are methods getPlatformVersion() and getPlatformModel() of NearbyService to determine the version and
model of the device respectively. You can use them to specify a
name for the IOS.
Use getCurrentDeviceInfo() of NearbyService to get information about the current device on the P2P network. Note
that the ID obtained from getCurrentDeviceInfo() for Android will always be 02:00:00:00:00:00 for privacy
issues.
For Android, it is possible to listen to the current state of the connection using
method getConnectionInfoStream of NearbyAndroidService.
When you are creating a service with getInstance(), you can define the logging level in the plugin using
field logLevel.
Possible logging levels:
enum NearbyServiceLogLevel {
debug,
info,
error,
disabled,
}
android_demo
IOS_demo
Version 0.2.0 adds support for macOS but introduces breaking changes to accommodate the unified Darwin platform (iOS and macOS).
Property Renaming
.ios property has been renamed to .darwin// Before (v0.1.0)
_nearbyService.ios?.setIsBrowser(value: isBrowser);
// After (v0.2.0)
_nearbyService.darwin?.setIsBrowser(value: isBrowser);
Class Renaming
NearbyIOSDevice has been renamed to NearbyDarwinDeviceNearbyIOSService has been renamed to NearbyDarwinServiceNearbyServiceIOSExceptionMapper has been renamed to NearbyServiceDarwinExceptionMapperParameter Renaming in NearbyInitializeData
iosDeviceName has been renamed to darwinDeviceName// Before (v0.1.0)
await _nearbyService.initialize(
data: NearbyInitializeData(iosDeviceName: deviceName),
);
// After (v0.2.0)
await _nearbyService.initialize(
data: NearbyInitializeData(darwinDeviceName: deviceName),
);
If you have assertions in your code that check for specific types, make sure to update them:
// Before (v0.1.0)
assert(
device is NearbyIOSDevice,
'The Nearby IOS Service can only work with the NearbyIOSDevice'
);
// After (v0.2.0)
assert(
device is NearbyDarwinDevice,
'The Nearby Darwin Service can only work with the NearbyDarwinDevice'
);
If you're developing a plugin based on nearby_service, note that the pubspec.yaml now uses sharedDarwinSource: true to share code between iOS and macOS platforms.