<FlutterSolution/> flutter · dart · clean architecture

Flutter Prep #30 Package & Plugin Development: What Senior Developers Know About Authoring

Package & Plugin Development: What Senior Developers Know About Authoring

Why "flutter pub add" is only half the job, and how to prove you understand the boundaries of reusable code.

1. Why Interviewers Ask About Authoring

You will hit this question the moment your team decides to share code across two apps, or when your monorepo grows large enough to require strict domain boundaries. Almost every non-trivial Flutter team eventually needs to extract internal packages.

Most developers use the output of package development constantly—running flutter pub add without a second thought. But an engineer who has only ever consumed packages and never reasoned about what happens on the authoring side is missing a testable piece of senior fluency. Extracting a shared core_network package out of an existing app forces you to make decisions about platform boundaries, semantic versioning, and API visibility that you simply don't have to make when writing a standard application feature.

2. Dart Package vs. Flutter Plugin

This is the first concept that trips candidates up. If an interviewer asks you to extract your core_network logic, they want to know if you understand where the platform boundary sits.

πŸ“„
Dart / Flutter Package
Written entirely in pure Dart. No native platform code included.
Runs anywhere Dart runs
πŸŒ‰
Flutter Plugin
Contains custom platform-specific implementation code (Kotlin, Swift, C++).
Bridges Dart to Native APIs

A Dart Package is pure Dart. Even if your core_network package uses Flutter widgets to show network error dialogs, or depends on http, it is still just a package because it does not contain its own native code.

A Flutter Plugin is required when you need to write platform-specific code bridging to native APIs. If your core_network package needed to implement highly custom SSL certificate pinning using raw Kotlin on Android and Swift on iOS, it would cross the boundary and become a plugin. Getting this distinction right proves to the interviewer that you don't treat all reusable code as a single undifferentiated black box.

3. Pubspec Semantics for Authors

As a package consumer, you just paste things into dependencies. As a package author, placing a library in the wrong section has massive downstream consequences.

  • dependencies: Required at runtime. If core_network depends on dio here, anyone who installs your package also downloads dio.
  • dev_dependencies: Needed only for developing or testing the package itself (e.g., mocktail or flutter_lints). These are explicitly not shipped to consumers.
  • dependency_overrides: A local escape hatch to force a specific version or local file path during development. You must never publish a package with active overrides—it overrides the resolution for the consumer's entire project, causing dependency hell.
yaml
name: core_network
description: A pure Dart package for shared network logic.
version: 1.2.3

dependencies:
  dio: ^5.3.0  # Shipped to consumers

dev_dependencies:
  test: ^1.24.0 # Ignored by consumers

# dependency_overrides:
#  dio:
#    path: ../local_dio_fork  <-- never="" publish="" span="" this="">

The Danger of Caret Syntax

When you define a version as ^1.2.3, you are telling the Dart pub resolver to use Semantic Versioning (Semver) rules.

^1.2.3
1.2.3
1.9.9
2.0.0
Means: >=1.2.3 and <2.0.0

Caret syntax allows any version that is considered "compatible"—meaning it will auto-upgrade minor and patch versions, but stop before the next major version. If an author incorrectly uses loose constraints like any or omits the caret, they risk silently pulling a breaking major change into every consumer's app on their next build. If they use a hardcoded version (e.g., 1.2.3 without the caret), they cause unnecessary version conflicts for the consumer.

4. Publishing, Semver, and Melos

Publishing a public package involves the dart pub publish command. But the technical command is easy; the discipline is what interviewers test. That discipline relies entirely on strict adherence to Semantic Versioning.

Major Version
Breaking Change

You changed an existing API signature or deleted a class.

1.4.2 ➔ 2.0.0
Minor Version
New Feature

You added a new endpoint method, fully backward-compatible.

1.4.2 ➔ 1.5.0
Patch Version
Bug Fix

