mdns_bloc 0.3.0 copy "mdns_bloc: ^0.3.0" to clipboard
mdns_bloc: ^0.3.0 copied to clipboard

Bloc wrapper over 'multicast_dns' which performs service discovery over multicast DNS (mDNS), Bonjour and Avahi.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mdns_bloc/mdns_bloc.dart';

void main() {
  runApp(const MyApp());
}

/// The service instance the example highlights when it is discovered.
const String _exampleService = 'example._http._tcp.local';

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'mdns_bloc example',
      home: BlocProvider(
        create: (_) => MDnsBloc()
          ..add(const MDnsEventStartSearch(
            serviceType: defaultServiceType,
            serviceName: _exampleService,
          )),
        child: const MDnsSearchPage(),
      ),
    );
  }
}

class MDnsSearchPage extends StatelessWidget {
  const MDnsSearchPage({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<MDnsBloc, MDnsState>(
      builder: (BuildContext context, MDnsState state) {
        final bool searching = state.status == MDnsStatus.initial ||
            state.status == MDnsStatus.searching;
        return Scaffold(
          appBar: AppBar(
            title: Text(_title(state.status)),
            actions: <Widget>[
              if (searching)
                IconButton(
                  icon: const Icon(Icons.stop),
                  tooltip: 'Stop searching',
                  onPressed: () =>
                      context.read<MDnsBloc>().add(const MDnsEventStopSearch()),
                )
              else
                IconButton(
                  icon: const Icon(Icons.refresh),
                  tooltip: 'Search again',
                  onPressed: () => context.read<MDnsBloc>().add(
                        const MDnsEventStartSearch(
                          serviceType: defaultServiceType,
                          serviceName: _exampleService,
                        ),
                      ),
                ),
            ],
          ),
          body: SafeArea(
            child: Padding(
              padding: const EdgeInsets.all(12),
              child: _MDnsSearchBody(state: state),
            ),
          ),
        );
      },
    );
  }

  String _title(MDnsStatus status) {
    switch (status) {
      case MDnsStatus.initial:
      case MDnsStatus.searching:
        return 'mDNS Scanning';
      case MDnsStatus.noneFound:
      case MDnsStatus.found:
      case MDnsStatus.matched:
        return 'mDNS Scanning Complete';
      case MDnsStatus.stopped:
        return 'mDNS Scanning Stopped';
      case MDnsStatus.error:
        return 'Error mDNS Scanning';
    }
  }
}

class _MDnsSearchBody extends StatelessWidget {
  const _MDnsSearchBody({required this.state});

  final MDnsState state;

  @override
  Widget build(BuildContext context) {
    switch (state.status) {
      case MDnsStatus.initial:
        return const _Spinner();
      case MDnsStatus.searching:
        // Results stream into the state while the search runs; show them as
        // they arrive, with a progress bar while the scan is still going.
        if (state.services.isEmpty) {
          return const _Spinner();
        }
        return Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            const LinearProgressIndicator(),
            Expanded(child: _ServiceList(state: state)),
          ],
        );
      case MDnsStatus.noneFound:
        return const Center(child: Text('No services found'));
      case MDnsStatus.stopped:
        if (state.services.isEmpty) {
          return const Center(child: Text('Search stopped'));
        }
        return _ServiceList(state: state);
      case MDnsStatus.found:
      case MDnsStatus.matched:
        return _ServiceList(state: state);
      case MDnsStatus.error:
        return Text(
          'There was an error in scanning: ${state.errorMessage}\n\n'
          'Final state was: $state',
        );
    }
  }
}

class _Spinner extends StatelessWidget {
  const _Spinner();

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          SizedBox(
            width: 60,
            height: 60,
            child: CircularProgressIndicator(),
          ),
          Padding(
            padding: EdgeInsets.only(top: 16),
            child: Text('Searching...'),
          ),
        ],
      ),
    );
  }
}

class _ServiceList extends StatelessWidget {
  const _ServiceList({required this.state});

  final MDnsState state;

  @override
  Widget build(BuildContext context) {
    final List<MDnsService> services = state.services;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.all(12),
          child: Text(
            'Detected ${services.length} '
            'service${services.length == 1 ? '' : 's'}:',
          ),
        ),
        Expanded(
          child: ListView.builder(
            itemCount: services.length,
            itemBuilder: (BuildContext context, int index) {
              final MDnsService service = services[index];
              final String addressText = service.addresses.isEmpty
                  ? 'no address resolved'
                  : service.addresses
                      .map((InternetAddress address) => address.address)
                      .join(', ');
              final String txtText = service.txt.join('; ');
              final String subtitle = txtText.isEmpty
                  ? '$addressText — port ${service.port}'
                  : '$addressText — port ${service.port}\n$txtText';
              return ListTile(
                title: Text(service.name),
                subtitle: Text(subtitle),
                isThreeLine: txtText.isNotEmpty,
                tileColor: service == state.match ? Colors.lightBlue : null,
              );
            },
          ),
        ),
      ],
    );
  }
}
2
likes
160
points
126
downloads

Documentation

API reference

Publisher

verified publishersleepasloth.com

Weekly Downloads

Bloc wrapper over 'multicast_dns' which performs service discovery over multicast DNS (mDNS), Bonjour and Avahi.

Repository (GitHub)
View/report issues

Topics

#mdns #bonjour #avahi #bloc #service-discovery

License

MIT (license)

Dependencies

bloc, bloc_concurrency, equatable, meta, multicast_dns

More

Packages that depend on mdns_bloc