Sitemap
Morel Denzel

Morel Denzel

Passionate about building intuitive, scalable solutions and sharing insights. https://denzel-labs.vercel.app/

Press enter or click to view image in full size

From App Code to Reusable Package: How I Built My Own Flutter Package

4 min readFeb 2, 2026

--

I’ve been building Flutter apps for a while now, and there always comes that moment when you copy-paste the same piece of code into three different projects. You know the feeling “Okay, this custom button / loader / validator works great… why am I doing this again?”

That happened to me recently. I had this smooth navigation route with varieties of animations that I kept reusing. Instead of keeping it trapped inside one app, I decided to turn it into a proper Flutter package.

Here’s how I went from messy app code to something clean and reusable step by step. Nothing super advanced, just the practical stuff I wish someone had told me the first time.

Step 1: Pull the code out and create a package project

I started by making a fresh package instead of hacking an existing app. In the terminal:

flutter create --template=package my_cool_widget
cd my_cool_widget

This gives you a clean skeleton:

  • lib/my_cool_widget.dart (your main file that people will import)
  • pubspec.yaml
  • a test/ folder ready for tests

Right away I renamed things to match my package name and updated the description in pubspec.yaml. Small thing, but it makes everything feel intentional.

name: my_cool_widget
description: A customizable animated widget with validation feedback for Flutter forms.
version: 0.1.0
homepage: https://github.com/yourusername/my_cool_widget

Add your GitHub link early it helps people trust the package later if your feel like publishing it on pub.dev.

Step 2: Organize the code properly (the part that shows maturity)

I didn’t just dump everything into one file. Inside lib/ I created a src/ folder for the real implementation (this is a common pattern so users only see the clean public API).

So the structure looked like:

lib/
├── my_cool_widget.dart ← only exports
└── src/
├── my_cool_widget.dart ← the actual widget code
├── utils/
│ └── helpers.dart
└── models/
└── validation_result.dart

In the top-level my_cool_widget.dart I just export what people need:

export 'src/my_cool_widget.dart';

This keeps the public surface small and clean. Users do import ‘package:my_cool_widget/my_cool_widget.dart’; and get only what they should.

Step 3: Write it like production code

I treated this package the way I’d want if I was using someone else’s:

  • Added good parameter names and default values
  • Made it null-safe (of course)
  • Used constants where it made sense
  • Added documentation comments on public classes/methods Flutter’s analyzer shows them nicely in IDEs

Example:

/// A customizable animated input field that shakes on error.
///
/// [onChanged] callback for value changes
/// [validator] optional function that returns error message or null
class CoolAnimatedField extends StatefulWidget {
final ValueChanged<String>? onChanged;
final String? Function(String?)? validator;
// ...
}

Step 4: Actually test it (yes, even small packages)

I used to skip this… big mistake. Tests give confidence when you change things later.

Get Morel Denzel’s stories in your inbox

Join Medium for free to get updates from this writer.

I wrote a few basic widget tests in test/my_cool_widget_test.dart:

  • Does it render without crashing?
  • Does the validator show error message?
  • Does the animation trigger?

Running flutter test became part of my routine before every commit. Aim for decent coverage on the important logic you don’t need 100%, but 0% looks bad to users.

Step 5: README, CHANGELOG, LICENSE

This is where a lot of packages fall short, but it really shows maturity.

  • README.md: I wrote a friendly one with
  • Quick “why this package” intro
  • Installation (just the pub.dev line)
  • Simple usage example with copy-paste code
  • Screenshot or GIF if possible (huge help)
  • Contribution notes
  • CHANGELOG.md: Even for v0.1.0 I started it. Later it’s gold for users.
  • LICENSE: MIT is fine for most open-source stuff. Don’t skip this pub.dev requires it.

Step 6: Dry-run

Before going live I always do:

dart pub publish --dry-run

It catches formatting issues, missing files, bad pubspec entries, etc.

Now if you want to publish, hit this when everything is green:

dart pub publish

First publish asks you to log in with your Google account. After that it’s smooth.

A few minutes later my package was live on https://pub.dev/packages/my_cool_widget 🎉

What I learned (the real takeaways)

  • Start small: solve your own pain first
  • Keep the API dead simple: fewer parameters = happier users
  • Tests + good README = way more stars/forks
  • Use src/ pattern and clean exports: looks professional
  • Do the dry-run every time: saved me from embarrassing v0.1.1 fixes

If you’ve got some code you keep copying, try turning it into a package. It forces you to write better code, and it’s satisfying to see other people use something you built.

--

--

Morel Denzel
Morel Denzel

Passionate about building intuitive, scalable solutions and sharing insights. https://denzel-labs.vercel.app/