Flutter packages
Three private Flutter packages gather what my applications had in common: a generic bloc state and a few widgets, the sign-in screen and session, the theme and language settings.
- Role
- Sole author
- Period
- July 2025, with a touch-up in January 2026
- Status
- In service. Used by two applications, Tontini and Soccercup
- Technologies
- Flutter, Dart, flutter_bloc, hydrated_bloc, dio, retrofit
- Repositories
- Three
Context
Tontini and Soccercup were developed the same year from the same template, and both repeated the same sign-in, the same settings and the same way of representing the state of a screen. The three packages were born in July 2025 from the extraction of that common code, and not from a prior design.
Problem
Each cubit redeclared its loading, success and failure states, and a fix made to the sign-in screen of one application had to be carried over by hand to the other. The shared code also had to avoid imposing its appearance, since the two applications each have their own colours.
Answer
An extraction from two applications in service
The day after the packages were created, Tontini deleted its own bloc state, its sign-in cubits and several widgets, that is 1,665 lines removed; Soccercup followed ten days later, with 1,240 lines removed. The packages therefore contain only code that had already been in use.
A single shape of state for all screens
The generic state carries a type, namely idle, loading, loaded, empty or failure, and an optional piece of data for each. An alias hides the four type parameters that most screens do not use, and it is the most reused piece: it appears 131 times in Soccercup and 45 times in Tontini.
typedef SimpleBlocState<T> = BlocState<T, void, void, void, dynamic>;
class BlocState<T, Tidle, Tloading, Tempty, Tfailure> extends Equatable {
const BlocState({
required this.type,
this.loadingData,
this.loadedData,
this.idleData,
this.emptyData,
this.failureData,
});A sign-in grafted onto the host’s client
The host application passes its own HTTP client to the sign-in package, which adds an interceptor to it: the interceptor attaches the token to each request and signs the user out when the server answers that they are no longer authorised. The theme and the language, for their part, are a persisted state, which the application applies to its interface itself; the shared widgets read the colours of the current theme, so that each application keeps its identity. Finally, the messages intended for the user go through a single bus, which one component alone is responsible for displaying.
Limits and outlook
The applications reference the packages by their path on disk and not by a version, which speeds up work across three repositories but lets incompatible changes through without a number or a warning, and requires each application to be cloned next to the packages. The tests amount to no more than the original template.
The sign-in also fixes the server’s contract, namely four paths and a token scheme, and therefore suits only the APIs that expose it identically; the interceptor further assumes that the host has stored the session in a precise form, which no documentation states. Finally, reuse is limited to two applications, and the Flutter work on Sanu, which is more recent, does not rely on these packages.
