This episode covers multiplatform strategy and shared libraries in Dart: sharing code between mobile, web, server, and CLI, modular architecture with reusable packages, cross-platform testing, and integration with Flutter plugins.

One of Dart's biggest promises comes true when the same code runs on mobile, web, server, and the command line. Episode 17 covers multiplatform strategy and shared libraries: how to separate logic from the platform, organize modular projects, test across platforms, and connect with Flutter plugins.
The key to all of this is discipline: shared code must be pure and free of platform dependencies. You'll learn to separate the layers properly so that a single logic package serves every client.
The heart of a multiplatform strategy is separating business logic (pure Dart) from platform details (UI, files, native networking):
monorepo/
├── packages/domain_core/ # murni Dart, dipakai semua platform
├── apps/flutter_app/ # mobile dan web via Flutter
├── apps/server/ # backend dengan Shelf
└── apps/cli/ # command-line toolpackages/domain_core/ is a pure Dart package without Flutter or io-specific dependencies. Every application adds this package as a dependency and uses exactly the same logic on every platform.
To make a package shareable, follow these rules: avoid dart:io and dart:html in the core layer — both are only available on some platforms. For platform-dependent operations, define abstractions (interfaces) in core and provide concrete implementations per platform.
Dart supports multi-package projects in a single monorepo. A common structure uses packages/ to hold reusable libraries:
name: apps_flutter
dependencies:
domain_core:
path: ../../packages/domain_coredomain_core is added with path: so changes to the package are visible immediately without publishing. Each package has its own pubspec.yaml and versioning cycle.
A good package has a single responsibility: domain, data access, or presentation. Clear boundaries let teams work on different parts without stepping on each other, and make testing easier since dependencies can be mocked.
Start with just two packages — one for the domain and one for the app — then split further as the team and features grow. Enforcing package boundaries too early only adds coordination cost without real benefit.
Since every platform uses the same logic, test the core package once and the result applies to all clients:
cd packages/domain_core
dart testdart test in the core package validates the logic used by Flutter, the server, and the CLI at the same time. That's a huge saving compared to writing separate tests per platform.
Combining these three produces a layer that can be tested in isolation: the repository uses interfaces from core, and the application injects an HTTP- or database-based implementation per platform. This flow lets you swap data sources without touching business logic at all.
Platform-specific needs are handled through Flutter plugins. A plugin combines Dart code with native Android, iOS, and other implementations:
import 'package:shared_preferences/shared_preferences.dart';
Future<void> simpanPref() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('tema', 'gelap');
}SharedPreferences.getInstance() is an example of a popular plugin that provides a Dart API over per-platform native implementations.
To create a plugin: flutter create --template=plugin. A plugin produces a Dart API in the lib/ folder, an Android implementation in android/, an iOS one in ios/, and so on. Platform channels connect the two. If the platform logic becomes complex, consider FFI (episode 16) as a more portable alternative.
Because plugins touch many platforms, make sure every change is tested across all targets. Start from the built-in template, then add platform functionality one at a time — this keeps the plugin small and maintainable.
Don't put business logic inside a plugin. Keep it in the core package and let the plugin be nothing more than a thin bridge to native capabilities.
Key takeaways:
dart:io and dart:html in layers shared across platforms.packages/ and path dependencies makes modular development easy.In the next episode 18, we'll cover ecosystem extensions and the community — popular packages like dio, freezed, and Riverpod, working with pub.dev and publishing packages, contributing to Dart open source, and learning resources and community channels.