service-boundary-and-native

Wires Flutter side effects and MethodChannels as injectable interfaces behind throwing Riverpod providers.

Updated Aug 22, 2026
One-click install
npx skills add https://github.com/zakariaf/NearlyStop --skill service-boundary-and-native-zakariaf
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: service-boundary-and-native
Source: https://github.com/zakariaf/NearlyStop/tree/main/.claude/skills/service-boundary-and-native
Command: npx skills add https://github.com/zakariaf/NearlyStop --skill service-boundary-and-native-zakariaf

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill includes scripts (resource) and references (resource) components.

What problem does it solve? Flutter apps leak platform SDKs, DateTime.now() calls, and MethodChannels into widgets and repositories, making features untestable headlessly and letting silent failures slip through. This Skill enforces a single seam where every side effect is an injected interface with typed outcomes, one live implementation wired at the composition root, and deterministic fakes in tests. ## Core Features & Use Cases - Injectable service boundaries: Define value-typed interfaces returning sealed outcomes, with providers that throw UnimplementedError until the composition root overrides them with exactly one live implementation. - Deterministic time and fakes: Inject Clock from package:clock via clockProvider, replace DateTime.now() everywhere, and test with hand-written fakes that implement the interface and expose reachable failure paths. - Native channel quarantine: Confine every MethodChannel to lib/native/, manage versioned cross-language contracts edited on both sides in one commit, and prove mirrors with round-trip integration tests. - Use Case: When adding a ShareService or AnalyticsService to a Flutter app, use this Skill to create the interface, throwing provider, live impl in main, fake for tests, and run the boundary-check scripts before opening a PR. ## Quick Start Ask the AI to add a new share service boundary following the service-boundary-and-native rules, with a typed result, throwing provider, live implementation in main, and a fake for tests.

Frequently Asked Questions about service-boundary-and-native

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I inject a service in Flutter with Riverpod?

Define an abstract interface class with value-typed signatures, then create a Provider that throws UnimplementedError. Override it once in main with the live implementation via ProviderScope overrides, and override it with a hand-written fake in tests.

How do I replace DateTime.now() for testable Flutter code?

Inject Clock from package:clock through a clockProvider that defaults to const Clock(). Feature code reads ref.read(clockProvider).now(), and tests override it with Clock.fixed(...) for deterministic time.

Should I use fakes or mocks for Flutter service testing?

Use hand-written fakes that implement the interface with implements, so adding a method breaks the build. The fake must model reachable failure paths; a fake that always succeeds is a happy-path lie. Mock frameworks are reserved for one-off stubs.

Where should MethodChannel code live in a Flutter project?

Every MethodChannel construction belongs under a single lib/native/ directory, wrapped in a thin bridge class and exposed through an injectable interface. A channel created inside a widget, repository, or notifier is a defect regardless of whether it works.

Why does my Flutter service fail silently instead of throwing errors?

Dart requires no throws declaration, so thrown exceptions get forgotten. Return a sealed outcome type instead, making failure control flow; a non-exhaustive switch over it is a compile error, forcing every call site to handle each failure variant.

How do I share data between Dart and native code safely?

Use a versioned file with one contract owner per side, edited on both sides in the same commit, and prove it with a round-trip integration test. Avoid shared_preferences, whose DataStore backing and flutter. key prefix cause silent read failures.