This app implements a lightweight “stories” feature (like Instagram/Snapchat):
- Images are stored locally in SQLite via Drift
- Stories auto-expire after 24 hours
- Viewing a story marks it as viewed; unviewed stories are prioritized
- Smooth preview with progress bars and auto-advance every 3 seconds
- Built with BLoC for state management and GetIt for DI
This document explains the architecture, data flow, platform setup, how to run, and how to extend the project.
Area | Choice |
---|---|
UI | Flutter |
State management | flutter_bloc |
Dependency Injection | get_it |
Local database | drift + drift_flutter (SQLite) |
Media picking | image_picker |
Paths | path_provider |
lib/
core/
locator.dart // GetIt service locator
data/
database/
app_database.dart // Drift tables + DAO-style methods
app_database.g.dart // Generated (build_runner)
datasources/
story_local_data_source.dart // Local source backed by Drift
repositories/
story_repository_impl.dart // Repository implementation
domain/
entities/
story.dart // StoryDTO used across layers
repositories/
story_repository.dart // Repository contract
presentation/
blocs/
events/ // BLoC events
states/ // BLoC states
home_bloc.dart
image_preview_bloc.dart
pages/
home_page.dart // List of stories + add/delete
image_preview_page.dart // Fullscreen preview + progress + auto-advance
main.dart // App entry
Layered architecture with clear separation of concerns:
- Domain: pure contracts and data types
- Data: implementations that talk to storage (Drift)
- Presentation: BLoCs + Widgets
- DI:
core/locator.dart
wires it all withGetIt
High-level flow:
main.dart
callssetupLocator()
and startsMyApp
HomePage
createsHomeBloc
usingStoryRepository
from the locator- On
LoadStories
,HomeBloc
:- Deletes old stories (>24h)
- Subscribes to a
Stream<List<StoryDTO>>
sorted by unviewed first, then newest
- UI reacts via
BlocBuilder
to show stories - Adding a story uses
image_picker
to select an image, then dispatchesAddStory
- Tapping a story navigates to
ImagePreviewPage
, which:- Auto-advances every 3s with a progress bar per story
- Emits
StoryViewed
for unviewed items - On back, dispatches
MarkStoriesAsViewed
to persist viewed flags
Stories
table (lib/data/database/app_database.dart
):
Column | Type | Notes |
---|---|---|
id | int | PK, autoincrement |
image | blob | Raw bytes (BLOB) |
createdAt | DateTime | Default now() |
viewed | bool | Default false |
LoadStories
: purge old stories, subscribe to the 24h streamAddStory
: persist imageDeleteStory
: remove by id- Emits:
HomeLoading
,HomeLoaded(stories)
,HomeError
Internal events are used to respond to stream updates and errors.
- Tracks a list of viewed story IDs during the session
StoryViewed(story)
: record if it was previously unviewedMarkStoriesAsViewed
: persist the collected viewed IDs when closing the screen
- Horizontal list of stories with an “Add” circle at index 0
- Long-press on an item to delete
- Border color indicates viewed state (green = unviewed, grey = viewed)
- Tapping an item opens the preview:
- If the tapped item is unviewed, the preview filters to only unviewed items going forward
- If the tapped item is already viewed, the preview shows all remaining items
- Fullscreen
PageView
- Auto-advance every 3s
- Per-story progress bars at the top
- Back button pops and persists viewed IDs via
MarkStoriesAsViewed
- Drift for local storage: typed schema, streams, seamless SQLite
- BLoC for predictable state transitions and testability
- GetIt for simple, explicit DI without boilerplate
- Storing images as BLOBs keeps everything self-contained (tradeoff: DB size growth)
- Remote sync layer with a cloud backend
- Videos support (consider file storage + URI in DB instead of BLOB)
- Story captions and metadata
- Pagination/archiving beyond 24h
- Unit/UI tests for preview timers and progress indicators
MIT
Add screenshots/GIFs under docs/media/
and reference them here. Suggested files:
docs/media/home.png
— Home with stories listdocs/media/add_story.png
— Image picker flowdocs/media/preview.gif
— Auto-advance preview with progress bars
Embed examples:
Home | Add Story | Preview GIF |
---|---|---|
![]() |
![]() |
![]() |