You fixed an internal parsing crash without changing the API.

1.4.2 ➔ 1.4.3

Every version bump requires a corresponding entry in your CHANGELOG.md. Consumers rely entirely on the changelog to determine if upgrading is safe. A package published without a human-readable changelog is considered unmaintained and dangerous.

If core_network lives inside a large monorepo alongside 15 other packages, bumping versions by hand is a nightmare. This is where you mention Melos to your interviewer: using commands like melos version and melos publish automates coordinated version bumps. If core_network has a breaking change, Melos ensures that every internal package depending on it also gets a version bump automatically.

5. Internal Registries: Where Code Lives

A common mistake candidates make is assuming "publishing a package" always means putting it on the public pub.dev registry. A proprietary core_network package containing your company's specific API authentication headers should absolutely not be public.

Where should this package live?
Public pub.dev
Choose this for open-source tools intended for global community use.
Private Pub Server
Choose this when sharing proprietary code across multiple separate repositories (e.g., Cloudsmith, JFrog).
Path / Git Dependency
Choose this for local packages inside a single Monorepo. No registry required.

Mentioning private pub servers or path-dependencies shows the interviewer you understand enterprise security and codebase architecture, rather than just open-source hobby projects.

6. The Discipline of Minimal APIs

Extracting a package is meant to enforce architectural boundaries. If you extract core_network but accidentally expose all your internal JSON parsing utility classes to the consumer, you haven't created a boundary at all—you've just moved the spaghetti code to a new folder.

Dart packages enforce this via the lib/ directory and `export` statements. Files inside lib/src/ are considered private to the package. You create a single "barrel file" at lib/core_network.dart that explicitly curates the public API.

dart (lib/core_network.dart)
// BAD: Exposing implementation details to the consumer
export 'src/network_client.dart';
export 'src/internal_retry_logic.dart'; 
export 'src/raw_json_parser.dart';

// GOOD: Curated public API
export 'src/network_client.dart' show NetworkClient;
export 'src/models/network_error.dart';
// internal_retry_logic.dart remains safely hidden inside lib/src/
🧠 Interviewer's Follow-Up

Q: When would you NOT publish an internal package to public pub.dev?
A: When it contains proprietary business logic, hardcoded internal API keys, or is highly coupled to the specific architectural quirks of our internal apps. We would use a private pub server or a monorepo path dependency instead.

7. Senior-Level Sample Answer

If an interviewer asks: "We have a lot of duplicated API request logic across our consumer app and our driver app. How would you handle sharing this code?"

"I would extract that logic into a pure Dart package called core_network. I’d ensure it remains a standard package, not a plugin, by keeping any necessary native dependencies abstracted rather than writing custom platform code ourselves. I'd keep the public API minimal by exposing only a single barrel file and hiding implementation details inside lib/src/. Since this is proprietary logic, I wouldn't publish it to the public pub.dev registry; instead, we'd reference it as a path dependency if we migrate to a monorepo, or host it on a private pub server. Finally, I'd enforce strict Semantic Versioning—if we ever change the signature of our core API client, we bump the major version and update the changelog so the driver app team doesn't pull in a breaking change silently."
✅ Quick Self-Check

Before moving to the next post, answer this unscripted: If you fix a crash in your package without changing any class names or method signatures, which number in v1.4.2 do you increment before publishing?

Official Resources


Written by Flutter Solution · Covering the Flutter & Dart ecosystem from Mumbai, India · fluttersolution.com
Nachiketa Pandey
// written by

Nachiketa Pandey

Senior Flutter Developer · Cross-Platform & Fintech Applications

Building production Flutter apps at Kotak Neo — clean architecture, BLoC, agentic AI integrations, and fintech-grade security. Sharing everything I learn, one post at a time.

Flutter & Dart Clean Architecture BLoC Firebase FinTech
View Full Portfolio

Comments

Nachiketa
Meet Author// tap to open