← Back to notes
Flutter & Cross-Platform 2026-10-08 13:00 3 min read Local copy

Building DevFeed — Part 1: MVVM Architecture Before Writing a Single Screen

Building DevFeed — Part 1: MVVM Architecture Before Writing a Single Screen
Yadnesh Teli
Yadnesh Teli

Posted on Oct 8 • AI-assisted

Building DevFeed — Part 1: MVVM Architecture Before Writing a Single Screen
#flutter #architecture #riverpod #dart

Series 2 of 2 — Part 1 of 4

Building DevFeed — Part 1: MVVM Architecture Before Writing a Single Screen

Series: Building DevFeed — A Proper Flutter MVVM App from Day One

When I started DevFeed, I had just finished the painful MVVM refactor of the shopping app. I knew what proper architecture looked like. I knew the difference between a repository and a ViewModel. I had felt the cost of not having that separation.

So this time I set up the entire architecture before I wrote a single screen.

DevFeed is a Hacker News reader app built as a Flutter MVVM practice project during my internship. This series documents how I built it — from a properly structured first commit all the way to shipping v1.0.1 with a GitHub Actions release workflow.

Planning the architecture first

Before opening the first Dart file, I mapped out the layers:

  • Model: ArticleModel to represent a Hacker News story — id, title, url, score, author, and comment count.
  • Repository: HackerNewsRepository responsible for fetching stories from the API. Returns a list of ArticleModel.
  • ViewModel: FeedViewModel using Riverpod AsyncNotifier. Calls the repository, exposes AsyncValue state to the UI.
  • View: FeedScreen that watches the ViewModel. Renders loading, error, and data states. No API knowledge.

This is the same structure I eventually reached in the shopping app. The difference in development experience because of starting with it is the main story of this series.

The first commit: full scaffold

The first commit was a full scaffold — all 35 files. Models, repositories, ViewModels, screens, widgets, routing, theming, and a basic test file. Nothing was fully implemented but the skeleton of every layer was in place.

This approach felt strange at first. You write a lot of boilerplate before you can see anything on screen. But it paid off immediately when I started filling in implementations — because I always knew exactly which file to open and what it should contain.

Riverpod AsyncNotifier setup

I used AsyncNotifier for the feed ViewModel. AsyncNotifier is the right choice when your state is the result of an async operation — it handles loading, error, and data states out of the box.

class FeedViewModel extends AsyncNotifier<List<ArticleModel>> {
  @override
  Future<List<ArticleModel>> build() async {
    return ref.read(hackerNewsRepositoryProvider).fetchTopStories();
  }

  Future<void> refresh() async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(
      () => ref.read(hackerNewsRepositoryProvider).fetchTopStories(),
    );
  }
}

go_router for navigation

I used go_router from day one. In the shopping app I had used Navigator.push directly, which scattered navigation logic across screens. go_router centralises routing in a single config file and supports declarative navigation, deep links, and route guards. Adding a new route later was a two-line change instead of a hunt through multiple screens.

What starting right feels like

The most noticeable difference compared to the shopping app was the absence of confusion. When I needed to add a feature, I knew where it should go. When I needed to fix a bug, I knew which layer was responsible.

The shopping app taught me what MVVM is. DevFeed taught me what it feels like to work in a well-structured codebase. You need both lessons.

Next: Part 2 — Building the Feed: Hacker News API, AsyncNotifier, and Parallel Fetching

Top comments (0)

Subscribe

For further actions, you may consider blocking this person and/or reporting abuse