Skip to main content

Flutter App (~/loklok_radio_app)

A native Android radio app — the premium LOKLOK player built with Flutter and media_kit (mpv). All data comes from the same public HTTP endpoints the PWA uses, so it works from any network with no keys or auth.

  • Where (local): E:\loklok_radio_app
  • Where (remote build): /home/sms/loklok_radio_app (build only on the remote — local C: is full, and the remote has JDK 17 + /opt/android-sdk)
  • Flutter: 3.44.9 stable at ~/flutter on sms@10.10.8.230
  • Artifact: app-release.apk (debug signing config) — local copy at E:\loklok_radio_app\app-release.apk == remote build/app/outputs/flutter-apk/app-release.apk

What it does

TabContent
LiveAuto-playing HLS stream, live now-playing card (title/artist/type), animated EQ, listener count pill, play/pause, mute, skip, volume, today's genre-mix chips
Up NextThe full on-air rotation (/api/station/state) with per-track crowd votes (/api/upnext) — tap the flame to boost a track
ChatLive room chat + presence avatars via the chat-server long-poll REST API
AboutStation facts + stream URLs

Design is a branded dark theme: gradient glass cards, glowing rotating "artwork" orb, animated equalizer bars, and a glass bottom-nav shell.

Code layout

FileRole
lib/main.dartentry: MediaKit.ensureInitialized(), MaterialApp theme, glass 4-tab bottom-nav shell, autoplay on launch
lib/models.darttyped parsers for every API (Track, ApiStatus, StationState, UpNextTrack, ChatMessage, MmtxSnapshot) + Pal brand colors + typeBadge()
lib/services.dartApiClient (engine proxy), ChatClient (long-poll), PlayerService (media_kit HLS singleton, ChangeNotifier)
lib/widgets.dartreusable premium widgets: EqBars, GlassCard, PlayButton, OrbArtwork, LivePill, PresenceStack, GlowBackground
lib/screens.dartHomeScreen / QueueScreen / ChatScreen / AboutScreen

Public endpoints used (no auth)

  • Proxy base http://files.mediahubnetwork.net:8094/api/status, /api/station/state, /api/upnext?uid=, /api/upnext/vote, /api/station/skip, /api/chat/{join,heartbeat,messages,history,presence}
  • Listener proxy http://files.mediahubnetwork.net:19353/v3/paths/listloklok.readers.length
  • Audio https://api.mediahubnetwork.net/loklok/index.m3u8 (HLS — HTTPS, standard port, same-origin cookie flow; since 2026-08-13 the plain-http://:19351 URL was dropped because mobile carriers block non-443 traffic and libmpv couldn't persist the MediaMTX cookie on it). OGG fallback: https://files.mediahubnetwork.net/main.

Vote identity is a per-install uid persisted in SharedPreferences (loklok.voteUid); chat auto-rejoins via stored name/token.

Building

# on sms@10.10.8.230
export PATH="$HOME/flutter/bin:$PATH"
cd ~/loklok_radio_app
flutter pub get
flutter analyze # must be clean
flutter build apk --release

Copy changed sources up with scp lib/*.dart pubspec.yaml sms@10.10.8.230:~/loklok_radio_app/… then pull the APK back with scp sms@10.10.8.230:~/loklok_radio_app/build/app/outputs/flutter-apk/app-release.apk .

Flutter gotchas hit in this project

  • MediaKit.ensureInitialized() returns void — do not await it (await_only_futures).
  • PlayerService extends ChangeNotifier requires import 'package:flutter/foundation.dart';.
  • Tween takes named params (Tween<double>(begin: 0.25, end: 1.0)) — the positional form is gone.
  • A helper like c(int hex) => Color(hex) is not const, so it can't appear inside const LinearGradient(...)/const BoxDecoration(...) — drop the const.
  • Android manifest already sets android:label="LOKLOK FM" and android:usesCleartextTraffic="true" (the HLS stream is plain HTTP).