Skip to main content

designless_serve_flutter 0.1.0 copy "designless_serve_flutter: ^0.1.0" to clipboard
designless_serve_flutter: ^0.1.0 copied to clipboard

Flutter bindings for designless_serve. Turns your published brand into a ThemeData, registers its fonts before the first frame that uses them, and draws its marks.

designless_serve_flutter #

Flutter bindings for designless_serve. Turns your published brand into a ThemeData, registers its fonts before the first frame that uses them, and draws its marks.

Everything that can be decided without a platform lives in the core, which this re-exports. You import one package.

Install #

dependencies:
  designless_serve_flutter: ^0.1.0

Use #

import 'package:designless_serve_flutter/designless_serve_flutter.dart';

final brand = Brand(
  publicId: 'r_XXXX',
  fetch: (url) async => (await http.get(Uri.parse(url))).body,
);

final staging = flutterFontStaging(
  fetch: (url) async => (await http.get(Uri.parse(url))).bodyBytes,
);

await brand.init();
await staging.stage(await brand.loadFonts());   // before the first branded frame

runApp(BrandScope(
  brand: brand,
  staging: staging,
  child: Builder(builder: (context) {
    final brand = BrandScope.of(context);
    return MaterialApp(
      theme: buildBrandTheme(brand.tokens!, staging: staging),
      home: const Scaffold(body: BrandMark(height: 32)),
    );
  }),
));

Fonts #

flutterFontStaging registers each face with Flutter's FontLoader under its PostScript name, and that same name is what buildBrandTheme puts in TextStyle.fontFamily. There is only one string, so the two ends cannot drift.

Registering under the human family name instead — Inter, and hoping Flutter pairs weight 600 with the SemiBold file — does not work. Flutter does no weight matching across separately loaded fonts. It renders the one face it has and synthesises the rest, which is how "our bold looks wrong on Android" starts.

Stage before you build a theme. A ThemeData built while faces are still downloading names no brand family at all, and text laid out under it keeps the platform font for the life of that view. The theme will not claim a family it cannot resolve, so this fails quietly and correctly rather than loudly and wrongly — but the fix is to await stage() first.

Changes land on the next foreground #

BrandScope watches the app lifecycle. A change fetched with brand.refresh() is held until the app resumes, then activates and rebuilds the tree below.

That default is not caution for its own sake. UIKit's appearance proxy does not restyle views already built and Android theming applies at inflation, so on several platforms an immediate swap does not work anyway. And a person halfway through a form does not expect the screen to move around them.

If nobody is mid-task — a kiosk, a preview — ask for it at the fetch:

await brand.refresh(activateNow: true);

Theme #

buildBrandTheme maps published values onto ThemeData and invents nothing. Where a brand publishes no value, Flutter's own default stands rather than a number chosen in this package. A binding that fills gaps with its own taste is a second designer nobody hired, and the difference only shows on the brands that publish least.

rootPixels converts rem sizes; it defaults to 16, the browser base the token tree is written against.

Marks #

const BrandMark(role: 'logo-symbol', height: 32)

PNG, not SVG, and that is a decision. Flutter cannot draw an SVG without a package, and a binding that reached for one would put a transitive dependency into every app that wants a logo. The surface rasterises the vector on demand at a closed ladder of sizes, so asking for the rung that fits gives a sharp mark with no extra dependency.

The rung is picked from physical pixels, not logical ones: a 32pt mark on a 3x screen asks for 128, because asking for 32 gives a soft mark on exactly the devices people look at closely.

The mark follows the appearance the brand is painted in, not the one that was last requested — between asking for light and light arriving, those differ.

An app that already has an SVG renderer can skip BrandMark and use brand.asset(role, format: AssetFormat.svg) for the address.

Licence #

Apache-2.0

0
likes
150
points
67
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter bindings for designless_serve. Turns your published brand into a ThemeData, registers its fonts before the first frame that uses them, and draws its marks.

Homepage
Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

designless_serve, flutter

More

Packages that depend on designless_serve_flutter