Why I Started Creating Models in My Flutter Project

BackerLeader posted 2 min read

When I was building my music app in Flutter, I began with the “quick and dirty” way of handling data. You know that vibe coding flow — fetch some JSON, dump it in a Map<String, dynamic>, and just wire it up to the UI. It felt fast at first, but it quickly turned into a mess.

Take this example: I was fetching playback data from my service and passing it around like this:

class PlaybackWidget extends StatelessWidget {
  final Map<String, dynamic> playbackData;

  PlaybackWidget({required this.playbackData});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text("Now playing: ${playbackData['title']}"),
        Text("Artist: ${playbackData['artist']}"),
        Text("Mode: ${playbackData['playbackMode']}"),
      ],
    );
  }
}

It worked. But every time I typed playbackData['title'], I felt uneasy. One typo like playbackData['titel'] and boom — runtime error. On top of that, the widget didn’t tell me what kind of data it really expected. Was playbackMode a string? An enum? A number? I had to keep it all in my head.

Switching to Models

After wrestling with this a few times, I finally decided to create a proper model for my playback data.

enum PlaybackMode { repeat, shuffle, normal }

class Playback {
  final String title;
  final String artist;
  final PlaybackMode playbackMode;

  Playback({
    required this.title,
    required this.artist,
    required this.playbackMode,
  });

  factory Playback.fromJson(Map<String, dynamic> json) {
    return Playback(
      title: json['title'],
      artist: json['artist'],
      playbackMode: PlaybackMode.values.firstWhere(
        (e) => e.toString() == 'PlaybackMode.${json['playbackMode']}',
      ),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'title': title,
      'artist': artist,
      'playbackMode': playbackMode.name,
    };
  }
}

Now my widget looked much cleaner:

class PlaybackWidget extends StatelessWidget {
  final Playback playback;

  PlaybackWidget({required this.playback});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text("Now playing: ${playback.title}"),
        Text("Artist: ${playback.artist}"),
        Text("Mode: ${playback.playbackMode.name}"),
      ],
    );
  }
}

Suddenly everything felt safer. Autocomplete worked, I didn’t worry about typos, and my code told me exactly what kind of data I was dealing with.

What I Learned

Models might look like “extra code” at first, but in reality, they save time and mental energy. Instead of juggling raw maps everywhere, you give your app a solid data structure to lean on.

In my case, once I switched to models in my music app, my code got way easier to maintain. Adding new fields, refactoring, or passing data between widgets stopped being a headache.

So if you’re just hacking away in Flutter and think models are overkill — trust me, they’re not. Future-you will thank present-you for adding them.

If you read this far, tweet to the author to show them you care. Tweet a Thanks
0 votes
0 votes

More Posts

How I Paid Off a Little Technical Debt in My Flutter Music App

yogirahul - Aug 15

Why does my Flutter music player jump from 0:00 to actual time? (One-liner fix inside!)

yogirahul - Aug 8

Fixing a Tricky Playback Issue in My Flutter Music App

yogirahul - Jul 24

After facing weird permission issues, I built my own music app in 6 hours.

yogirahul - Jul 14

When to Use setState, Provider, and Consumer in Flutter

yogirahul - Aug 11
chevron_left