mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-06-29 10:17:16 +08:00
Experiment with advertising a ZeroConf Service
This commit is contained in:
@ -5,6 +5,7 @@ Future<void> main() async {
|
|||||||
|
|
||||||
var name = '_dartobservatory._tcp.local';
|
var name = '_dartobservatory._tcp.local';
|
||||||
name = '_gitjournal._tcp';
|
name = '_gitjournal._tcp';
|
||||||
|
name = '_bonsoirdemo._tcp';
|
||||||
final MDnsClient client = MDnsClient();
|
final MDnsClient client = MDnsClient();
|
||||||
// Start the client with default options.
|
// Start the client with default options.
|
||||||
await client.start();
|
await client.start();
|
||||||
@ -39,5 +40,9 @@ Future<void> main() async {
|
|||||||
// Use connectivity plugin to get ssid info when connected to the wifi network
|
// Use connectivity plugin to get ssid info when connected to the wifi network
|
||||||
// https://stackoverflow.com/questions/55716751/flutter-ios-reading-wifi-name-using-the-connectivity-or-wifi-plugin/55732656#55732656
|
// https://stackoverflow.com/questions/55716751/flutter-ios-reading-wifi-name-using-the-connectivity-or-wifi-plugin/55732656#55732656
|
||||||
|
|
||||||
// For Registering on Mobiles -
|
// Use the WorkManager in Android to do the syncing in the background
|
||||||
// https://pub.dev/packages/bonsoir
|
// -> This should be configurable if we want to be able to sync in the background
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// * I don't know if/how I can run an HTTP Server in the Background
|
||||||
|
// Is that still possible with current Android Versions?
|
||||||
|
258
lib/main_zeroconf.dart
Normal file
258
lib/main_zeroconf.dart
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:bonsoir/bonsoir.dart';
|
||||||
|
import 'package:device_info/device_info.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
/// Plugin's main method.
|
||||||
|
void main() => runApp(BonsoirExampleMainWidget());
|
||||||
|
|
||||||
|
/// Allows to get the Bonsoir service corresponding to the current device.
|
||||||
|
class AppService {
|
||||||
|
/// The service type.
|
||||||
|
static const String type = '_bonsoirdemo._tcp';
|
||||||
|
|
||||||
|
/// The service port (in this example we're not doing anything on that port but you should).
|
||||||
|
static const int port = 4000;
|
||||||
|
|
||||||
|
/// The cached service.
|
||||||
|
static BonsoirService _service;
|
||||||
|
|
||||||
|
/// Returns (and create if needed) the app Bonsoir service.
|
||||||
|
static Future<BonsoirService> getService() async {
|
||||||
|
if (_service != null) {
|
||||||
|
return _service;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name;
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
name = (await DeviceInfoPlugin().androidInfo).model;
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
name = (await DeviceInfoPlugin().iosInfo).localizedModel;
|
||||||
|
} else {
|
||||||
|
name = 'Flutter';
|
||||||
|
}
|
||||||
|
name += ' Bonsoir Demo';
|
||||||
|
|
||||||
|
_service = BonsoirService(name: name, type: type, port: port);
|
||||||
|
return _service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provider model that allows to handle Bonsoir broadcasts.
|
||||||
|
class BonsoirBroadcastModel extends ChangeNotifier {
|
||||||
|
/// The current Bonsoir broadcast object instance.
|
||||||
|
BonsoirBroadcast _bonsoirBroadcast;
|
||||||
|
|
||||||
|
/// Whether Bonsoir is currently broadcasting the app's service.
|
||||||
|
bool _isBroadcasting = false;
|
||||||
|
|
||||||
|
/// Returns wether Bonsoir is currently broadcasting the app's service.
|
||||||
|
bool get isBroadcasting => _isBroadcasting;
|
||||||
|
|
||||||
|
/// Starts the Bonsoir broadcast.
|
||||||
|
Future<void> start({bool notify = true}) async {
|
||||||
|
if (_bonsoirBroadcast == null || _bonsoirBroadcast.isStopped) {
|
||||||
|
_bonsoirBroadcast =
|
||||||
|
BonsoirBroadcast(service: await AppService.getService());
|
||||||
|
await _bonsoirBroadcast.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _bonsoirBroadcast.start();
|
||||||
|
_isBroadcasting = true;
|
||||||
|
if (notify) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the Bonsoir broadcast.
|
||||||
|
void stop({bool notify = true}) {
|
||||||
|
_bonsoirBroadcast?.stop();
|
||||||
|
_isBroadcasting = false;
|
||||||
|
if (notify) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
stop(notify: false);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provider model that allows to handle Bonsoir discoveries.
|
||||||
|
class BonsoirDiscoveryModel extends ChangeNotifier {
|
||||||
|
/// The current Bonsoir discovery object instance.
|
||||||
|
BonsoirDiscovery _bonsoirDiscovery;
|
||||||
|
|
||||||
|
/// Contains all discovered (and resolved) services.
|
||||||
|
final List<ResolvedBonsoirService> _resolvedServices = [];
|
||||||
|
|
||||||
|
/// The subscription object.
|
||||||
|
StreamSubscription<BonsoirDiscoveryEvent> _subscription;
|
||||||
|
|
||||||
|
/// Creates a new Bonsoir discovery model instance.
|
||||||
|
BonsoirDiscoveryModel() {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all discovered (and resolved) services.
|
||||||
|
List<ResolvedBonsoirService> get discoveredServices =>
|
||||||
|
List.of(_resolvedServices);
|
||||||
|
|
||||||
|
/// Starts the Bonsoir discovery.
|
||||||
|
Future<void> start() async {
|
||||||
|
if (_bonsoirDiscovery == null || _bonsoirDiscovery.isStopped) {
|
||||||
|
_bonsoirDiscovery =
|
||||||
|
BonsoirDiscovery(type: (await AppService.getService()).type);
|
||||||
|
await _bonsoirDiscovery.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _bonsoirDiscovery.start();
|
||||||
|
_subscription = _bonsoirDiscovery.eventStream.listen(_onEventOccurred);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the Bonsoir discovery.
|
||||||
|
void stop() {
|
||||||
|
_subscription?.cancel();
|
||||||
|
_subscription = null;
|
||||||
|
_bonsoirDiscovery?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Triggered when a Bonsoir discovery event occurred.
|
||||||
|
void _onEventOccurred(BonsoirDiscoveryEvent event) {
|
||||||
|
if (event.service == null || !event.isServiceResolved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == BonsoirDiscoveryEventType.DISCOVERY_SERVICE_RESOLVED) {
|
||||||
|
_resolvedServices.add(event.service);
|
||||||
|
notifyListeners();
|
||||||
|
} else if (event.type == BonsoirDiscoveryEventType.DISCOVERY_SERVICE_LOST) {
|
||||||
|
_resolvedServices.remove(event.service);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
stop();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows to switch the app broadcast state.
|
||||||
|
class BroadcastSwitch extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
BonsoirBroadcastModel model = context.watch<BonsoirBroadcastModel>();
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => _onTap(model),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('Broadcast'.toUpperCase()),
|
||||||
|
Switch(
|
||||||
|
value: model.isBroadcasting,
|
||||||
|
onChanged: (value) => _onTap(model),
|
||||||
|
activeColor: Colors.white,
|
||||||
|
activeTrackColor: Colors.white54,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Triggered when the widget has been tapped on.
|
||||||
|
void _onTap(BonsoirBroadcastModel model) {
|
||||||
|
if (model.isBroadcasting) {
|
||||||
|
model.stop();
|
||||||
|
} else {
|
||||||
|
model.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows to display all discovered services.
|
||||||
|
class ServiceList extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
BonsoirDiscoveryModel model = context.watch<BonsoirDiscoveryModel>();
|
||||||
|
List<ResolvedBonsoirService> discoveredServices = model.discoveredServices;
|
||||||
|
if (discoveredServices.isEmpty) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Found no service of type "${AppService.type}".',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black54,
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: discoveredServices.length,
|
||||||
|
itemBuilder: (context, index) =>
|
||||||
|
_ServiceWidget(service: discoveredServices[index]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows to display a discovered service.
|
||||||
|
class _ServiceWidget extends StatelessWidget {
|
||||||
|
/// The discovered service.
|
||||||
|
final ResolvedBonsoirService service;
|
||||||
|
|
||||||
|
/// Creates a new service widget.
|
||||||
|
const _ServiceWidget({
|
||||||
|
@required this.service,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => ListTile(
|
||||||
|
title: Text(service.name),
|
||||||
|
subtitle: Text(
|
||||||
|
'Type : ${service.type}, ip : ${service.ip}, port : ${service.port}'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows to display the app title based on how many services have been discovered.
|
||||||
|
class TitleWidget extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
int count =
|
||||||
|
context.watch<BonsoirDiscoveryModel>().discoveredServices.length;
|
||||||
|
return Text(count == 0 ? 'Bonsoir app demo' : 'Found $count service(s)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main widget.
|
||||||
|
class BonsoirExampleMainWidget extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => MultiProvider(
|
||||||
|
providers: [
|
||||||
|
ChangeNotifierProvider<BonsoirBroadcastModel>(
|
||||||
|
create: (context) => BonsoirBroadcastModel()),
|
||||||
|
ChangeNotifierProvider<BonsoirDiscoveryModel>(
|
||||||
|
create: (context) => BonsoirDiscoveryModel()),
|
||||||
|
],
|
||||||
|
builder: (context, child) => MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: TitleWidget(),
|
||||||
|
actions: [BroadcastSwitch()],
|
||||||
|
centerTitle: false,
|
||||||
|
),
|
||||||
|
body: ServiceList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
16
pubspec.lock
16
pubspec.lock
@ -64,6 +64,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
bonsoir:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: bonsoir
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2+2"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -588,6 +595,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.3"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.4"
|
||||||
node_interop:
|
node_interop:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -713,7 +727,7 @@ packages:
|
|||||||
name: provider
|
name: provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "4.3.2+2"
|
||||||
pub_cache:
|
pub_cache:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -28,7 +28,7 @@ dependencies:
|
|||||||
fimber: ^0.3.0
|
fimber: ^0.3.0
|
||||||
dynamic_theme: ^1.0.0
|
dynamic_theme: ^1.0.0
|
||||||
flutter_staggered_grid_view: ^0.3.0
|
flutter_staggered_grid_view: ^0.3.0
|
||||||
provider: ^3.2.0
|
provider: ^4.3.2+2
|
||||||
git_bindings: ^0.0.15
|
git_bindings: ^0.0.15
|
||||||
dart_git:
|
dart_git:
|
||||||
git: https://github.com/GitJournal/dart_git.git
|
git: https://github.com/GitJournal/dart_git.git
|
||||||
@ -57,6 +57,7 @@ dependencies:
|
|||||||
flutter_plugin_android_lifecycle: ^1.0.8 # for fixing the build
|
flutter_plugin_android_lifecycle: ^1.0.8 # for fixing the build
|
||||||
timeago: ^2.0.27
|
timeago: ^2.0.27
|
||||||
multicast_dns: ^0.2.2
|
multicast_dns: ^0.2.2
|
||||||
|
bonsoir: ^0.1.2+2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_launcher_icons: "^0.7.2"
|
flutter_launcher_icons: "^0.7.2"
|
||||||
|
Reference in New Issue
Block a user