flutter_hue 2.1.1 copy "flutter_hue: ^2.1.1" to clipboard
flutter_hue: ^2.1.1 copied to clipboard

An SDK designed for the Flutter framework that enables developers to easily integrate Philips Hue smart devices into their applications.

example/lib/main.dart

import 'dart:async';

import 'package:app_links/app_links.dart';
import 'package:example/stream_demos/stream_demos_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hue/flutter_hue.dart';
import 'package:radio_group_v2/radio_group_v2.dart';

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

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

  // TODO: Replace with your client ID
  static const String clientId = '[clientId]';

  // TODO: Replace with your client secret
  static const String clientSecret = '[clientSecret]';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomePage(
        clientId: clientId,
        clientSecret: clientSecret,
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({
    super.key,
    required this.clientId,
    required this.clientSecret,
  });

  final String clientId;

  final String clientSecret;

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  static const double padding = 15.0;

  /// Whether or not the page is loading some async action.
  bool isLoading = false;

  /// The IP address of the bridges on the network.
  final List<String> bridgeIps = [];

  /// Bridges that have already been connected to in the past.
  final List<Bridge> oldBridges = [];

  /// Controls the radio group of bridge IP addresses.
  final RadioGroupController<String> ipGroupController = RadioGroupController();

  /// Controls the radio group of old bridges.
  final RadioGroupController<Bridge> oldBridgeGroupController =
      RadioGroupController();

  /// Controls the bridge discovery process.
  final DiscoveryTimeoutController timeoutController =
      DiscoveryTimeoutController(timeoutSeconds: 25);

  /// Cancels the "first contact" action.
  VoidCallback? onContactCancel;

  /// The bridge that [firstContact] decided to connect with.
  Bridge? bridge;

  /// All of the Philips Hue resources connected to [bridge].
  HueNetwork? hueNetwork;

  /// The light that is being worked with in the "writing data" section.
  Light? light;

  /// Watches for deep links.
  late final StreamSubscription deepLinkStream;

  @override
  void initState() {
    super.initState();

    deepLinkStream = AppLinks().uriLinkStream.listen(
      (Uri? uri) {
        if (uri == null) return;

        final int start = uri.toString().indexOf('?');
        String queryParams = uri.toString().substring(start);
        Uri truncatedUri = Uri.parse(queryParams);

        try {
          final String? pkce = truncatedUri.queryParameters[ApiFields.pkce];
          final String? code = truncatedUri.queryParameters[ApiFields.code];
          final String? resState =
              truncatedUri.queryParameters[ApiFields.state];

          // Handle Flutter Hue deep link
          if (pkce != null && code != null && resState != null) {
            String stateSecret;
            if (resState.contains('-')) {
              stateSecret = resState.substring(0, resState.indexOf('-'));
            } else {
              stateSecret = resState;
            }

            TokenRepo.fetchRemoteToken(
              clientId: widget.clientId,
              clientSecret: widget.clientSecret,
              pkce: pkce,
              code: code,
              stateSecret: stateSecret,
              decrypter: (ciphertext) =>
                  ciphertext.substring(4, ciphertext.length - 4),
            );
          }
        } catch (_) {
          // Do nothing
        }
      },
    );

    // Initialize Flutter Hue and keep all of the locally stored data up to
    // date.
    FlutterHueMaintenanceRepo.maintain(
      clientId: widget.clientId,
      clientSecret: widget.clientSecret,
      redirectUri: 'flutterhue://auth',
      deviceName: 'TestDevice',
      stateEncrypter: (plaintext) => 'abcd${plaintext}1234',
    );

    // Fetch all of the bridges that have been connected to in the past.
    BridgeDiscoveryRepo.fetchSavedBridges().then(
      (bridges) {
        if (bridges.isNotEmpty) {
          setState(() {
            oldBridges.clear();
            oldBridges.addAll(bridges);
            bridge = oldBridges.first;
          });
        }
      },
    );
  }

  @override
  void dispose() {
    deepLinkStream.cancel();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: doSomething,
          icon: const Icon(Icons.science),
        ),
        title: const Text('Flutter Hue'),
        actions: isLoading
            ? [
                const Padding(
                  padding: EdgeInsets.only(right: padding),
                  child: Row(
                    children: [
                      Text('Loading... '),
                      Icon(Icons.query_builder),
                    ],
                  ),
                ),
              ]
            : null,
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          child: Column(
            children: [
              const SizedBox(height: padding),

              sectionHeader('Getting Started'),

              // DISCOVER BRIDGES
              Visibility(
                visible: oldBridges.isNotEmpty,
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: padding),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text('Previously connected bridge'
                          '${oldBridges.length == 1 ? '' : 's'}'),
                      RadioGroup(
                        controller: oldBridgeGroupController,
                        values: oldBridges,
                        indexOfDefault: oldBridges.isEmpty ? -1 : 0,
                        orientation: RadioGroupOrientation.horizontal,
                        onChanged: (value) {
                          setState(() {
                            ipGroupController.setValueSilently(null);
                            bridge = value;
                            hueNetwork = null;
                            light = null;
                          });
                        },
                        labelBuilder: (value) => Text(value.ipAddress ?? ''),
                      ),
                    ],
                  ),
                ),
              ),

              const SizedBox(height: padding * 2),

              ElevatedButton(
                onPressed: discoverBridges,
                child: const Text('Discover Bridges'),
              ),
              Visibility(
                visible: bridgeIps.isNotEmpty,
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: padding),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text('Found ${bridgeIps.length} bridge IP'
                          '${bridgeIps.length == 1 ? '' : 's'}'),
                      RadioGroup(
                        controller: ipGroupController,
                        values: bridgeIps,
                        indexOfDefault:
                            (bridgeIps.isEmpty || oldBridges.isNotEmpty)
                                ? -1
                                : 0,
                        orientation: RadioGroupOrientation.horizontal,
                        onChanged: (value) {
                          setState(() {
                            oldBridgeGroupController.setValueSilently(null);
                            bridge = null;
                            hueNetwork = null;
                            light = null;
                          });
                        },
                      ),
                    ],
                  ),
                ),
              ),

              const SizedBox(height: padding * 2),

              // FIRST CONTACT
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                    onPressed: ((ipGroupController.myRadioGroupKey == null ||
                                    ipGroupController.value == null) &&
                                (oldBridgeGroupController.myRadioGroupKey !=
                                        null &&
                                    oldBridgeGroupController.value != null)) ||
                            bridgeIps.isEmpty
                        ? null
                        : () => firstContact(),
                    child: const Text('First Contact'),
                  ),
                  const SizedBox(width: 11),
                  ElevatedButton(
                    onPressed: onContactCancel,
                    child: const Text('Cancel'),
                  ),
                ],
              ),

              Visibility(
                visible: onContactCancel != null,
                maintainAnimation: true,
                maintainState: true,
                maintainSize: true,
                child: const Text(
                  'Press the button on your bridge.\n'
                  'You have 25 seconds.',
                  textAlign: TextAlign.center,
                ),
              ),
              const SizedBox(height: padding * 2),

              // ESTABLISH REMOTE CONTACT
              ElevatedButton(
                onPressed: bridge == null ? null : remoteContact,
                child: const Text('Establish Remote Contact'),
              ),

              const SizedBox(height: padding * 2),

              sectionHeader('Reading Data'),

              // FETCH NETWORK
              ElevatedButton(
                onPressed: bridge == null ? null : fetchNetwork,
                child: const Text('Fetch Network'),
              ),

              const SizedBox(height: padding * 2),

              // FETCH BRIDGE
              ElevatedButton(
                onPressed: bridge == null ? null : fetchBridge,
                child: const Text('Fetch Bridge'),
              ),

              const SizedBox(height: padding * 2),

              // FETCH LIGHT
              ElevatedButton(
                onPressed: bridge == null ? null : fetchLight,
                child: const Text('Fetch Light'),
              ),

              const SizedBox(height: padding * 2),

              sectionHeader('Writing Data'),

              // IDENTIFY LIGHT
              ElevatedButton(
                onPressed: light == null ? null : identifyLight,
                child: const Text('Identify Light'),
              ),

              const SizedBox(height: padding * 2),

              // TOGGLE LIGHT ON/OFF
              ElevatedButton(
                onPressed: light == null ? null : toggleLight,
                child: const Text('Toggle Light on/off'),
              ),

              const SizedBox(height: padding * 2),

              // LIGHT COLORS
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: padding),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    // RED
                    ElevatedButton(
                      onPressed: light == null ? null : () => colorLight('red'),
                      child: const Text('Red'),
                    ),

                    // GREEN
                    ElevatedButton(
                      onPressed:
                          light == null ? null : () => colorLight('green'),
                      child: const Text('Green'),
                    ),

                    // BLUE
                    ElevatedButton(
                      onPressed:
                          light == null ? null : () => colorLight('blue'),
                      child: const Text('Blue'),
                    ),

                    // WHITE
                    ElevatedButton(
                      onPressed:
                          light == null ? null : () => colorLight('white'),
                      child: const Text('White'),
                    ),
                  ],
                ),
              ),

              const SizedBox(height: padding * 2),

              sectionHeader(
                'Entertainment Streaming',
                GestureDetector(
                  onTap: (hueNetwork == null || !isStreaming)
                      ? null
                      : () {
                          Navigator.of(context).push(
                            MaterialPageRoute(
                              builder: (_) {
                                return StreamDemosScreen(
                                  entertainmentConfiguration: hueNetwork!
                                      .entertainmentConfigurations.first,
                                );
                              },
                            ),
                          );
                        },
                  child: Text(
                    'more >',
                    style: TextStyle(
                      color: (hueNetwork == null || !isStreaming)
                          ? Colors.grey
                          : Colors.blue,
                    ),
                  ),
                ),
              ),

              Padding(
                padding: const EdgeInsets.symmetric(horizontal: padding),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    // STREAM 1
                    ElevatedButton(
                      onPressed:
                          hueNetwork == null ? null : () => startStreaming(1),
                      child: const Text('Stream 1'),
                    ),

                    // STREAM 2
                    ElevatedButton(
                      onPressed:
                          hueNetwork == null ? null : () => startStreaming(2),
                      child: const Text('Stream 2'),
                    ),

                    // STREAM 3
                    ElevatedButton(
                      onPressed:
                          hueNetwork == null ? null : () => startStreaming(3),
                      child: const Text('Stream 3'),
                    ),
                  ],
                ),
              ),

              const SizedBox(height: padding * 2),

              // STOP STREAMING
              ElevatedButton(
                onPressed:
                    (hueNetwork == null || !isStreaming) ? null : stopStreaming,
                child: const Text('Stop Streaming'),
              ),

              const SizedBox(height: padding),
            ],
          ),
        ),
      ),
    );
  }

  /// The titles and dividers that separate each group of buttons.
  Widget sectionHeader(String title, [Widget? actionBtn]) {
    return Column(
      children: [
        Row(
          children: [
            const SizedBox(width: padding),
            Text(
              title,
              style: const TextStyle(
                fontSize: padding,
                fontWeight: FontWeight.bold,
              ),
            ),
            if (actionBtn != null) const Spacer(),
            if (actionBtn != null) actionBtn,
            if (actionBtn != null) const SizedBox(width: padding),
          ],
        ),
        const Padding(
          padding: EdgeInsets.symmetric(horizontal: padding),
          child: Divider(thickness: 2.0),
        ),
      ],
    );
  }

  /// Show the IP addresses of the bridges that have been found on the network.
  void showIps(BuildContext context) {
    showDialog<void>(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('Bridge IP'),
          content: SingleChildScrollView(
            child: ListBody(
              children: bridgeIps.map((ip) => Text(ip)).toList(),
            ),
          ),
          actions: [
            TextButton(
              child: const Text('Ok'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  Future<void> doSomething() async {
    setState(() {
      isLoading = true;
    });

    // ignore: avoid_print
    print('Doing something...');

    // TODO: You can do your experiments easily here.

    // ignore: avoid_print
    print('Done!');

    setState(() {
      isLoading = false;
    });
  }

  /// Searches the network for bridges.
  ///
  /// If any are found, their IP addresses are placed in the [bridgeIps] list.
  Future<void> discoverBridges() async {
    setState(() {
      isLoading = true;
    });

    List<DiscoveredBridge> bridges =
        await BridgeDiscoveryRepo.discoverBridges();

    setState(() {
      bridgeIps.clear();
      bridgeIps.addAll(bridges.map((e) => e.ipAddress));
      isLoading = false;
    });
  }

  /// For the simplicity of this demo, this method only looks at the first
  /// bridge from the [bridgeIps] list.
  ///
  /// It attempts to establish contact with the bridge. This is when the user
  /// needs to press the button on their bridge.
  Future<void> firstContact() async {
    setState(() {
      onContactCancel = () => timeoutController.cancelDiscovery = true;
      isLoading = true;
    });

    bridge = await BridgeDiscoveryRepo.firstContact(
      bridgeIpAddr: ipGroupController.value ?? bridgeIps.first,
      controller: timeoutController,
    );

    setState(() {
      onContactCancel = null;
      isLoading = false;
    });
  }

  /// Establishes remote contact with the bridge.
  Future<void> remoteContact() async {
    setState(() {
      isLoading = true;
    });

    await BridgeDiscoveryRepo.remoteAuthRequest(
      clientId: widget.clientId,
      redirectUri: 'flutterhue://auth',
      deviceName: 'TestDevice',
      encrypter: (plaintext) => 'abcd${plaintext}1234',
    );

    setState(() {
      isLoading = false;
    });
  }

  /// Fetches all of the resources that are attached to [bridge].
  Future<void> fetchNetwork() async {
    setState(() {
      isLoading = true;
    });

    hueNetwork = HueNetwork(bridges: [bridge!]);

    await hueNetwork?.fetchAll();

    try {
      light = hueNetwork!.lights.first;
    } catch (_) {
      // Do nothing
    }

    if (hueNetwork?.hasFailedFetches == true) {
      // ignore: avoid_print
      print('WARNING — Failed to fetch all resources');
    }

    setState(() {
      isLoading = false;
    });
  }

  /// This does nothing for the demo other than to show the code that is used to
  /// fetch a bridge object from JSON.
  Future<void> fetchBridge() async {
    setState(() {
      isLoading = true;
    });

    final List<Map<String, dynamic>>? res =
        await bridge!.getResource(ResourceType.bridge);

    try {
      // ignore: unused_local_variable
      Bridge myBridge = Bridge.fromJson(res?.first ?? {});

      // Shows a way to display the info in these objects.
      // log('Bridge Json - ${JsonTool.writeJson(res?.first ?? {})}');
      // log('Bridge Object - ${JsonTool.writeJson(myBridge.toJson(optimizeFor: OptimizeFor.dontOptimize))}');
    } catch (_) {
      // res list was empty
    }

    setState(() {
      isLoading = false;
    });
  }

  /// This does nothing for the demo other than to show the code that is used to
  /// fetch a light object from JSON.
  Future<void> fetchLight() async {
    setState(() {
      isLoading = true;
    });

    final Map<String, dynamic>? res =
        (await bridge!.getResource(ResourceType.light))?.first;

    // ignore: unused_local_variable
    Light light = Light.fromJson(res ?? {});

    setState(() {
      isLoading = false;
    });
  }

  /// Causes the light to "breath" to let the user know which light they are
  /// working with.
  Future<void> identifyLight() async {
    setState(() {
      isLoading = true;
    });

    Device lightDevice;

    try {
      lightDevice = hueNetwork!.devices
          // ignore: deprecated_member_use
          .firstWhere((device) => device.metadata.name == light!.metadata.name);
    } catch (_) {
      return;
    }

    lightDevice.identifyAction = 'identify';

    await bridge!.put(lightDevice);

    setState(() {
      isLoading = false;
    });
  }

  /// Toggles [light] on and off.
  Future<void> toggleLight() async {
    setState(() {
      isLoading = true;
    });

    bool isOn = light!.isOn;

    light!.on.isOn = !isOn;

    await bridge!.put(light!);

    setState(() {
      isLoading = false;
    });
  }

  /// Changes the color of [light].
  Future<void> colorLight(String color) async {
    setState(() {
      isLoading = true;
    });

    double x;
    double y;

    if (color == 'red') {
      x = 0.6718;
      y = 0.3184;
    } else if (color == 'green') {
      x = 0.2487;
      y = 0.6923;
    } else if (color == 'blue') {
      x = 0.1121;
      y = 0.1139;
    } else {
      x = 0.3127;
      y = 0.3127;
    }

    light = light!
        .copyWith(color: light!.color.copyWith(xy: LightColorXy(x: x, y: y)));

    await bridge!.put(light!);

    setState(() {
      isLoading = false;
    });
  }

  /// Whether or not the entertainment streaming process is currently active.
  bool get isStreaming =>
      _isStreamingPattern1 || _isStreamingPattern2 || _isStreamingPattern3;

  /// Starts the entertainment streaming process.
  ///
  /// The `pattern` parameter determines which pattern to use. The patterns are
  /// as follows:
  /// * `1`: Toggle 1 light between red and blue colors.
  /// * `2`: Gently fade 1 light between red and blue colors.
  /// * `3`: Toggles 2 lights between white and off, as if the light is jumping
  ///         back and forth between the two lights.
  Future<void> startStreaming(int pattern) async {
    setState(() {
      isLoading = true;
    });

    try {
      if (!isStreaming) {
        final bool didStart = await hueNetwork!
            .entertainmentConfigurations.first
            .startStreaming(bridge!);

        if (!didStart) throw 'Failed to start stream';
      }

      // Clear out the stream queue before starting a new stream.
      hueNetwork!.entertainmentConfigurations.first.flushStreamQueue();

      if (pattern == 1) {
        await _startStreaming1();
      } else if (pattern == 2) {
        await _startStreaming2();
      } else if (pattern == 3) {
        await _startStreaming3();
      } else {
        // ignore: avoid_print
        print('Invalid pattern');
      }
    } catch (e) {
      // ignore: avoid_print
      print('Error starting stream: $e');
    }

    setState(() {
      isLoading = false;
    });
  }

  /// Whether or not the entertainment streaming process is currently active and
  /// using pattern 1.
  bool _isStreamingPattern1 = false;

  /// Starts the entertainment streaming process, with streaming pattern 1.
  ///
  /// Toggle 1 light between red and blue colors.
  ///
  /// This should cause one light to alternate between red and blue.
  ///
  /// The light will stay red or blue for 500ms then switch. This will
  /// happen for 5 seconds, then it should stop on blue.
  ///
  /// Since blue is the last state, it will still be streaming blue so
  /// the bridge doesn't drop the connection due to inactivity.
  Future<void> _startStreaming1() async {
    _isStreamingPattern1 = true;
    _isStreamingPattern2 = false;
    _isStreamingPattern3 = false;

    final ColorXy red = ColorXy.fromRgbNormalized(1.0, 0.0, 0.0, 1.0);
    final ColorXy blue = ColorXy.fromRgbNormalized(0.0, 0.0, 1.0, 1.0);

    final EntertainmentStreamCommand command1 = EntertainmentStreamCommand(
      channel: 0,
      color: red,
      waitAfterAnimation: const Duration(milliseconds: 500),
    );

    final EntertainmentStreamCommand command2 = EntertainmentStreamCommand(
      channel: 0,
      color: blue,
      waitAfterAnimation: const Duration(milliseconds: 500),
    );

    for (int i = 0; i < 5; i++) {
      // IMPORTANT NOTE: The copy method is used here to ensure that the
      // same command isn't added to the queue multiple times. If the
      // same command is added multiple times, the bridge will only
      // execute the command once.
      hueNetwork!.entertainmentConfigurations.first.addAllToStreamQueue(
        [command1.copy(), command2.copy()],
      );
    }
  }

  /// Whether or not the entertainment streaming process is currently active and
  /// using pattern 2.
  bool _isStreamingPattern2 = false;

  /// Starts the entertainment streaming process, with streaming pattern 2.
  ///
  /// Gently fade 1 light between red and blue colors.
  ///
  /// This should cause one light to fade between red and blue.
  ///
  /// The light will fade from red to blue over 500ms, then fade back to red
  /// over 500ms. This will happen for 5 seconds, then it should stop on blue.
  ///
  /// Since blue is the last state, it will still be streaming blue so
  /// the bridge doesn't drop the connection due to inactivity.
  Future<void> _startStreaming2() async {
    _isStreamingPattern1 = false;
    _isStreamingPattern2 = true;
    _isStreamingPattern3 = false;

    final ColorXy red = ColorXy.fromRgbNormalized(1.0, 0.0, 0.0, 1.0);
    final ColorXy blue = ColorXy.fromRgbNormalized(0.0, 0.0, 1.0, 1.0);

    final EntertainmentStreamCommand command1 = EntertainmentStreamCommand(
      channel: 0,
      color: red,
      animationDuration: const Duration(milliseconds: 500),
      animationType: AnimationType.ease,
    );

    final EntertainmentStreamCommand command2 = EntertainmentStreamCommand(
      channel: 0,
      color: blue,
      animationDuration: const Duration(milliseconds: 500),
      animationType: AnimationType.ease,
    );

    // This should cause one lights to alternate between red and blue.
    //
    // They will stay red or blue for 500ms then switch. This will
    // happen for 5 seconds, then it should stop on blue.
    //
    // Since blue is the last state, it will still be streaming blue so
    // the bridge doesn't drop the connection due to inactivity.
    for (int i = 0; i < 5; i++) {
      // IMPORTANT NOTE: The copy method is used here to ensure that the
      // same command isn't added to the queue multiple times. If the
      // same command is added multiple times, the bridge will only
      // execute the command once.
      hueNetwork!.entertainmentConfigurations.first.addAllToStreamQueue(
        [command1.copy(), command2.copy()],
      );
    }
  }

  /// Whether or not the entertainment streaming process is currently active and
  /// using pattern 3.
  bool _isStreamingPattern3 = false;

  /// Starts the entertainment streaming process, with streaming pattern 3.
  ///
  /// Toggles 2 lights between white and off, as if the light is jumping back
  /// and forth between the two lights.
  ///
  /// One light will stay white for 500ms, then turn off for 500ms. The other
  /// light will do the opposite. This will happen continuously until the user
  /// stops the stream.
  Future<void> _startStreaming3() async {
    _isStreamingPattern1 = false;
    _isStreamingPattern2 = false;
    _isStreamingPattern3 = true;

    final ColorXy white = ColorXy.fromRgbNormalized(1.0, 1.0, 1.0, 0.5);
    final ColorXy off = ColorXy.fromRgbNormalized(0.0, 0.0, 0.0, 0.0);

    // Continuously alternate between white and off for 500ms each.
    Timer.periodic(
      const Duration(milliseconds: 500),
      (timer) {
        // Turn the timer off when the user stops the stream.
        if (!_isStreamingPattern3) {
          timer.cancel();
          return;
        }

        hueNetwork!.entertainmentConfigurations.first.addAllToStreamQueue(
          [
            EntertainmentStreamCommand(
              channel: 0,
              color: (timer.tick - 1) % 2 == 0 ? white : off,
              waitAfterAnimation: const Duration(milliseconds: 500),
            ),
            EntertainmentStreamCommand(
              channel: 1,
              color: (timer.tick - 1) % 2 == 0 ? off : white,
              waitAfterAnimation: const Duration(milliseconds: 500),
            ),
          ],
        );
      },
    );
  }

  /// Stops the entertainment streaming process.
  Future<void> stopStreaming() async {
    setState(() {
      isLoading = true;
    });

    bool isStreaming = this.isStreaming;

    try {
      await hueNetwork!.entertainmentConfigurations.first
          .stopStreaming(bridge!)
          .then(
        (value) {
          if (value) {
            isStreaming = false;
          }
        },
      );
    } catch (e) {
      // ignore: avoid_print
      print('Error stopping stream: $e');
    }

    setState(() {
      if (!isStreaming) {
        _isStreamingPattern1 = false;
        _isStreamingPattern2 = false;
        _isStreamingPattern3 = false;
      }
      isLoading = false;
    });
  }
}
26
likes
0
points
500
downloads

Publisher

verified publisherhexcat.dev

Weekly Downloads

An SDK designed for the Flutter framework that enables developers to easily integrate Philips Hue smart devices into their applications.

Repository (GitHub)
View/report issues

Topics

#philips-hue #smart-lights

Funding

Consider supporting this project:

www.buymeacoffee.com
paypal.me
venmo.com

License

unknown (license)

Dependencies

collection, crypto, dtls2, flutter, http, multicast_dns, path_provider, url_launcher

More

Packages that depend on flutter_hue