DEV Community

Kouki Badr
Kouki Badr

Posted on

I built a remote skin engine for Flutter

Every one of us has been here.

The designer opens Figma and changes the primary color from #6C63FF to #5B52E8. A small update (they think).

Our release cycle: create a branch, update the color, build, test, submit to App Store, wait for store review, for just a hex code.

This is the tax Flutter developers pay for a compile-time theming system. It's powerful and type-safe and beautiful — and completely frozen the moment your app ships.

flutter_skin

flutter_skin is a runtime skin engine. Instead of hardcoding your color values, you define them as named tokens. Those tokens are managed from a dashboard and delivered to your app.
When you publish a new skin, every connected device updates instantly.

How it works in 2 lines

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await FlutterSkin.init(apiKey: 'fsk_your_key_here');
  runApp(const MyApp());
}
Enter fullscreen mode Exit fullscreen mode
// In your MaterialApp:
theme: FlutterSkin.toThemeData()
Enter fullscreen mode Exit fullscreen mode

That's the integration. FlutterSkin.init() fetches the active skin from the FSkin backend and opens a persistent SSE connection. From that point, any skin you publish reaches the app in about 2 seconds.

The live update architecture

Here's what happens end to end when you click Publish:

Dashboard: click Publish
        ↓
Skin marked active in PostgreSQL
        ↓
Supabase Realtime detects the UPDATE on skins table
        ↓
Node.js backend receives the Realtime event
        ↓
Backend writes SSE event to all connected Flutter clients
        ↓
flutter_skin package receives the flag
        ↓
Package re-fetches active skin from CDN
        ↓
A stream emits new SkinTokens
        ↓
setState() → MaterialApp rebuilds → app repaints
Enter fullscreen mode Exit fullscreen mode

Total time: ~1–2 seconds. No polling. No App Store. No user action.

The full MaterialApp setup

To react to live updates, wrap your app in a StatefulWidget:

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    FlutterSkin.onSkinChanged.listen((_) {
      if (mounted) setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: FlutterSkin.toThemeData(),
      home: const HomePage(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

What's available today

The alpha supports color tokens — full Material ColorScheme mapping:

  • primary, secondary, background, surface, error
  • All the on* counterparts
  • brightness for light/dark

The dashboard (app.fskin.dev) gives you:

  • Project management
  • Skin editor with JSON view
  • Team collaboration with role-based permissions
  • API key management (auto-generated per project)
  • Publishing with confirmation + version history

A lot of cool features still coming

Links

It's still on alpha track.
I'd love feedback from anyone who tries it — bug reports, feature requests.
don't forget to thumbs up our package on pub.dev 🥸 👀.

Top comments (0)