monster_halo

Drive the iQOO / Vivo Monster Halo LED ring (the 4-LED ring on the back of the iQOO 13/15 and similar OriginOS devices) from Flutter.

This package talks directly to the vivo_light_service binder — no root, no signature permission, no Vivo SDK. The service does not enforce permissions on its start/stop calls, so any app can drive the ring as long as the user's Settings → Dynamic Light master toggle is enabled.

Supported devices

Tested on iQOO 13 (OriginOS, Android 15-ish). Likely works on any current Vivo / iQOO phone that ships the same vivo_light_service AIDL. IQOO 15 Might also support (untested)

Features

  • Solid colors (single color or per-LED)
  • Strobe, chase, pair-flip, slow-pulse, square-wave, comet, alt-chase
  • Fluent HaloEffectBuilder API
  • 19 ready-made presets via HaloPresets
  • Full access to the 38 binder methods: brightness, breathing-mode toggles, time windows, contact / notification / media filters, persistent scene-effect config, animation info queries
  • Raw JSON pass-through for advanced payloads

Quick start

dependencies:
  monster_halo: ^0.1.0
import 'package:monster_halo/monster_halo.dart';

final halo = MonsterHalo.instance;

if (await halo.hasLight()) {
  final handle = await halo.play(HaloPresets.cometRainbow);
  await Future.delayed(const Duration(seconds: 5));
  await halo.stopById(handle);
}

Building custom effects

final effect = HaloEffectBuilder()
    .style(HaloStyle.chase)
    .colors([HaloColor.red, HaloColor.blue, HaloColor.white, HaloColor.green])
    .period(const Duration(milliseconds: 600))
    .count(20)
    .brightness(80)
    .build();

final handle = await halo.play(effect);

Styles

HaloStyle Behavior
solid All 4 LEDs same color, stays on ~60s
perLedSolid Each LED its own color, stays on ~60s
strobe All 4 blink together through color sequence
chase LEDs light in sequence 1→2→3→4
pairFlip LEDs 1,3 vs LEDs 2,4 alternating
slowPulse All 4 pulse together with soft fade
squareWave All on / all off, equal duty
comet Short bright flash + long tail per LED
altChase Alternate chase pattern

Per-LED colors

HaloEffectBuilder()
    .style(HaloStyle.perLedSolid)
    .perLed(HaloColor.red, HaloColor.green, HaloColor.blue, HaloColor.white)
    .build();

Presets

await halo.play(HaloPresets.policeStrobe);
await halo.play(HaloPresets.heartbeat);
await halo.play(HaloPresets.rgbRainbow);

for (final p in HaloPresets.all) {
  await halo.play(p);
  await Future.delayed(const Duration(seconds: 2));
}

Persistent scene config

Configure the per-event default style — the same settings the user adjusts via Settings → Dynamic Light. Use these to set what the ring does for incoming calls / notifications / charging:

await halo.setEffectType(lightType: 502, type: 1);
await halo.setEffectSubType(lightType: 502, type: 1, subType: 3);
await halo.setSceneCollectColor(
  lightType: 502, type: 1, subType: 3, colorIdx: 0,
);

Permissions

None. The plugin uses reflection to reach android.os.ServiceManager.getService(...). On Android 9+ this requires no special manifest declaration.

Platform support

Platform Supported
Android (Vivo / iQOO with Halo ring) yes
Other Android devices no (hasLight() returns false)
iOS / Web / Desktop no

License

MIT

Libraries

monster_halo