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
HaloEffectBuilderAPI - 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