diff --git a/AGENTS.md b/AGENTS.md
index 0d9c64b..8cc12c1 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -8,7 +8,8 @@ This file provides guidance to Codex (Codex.ai/code) when working with code in t
Two hard constraints shape everything here:
-- **Two dependencies beyond flutter.dev, each argued.** `dependencies:` in `pubspec.yaml` holds the Flutter SDK, Flutter's own first-party packages — `material_ui` (Material's home since Flutter 3.47) and its transitive set, all published by flutter.dev — plus `file_selector` (flutter.dev), the plugin behind the composer's attach button, and `google_fonts` (material.io), which fetches Google Sans and Google Sans Code at runtime. No font files ship. Nothing else, and adding one is a decision rather than a convenience: it must not force configuration on hosts that never touch the feature (this is why `file_selector` and not `image_picker`, which writes a permission, a FileProvider and a Play-services entry into every host's Android manifest), and the PR has to argue it. What the two we have do ask for is documented: the fonts need `android.permission.INTERNET` and macOS `com.apple.security.network.client`; the picker needs macOS `com.apple.security.files.user-selected.read-only`. Dev dependencies (`flutter_test`, `flutter_lints`) are fine.
+- **One dependency beyond flutter.dev, argued.** `dependencies:` in `pubspec.yaml` holds the Flutter SDK, Flutter's own first-party packages — `material_ui` (Material's home since Flutter 3.47) and its transitive set, all published by flutter.dev — plus `file_selector` (flutter.dev), the plugin behind the composer's attach button. Nothing else, and adding one is a decision rather than a convenience: it must not force configuration on hosts that never touch the feature (this is why `file_selector` and not `image_picker`, which writes a permission, a FileProvider and a Play-services entry into every host's Android manifest), and the PR has to argue it. What the one we have does ask for is documented: the picker needs macOS `com.apple.security.files.user-selected.read-only`. Dev dependencies (`flutter_test`, `flutter_lints`) are fine.
+- **The typefaces ship, they are not fetched.** Google Sans and Google Sans Code live in `packages/flow_ui/fonts/` under the SIL Open Font License, declared under `flutter: fonts:` with every cut (sans 400–700, mono 300–800, each upright and italic) and addressed as `package: 'flow_ui'`. Google Sans is a Latin subset — the full cuts are ~1.95 MB each — rebuilt by `packages/flow_ui/tool/subset_fonts.sh`; the scripts it drops fall back to the platform face. Nothing reaches the network for a glyph, so hosts need no INTERNET permission and no macOS network entitlement.
- **Nothing model-facing.** Components render state passed in and report intent out through callbacks. No prompts, schemas, provider/network calls, or any LLM awareness — that belongs to the layers built on top.
The theme, the conversation components (message, thread, streaming text, actions, loading), the composer and its menus, attachments with their preview, suggestions, and the chat surface are implemented; the roadmap below tracks the rest. Message content is modeled as typed parts (`lib/src/models/`) — sealed `FlowMessagePart` subtypes rendered by `FlowMessage`, with `FlowCustomPart` + `FlowCustomPartBuilder` as the extension seam for host-injected content.
@@ -16,7 +17,7 @@ The theme, the conversation components (message, thread, streaming text, actions
## Layout
- Root `pubspec.yaml` is the pub workspace (members under `workspace:`) with the melos scripts; root `analysis_options.yaml` (very_good_analysis) governs `tool/` and the SDK package only.
-- `packages/flow_ui/` — the published package: `lib/`, `example/` (the README's chat screen against Gemini), `assets/`, its own flutter_lints `analysis_options.yaml` and `.pubignore`.
+- `packages/flow_ui/` — the published package: `lib/`, `example/` (the README's chat screen against Gemini), `assets/`, `fonts/` (the bundled typefaces and their OFL texts), `tool/` (`subset_fonts.sh`, not published), its own flutter_lints `analysis_options.yaml` and `.pubignore`.
- `packages/stacflow/` — the StacFlow SDK package (see "SDK package"), with `example/` (the README's chat screen against Gemini; flutter_lints like the flow_ui example).
- `playground/` — the Flow UI Playground: a full Flutter app and workspace member depending on `flow_ui: ^0.4.0`. Use it to demo and manually exercise components (every component has a stage demo, with variant pills and code snippets).
- `docs/` — the Astro site behind flowui.stac.dev. `contracts/` — the SDK wire contract.
diff --git a/CLAUDE.md b/CLAUDE.md
index 9b3e1b7..602895c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -8,7 +8,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
Two hard constraints shape everything here:
-- **Two dependencies beyond flutter.dev, each argued.** `dependencies:` in `pubspec.yaml` holds the Flutter SDK, Flutter's own first-party packages — `material_ui` (Material's home since Flutter 3.47) and its transitive set, all published by flutter.dev — plus `file_selector` (flutter.dev), the plugin behind the composer's attach button, and `google_fonts` (material.io), which fetches Google Sans and Google Sans Code at runtime. No font files ship. Nothing else, and adding one is a decision rather than a convenience: it must not force configuration on hosts that never touch the feature (this is why `file_selector` and not `image_picker`, which writes a permission, a FileProvider and a Play-services entry into every host's Android manifest), and the PR has to argue it. What the two we have do ask for is documented: the fonts need `android.permission.INTERNET` and macOS `com.apple.security.network.client`; the picker needs macOS `com.apple.security.files.user-selected.read-only`. Dev dependencies (`flutter_test`, `flutter_lints`) are fine.
+- **One dependency beyond flutter.dev, argued.** `dependencies:` in `pubspec.yaml` holds the Flutter SDK, Flutter's own first-party packages — `material_ui` (Material's home since Flutter 3.47) and its transitive set, all published by flutter.dev — plus `file_selector` (flutter.dev), the plugin behind the composer's attach button. Nothing else, and adding one is a decision rather than a convenience: it must not force configuration on hosts that never touch the feature (this is why `file_selector` and not `image_picker`, which writes a permission, a FileProvider and a Play-services entry into every host's Android manifest), and the PR has to argue it. What the one we have does ask for is documented: the picker needs macOS `com.apple.security.files.user-selected.read-only`. Dev dependencies (`flutter_test`, `flutter_lints`) are fine.
+- **The typefaces ship, they are not fetched.** Google Sans and Google Sans Code live in `packages/flow_ui/fonts/` under the SIL Open Font License, declared under `flutter: fonts:` with every cut (sans 400–700, mono 300–800, each upright and italic) and addressed as `package: 'flow_ui'`. Google Sans is a Latin subset — the full cuts are ~1.95 MB each — rebuilt by `packages/flow_ui/tool/subset_fonts.sh`; the scripts it drops fall back to the platform face. Nothing reaches the network for a glyph, so hosts need no INTERNET permission and no macOS network entitlement.
- **Nothing model-facing.** Components render state passed in and report intent out through callbacks. No prompts, schemas, provider/network calls, or any LLM awareness — that belongs to the layers built on top.
The theme, the conversation components (message, thread, streaming text, actions, loading), the composer and its menus, attachments with their preview, suggestions, and the chat surface are implemented; the roadmap below tracks the rest. Message content is modeled as typed parts (`lib/src/models/`) — sealed `FlowMessagePart` subtypes rendered by `FlowMessage`, with `FlowCustomPart` + `FlowCustomPartBuilder` as the extension seam for host-injected content.
@@ -16,7 +17,7 @@ The theme, the conversation components (message, thread, streaming text, actions
## Layout
- Root `pubspec.yaml` is the pub workspace (members under `workspace:`) with the melos scripts; root `analysis_options.yaml` (very_good_analysis) governs `tool/` and the SDK package only.
-- `packages/flow_ui/` — the published package: `lib/`, `example/` (the README's chat screen against Gemini), `assets/`, its own flutter_lints `analysis_options.yaml` and `.pubignore`.
+- `packages/flow_ui/` — the published package: `lib/`, `example/` (the README's chat screen against Gemini), `assets/`, `fonts/` (the bundled typefaces and their OFL texts), `tool/` (`subset_fonts.sh`, not published), its own flutter_lints `analysis_options.yaml` and `.pubignore`.
- `packages/stacflow/` — the StacFlow SDK package (see "SDK package"), with `example/` (the README's chat screen against Gemini; flutter_lints like the flow_ui example).
- `playground/` — the Flow UI Playground: a full Flutter app and workspace member depending on `flow_ui: ^0.4.0`. Use it to demo and manually exercise components (every component has a stage demo, with variant pills and code snippets).
- `docs/` — the Astro site behind flowui.stac.dev. `contracts/` — the SDK wire contract.
diff --git a/docs/README.md b/docs/README.md
index e5548c0..e5dff93 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -9,8 +9,8 @@ npm run dev # http://localhost:4321
npm run build # static site in dist/
```
-Google Sans loads from Google Fonts via the `` in `astro.config.mjs` —
-the same source the package fetches from at runtime. Brand colors in
+Google Sans loads from Google Fonts via the `` in `astro.config.mjs`,
+for this site's own chrome — the package bundles its own copy. Brand colors in
`src/styles/theme.css` mirror the package's design tokens in
`packages/flow_ui/lib/src/theme/flow_colors.dart`.
diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
index 7b52453..318a0cd 100644
--- a/docs/astro.config.mjs
+++ b/docs/astro.config.mjs
@@ -43,7 +43,8 @@ export default defineConfig({
customCss: ['./src/styles/theme.css'],
logo: { src: './src/assets/flow-ui-logo.svg', alt: '' },
head: [
- // Google Sans from Google Fonts — the same source the package fetches at runtime.
+ // Google Sans from Google Fonts, for this site's own chrome. The package
+ // bundles its own copy; this is the web page, not the Flutter build.
{ tag: 'link', attrs: { rel: 'preconnect', href: 'https://fonts.googleapis.com' } },
{
tag: 'link',
diff --git a/docs/src/content/docs/components/code-block.mdx b/docs/src/content/docs/components/code-block.mdx
index fd9adfb..6d329ac 100644
--- a/docs/src/content/docs/components/code-block.mdx
+++ b/docs/src/content/docs/components/code-block.mdx
@@ -137,7 +137,7 @@ FlowCodeLanguage.register(
## Theming
Token inks are theme tokens. Install a `FlowSyntaxColors` on the theme to
-recolor them; the code face is Google Sans Code, fetched through `google_fonts`, and
+recolor them; the code face is Google Sans Code, bundled with the package, and
swaps via the typography:
```dart title="Custom syntax palette and mono face"
diff --git a/docs/src/content/docs/getting-started.mdx b/docs/src/content/docs/getting-started.mdx
index a3ef287..deff9b4 100644
--- a/docs/src/content/docs/getting-started.mdx
+++ b/docs/src/content/docs/getting-started.mdx
@@ -23,10 +23,8 @@ dependencies:
flow_ui: ^0.4.0
```
-That brings in the Flutter SDK, `material_ui`, `file_selector` — the
-flutter.dev plugin behind the composer's attach button — and
-`google_fonts`, which fetches Google Sans and Google Sans Code at
-runtime. Nothing else.
+That brings in the Flutter SDK, `material_ui` and `file_selector` — the
+flutter.dev plugin behind the composer's attach button. Nothing else.
## Wire the theme
@@ -47,12 +45,12 @@ MaterialApp(
back to a preset matched to the ambient `ThemeData.brightness`.
-Google Sans — the face the design system is set in — is fetched through
-`google_fonts` on first use, so there is nothing to add to your `pubspec.yaml`
-fonts section. The fetch needs network access: `android.permission.INTERNET`
-in the main Android manifest (Flutter grants it only to debug and profile)
-and the `com.apple.security.network.client` entitlement in a sandboxed macOS
-app. To render offline on first launch, ship the files under `google_fonts/`.
+Google Sans — the face the design system is set in — ships with the package,
+along with Google Sans Code for code, so there is nothing to add to your
+`pubspec.yaml` fonts section and nothing to grant. No network access, no
+entitlement, and no fallback face on the first frame. Google Sans ships as a
+Latin subset; text in the scripts it leaves out falls back to the platform
+face, the way CJK does.
## Render a conversation
diff --git a/docs/src/content/docs/theming.mdx b/docs/src/content/docs/theming.mdx
index a089cfb..209a57e 100644
--- a/docs/src/content/docs/theming.mdx
+++ b/docs/src/content/docs/theming.mdx
@@ -117,6 +117,12 @@ one (`titleLargeEmphasised` at w600, `labelLargeEmphasised` at w500), so
emphasis in a heading or a control never needs an ad-hoc `copyWith`.
Styles are colorless; components pair them with color tokens at draw time.
+Both faces ship with the package — Google Sans at 400 through 700 and
+Google Sans Code at 300 through 800, each weight upright and italic — so
+every cut the scale asks for is a real one and nothing is fetched. Google
+Sans ships as a Latin subset; the scripts it leaves out fall back to the
+platform face.
+
To keep the scale and swap the face:
```dart title="Your brand's typeface"
diff --git a/packages/flow_ui/.pubignore b/packages/flow_ui/.pubignore
index fda8a65..2b7d3ad 100644
--- a/packages/flow_ui/.pubignore
+++ b/packages/flow_ui/.pubignore
@@ -27,3 +27,6 @@ doc/
# The workspace pubspec cannot resolve outside this repo; the example ships
# as readable source, not as a standalone package.
example/pubspec.yaml
+
+# The subsetting script is a maintainer tool, not package content.
+tool/
diff --git a/packages/flow_ui/CHANGELOG.md b/packages/flow_ui/CHANGELOG.md
index ab88fc0..08cf7ef 100644
--- a/packages/flow_ui/CHANGELOG.md
+++ b/packages/flow_ui/CHANGELOG.md
@@ -1,5 +1,8 @@
## 0.4.0 (unreleased)
+- Bundles Google Sans and Google Sans Code with the package instead of fetching them through google_fonts, which is dropped as a dependency. Nothing is fetched at runtime, so hosts no longer need android.permission.INTERNET or com.apple.security.network.client, and there is no fallback face on the first frame. Google Sans ships as a Latin subset; the scripts it leaves out fall back to the platform face, as CJK already did.
+- Makes `FlowTypography.standard` and `FlowTheme.typography` `const` again, and `FlowTypography.code` / `codeInline` plain fields with const defaults.
+- Deprecates `FlowTypography.recut` in favour of `copyWith(fontWeight:, fontStyle:)` — the bundled families declare every cut, so the engine resolves it from the weight on the style. Removed in 0.6.0.
- Adds FlowTool, the tool-call card: one mark morphs with the call's status (a still asterisk while staged, turning while it runs, a check or an error glyph once settled), the host's title and primary argument sit on the row, and the input and output render as code blocks behind a disclosure, with a failure readable without a tap. FlowToolPart renders it in a thread, keyed by the provider's call id and copying through FlowThread.onCodeCopy; FlowToolStyle joins the component styles with a FlowTheme.toolStyle default. Metrics are provisional pending a design frame.
## 0.3.0
diff --git a/packages/flow_ui/README.md b/packages/flow_ui/README.md
index 5e96837..91eb94a 100644
--- a/packages/flow_ui/README.md
+++ b/packages/flow_ui/README.md
@@ -78,14 +78,19 @@ MaterialApp(
)
```
-The default typography — Google Sans and Google Sans Code — arrives through
-[google_fonts](https://pub.dev/packages/google_fonts): each cut is fetched
-on first use and cached on the device, so there is no font to bundle. The
-fetch needs network access: `android.permission.INTERNET` in an Android
-app's main manifest (Flutter's template grants it only to debug and profile)
-and the `com.apple.security.network.client` entitlement in a sandboxed macOS
-app; without it text falls back to the platform face. To render offline on
-first launch, ship the files under a `google_fonts/` asset folder.
+The default typography — Google Sans and Google Sans Code — ships with the
+package. Nothing is fetched, so there is no permission, no entitlement and
+no fallback face on the first frame, and your app declares no font of its
+own. Google Sans ships as a Latin subset, which keeps the whole set near a
+megabyte; the scripts it leaves out fall back to the platform face, the way
+CJK does. To set the scale in your own typeface instead:
+
+```dart
+FlowTheme(
+ colors: FlowColors.light,
+ typography: FlowTypography.standard.withFontFamily('Inter'),
+)
+```
## Build a chat screen
@@ -306,6 +311,8 @@ cd playground && flutter run -d chrome
## License
-Code is released under the [MIT License](LICENSE). Google Sans and Google
-Sans Code are fetched from Google Fonts under the
-[SIL Open Font License](https://openfontlicense.org), not bundled.
+Code is released under the [MIT License](LICENSE). The bundled Google Sans
+and Google Sans Code fonts are licensed separately under the
+[SIL Open Font License](https://openfontlicense.org) — see
+[fonts/OFL.txt](fonts/OFL.txt) and
+[fonts/OFL-GoogleSansCode.txt](fonts/OFL-GoogleSansCode.txt).
diff --git a/packages/flow_ui/example/linux/flutter/generated_plugins.cmake b/packages/flow_ui/example/linux/flutter/generated_plugins.cmake
index a630fa9..2db3c22 100644
--- a/packages/flow_ui/example/linux/flutter/generated_plugins.cmake
+++ b/packages/flow_ui/example/linux/flutter/generated_plugins.cmake
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
- jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/packages/flow_ui/example/windows/flutter/generated_plugins.cmake b/packages/flow_ui/example/windows/flutter/generated_plugins.cmake
index 277a56f..a423a02 100644
--- a/packages/flow_ui/example/windows/flutter/generated_plugins.cmake
+++ b/packages/flow_ui/example/windows/flutter/generated_plugins.cmake
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
- jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/packages/flow_ui/fonts/GoogleSans-Bold.ttf b/packages/flow_ui/fonts/GoogleSans-Bold.ttf
new file mode 100644
index 0000000..1aa1f6b
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-Bold.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSans-BoldItalic.ttf b/packages/flow_ui/fonts/GoogleSans-BoldItalic.ttf
new file mode 100644
index 0000000..6384c4e
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-BoldItalic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSans-Italic.ttf b/packages/flow_ui/fonts/GoogleSans-Italic.ttf
new file mode 100644
index 0000000..cad35ed
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-Italic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSans-Medium.ttf b/packages/flow_ui/fonts/GoogleSans-Medium.ttf
new file mode 100644
index 0000000..13c2e56
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-Medium.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSans-MediumItalic.ttf b/packages/flow_ui/fonts/GoogleSans-MediumItalic.ttf
new file mode 100644
index 0000000..499fafc
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-MediumItalic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSans-Regular.ttf b/packages/flow_ui/fonts/GoogleSans-Regular.ttf
new file mode 100644
index 0000000..b94ce54
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-Regular.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSans-SemiBold.ttf b/packages/flow_ui/fonts/GoogleSans-SemiBold.ttf
new file mode 100644
index 0000000..bcbfe2f
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-SemiBold.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSans-SemiBoldItalic.ttf b/packages/flow_ui/fonts/GoogleSans-SemiBoldItalic.ttf
new file mode 100644
index 0000000..353d6a3
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSans-SemiBoldItalic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-Bold.ttf b/packages/flow_ui/fonts/GoogleSansCode-Bold.ttf
new file mode 100644
index 0000000..db086be
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-Bold.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-BoldItalic.ttf b/packages/flow_ui/fonts/GoogleSansCode-BoldItalic.ttf
new file mode 100644
index 0000000..257078f
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-BoldItalic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-ExtraBold.ttf b/packages/flow_ui/fonts/GoogleSansCode-ExtraBold.ttf
new file mode 100644
index 0000000..0cd7748
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-ExtraBold.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-ExtraBoldItalic.ttf b/packages/flow_ui/fonts/GoogleSansCode-ExtraBoldItalic.ttf
new file mode 100644
index 0000000..946a128
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-ExtraBoldItalic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-Italic.ttf b/packages/flow_ui/fonts/GoogleSansCode-Italic.ttf
new file mode 100644
index 0000000..332a93f
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-Italic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-Light.ttf b/packages/flow_ui/fonts/GoogleSansCode-Light.ttf
new file mode 100644
index 0000000..25f3bdb
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-Light.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-LightItalic.ttf b/packages/flow_ui/fonts/GoogleSansCode-LightItalic.ttf
new file mode 100644
index 0000000..f3270e7
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-LightItalic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-Medium.ttf b/packages/flow_ui/fonts/GoogleSansCode-Medium.ttf
new file mode 100644
index 0000000..b2323c4
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-Medium.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-MediumItalic.ttf b/packages/flow_ui/fonts/GoogleSansCode-MediumItalic.ttf
new file mode 100644
index 0000000..2112bad
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-MediumItalic.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-Regular.ttf b/packages/flow_ui/fonts/GoogleSansCode-Regular.ttf
new file mode 100644
index 0000000..478c523
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-Regular.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-SemiBold.ttf b/packages/flow_ui/fonts/GoogleSansCode-SemiBold.ttf
new file mode 100644
index 0000000..0a9742e
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-SemiBold.ttf differ
diff --git a/packages/flow_ui/fonts/GoogleSansCode-SemiBoldItalic.ttf b/packages/flow_ui/fonts/GoogleSansCode-SemiBoldItalic.ttf
new file mode 100644
index 0000000..867ea34
Binary files /dev/null and b/packages/flow_ui/fonts/GoogleSansCode-SemiBoldItalic.ttf differ
diff --git a/packages/flow_ui/fonts/OFL-GoogleSansCode.txt b/packages/flow_ui/fonts/OFL-GoogleSansCode.txt
new file mode 100644
index 0000000..2592415
--- /dev/null
+++ b/packages/flow_ui/fonts/OFL-GoogleSansCode.txt
@@ -0,0 +1,93 @@
+Copyright 2025 The Google Sans Code Project Authors (github.com/googlefonts/googlesans-code)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/packages/flow_ui/fonts/OFL.txt b/packages/flow_ui/fonts/OFL.txt
new file mode 100644
index 0000000..5d6f71c
--- /dev/null
+++ b/packages/flow_ui/fonts/OFL.txt
@@ -0,0 +1,91 @@
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/packages/flow_ui/lib/src/theme/flow_theme.dart b/packages/flow_ui/lib/src/theme/flow_theme.dart
index 2c2db4b..9941318 100644
--- a/packages/flow_ui/lib/src/theme/flow_theme.dart
+++ b/packages/flow_ui/lib/src/theme/flow_theme.dart
@@ -47,7 +47,7 @@ import 'flow_typography.dart';
class FlowTheme extends ThemeExtension {
const FlowTheme({
required this.colors,
- this._typography,
+ this.typography = FlowTypography.standard,
this.syntax,
this.composerStyle,
this.messageStyle,
@@ -74,11 +74,9 @@ class FlowTheme extends ThemeExtension {
const FlowTheme(colors: FlowColors.dark, syntax: FlowSyntaxColors.dark);
final FlowColors colors;
- final FlowTypography? _typography;
/// The type scale — [FlowTypography.standard] unless the host set one.
- /// Resolved on read, since the standard scale isn't `const`.
- FlowTypography get typography => _typography ?? FlowTypography.standard;
+ final FlowTypography typography;
/// Syntax token colors for code blocks. Null resolves to the preset
/// matching the ambient brightness — unlike [typography], the right
@@ -155,7 +153,7 @@ class FlowTheme extends ThemeExtension {
}) {
return FlowTheme(
colors: colors ?? this.colors,
- typography: typography ?? _typography,
+ typography: typography ?? this.typography,
syntax: syntax ?? this.syntax,
composerStyle: composerStyle ?? this.composerStyle,
messageStyle: messageStyle ?? this.messageStyle,
diff --git a/packages/flow_ui/lib/src/theme/flow_typography.dart b/packages/flow_ui/lib/src/theme/flow_typography.dart
index dccb048..200c41e 100644
--- a/packages/flow_ui/lib/src/theme/flow_typography.dart
+++ b/packages/flow_ui/lib/src/theme/flow_typography.dart
@@ -1,30 +1,32 @@
-import 'package:google_fonts/google_fonts.dart';
import 'package:material_ui/material_ui.dart';
-/// Google Sans for prose and Google Sans Code for code, served by
-/// `google_fonts`: each cut is fetched on first use and cached, or read from
-/// a `google_fonts/` asset folder when the host ships one.
+/// Google Sans for prose and Google Sans Code for code, both bundled with
+/// this package under `fonts/` (SIL Open Font License), every cut declared.
///
-/// google_fonts registers each cut as its own font family (`GoogleSans_600`,
-/// …), so roles are built through the package and re-weighted with
-/// [FlowTypography.recut] rather than `copyWith(fontWeight:)`.
+/// Addressed by [_fontPackage], so the families resolve to
+/// `packages/flow_ui/…` — a host gets the design's typefaces without
+/// declaring a font of its own, and nothing is fetched before the first
+/// paint.
const String _fontFamily = 'GoogleSans';
const String _monoFontFamily = 'GoogleSansCode';
+const String _fontPackage = 'flow_ui';
-TextStyle _sans(double size, FontWeight weight, double height) =>
- GoogleFonts.googleSans(fontSize: size, fontWeight: weight, height: height);
-
-TextStyle _mono(double size, FontWeight weight, double height) =>
- GoogleFonts.googleSansCode(
- fontSize: size,
- fontWeight: weight,
- height: height,
- );
-
-/// Defaults for the mono roles, resolved by the [FlowTypography.code] and
-/// [FlowTypography.codeInline] getters.
-final TextStyle _standardCode = _mono(13, FontWeight.w400, 1.6);
-final TextStyle _standardCodeInline = _mono(13, FontWeight.w400, 1.5);
+/// The standard cuts of the mono roles, which double as the constructor
+/// defaults for [FlowTypography.code] and [FlowTypography.codeInline].
+const TextStyle _standardCode = TextStyle(
+ fontFamily: _monoFontFamily,
+ package: _fontPackage,
+ fontSize: 13,
+ fontWeight: FontWeight.w400,
+ height: 1.6,
+);
+const TextStyle _standardCodeInline = TextStyle(
+ fontFamily: _monoFontFamily,
+ package: _fontPackage,
+ fontSize: 13,
+ fontWeight: FontWeight.w400,
+ height: 1.5,
+);
/// Text style tokens for flow_ui components.
///
@@ -68,8 +70,8 @@ class FlowTypography {
required this.labelMediumEmphasised,
required this.labelSmall,
required this.labelSmallEmphasised,
- this._code,
- this._codeInline,
+ this.code = _standardCode,
+ this.codeInline = _standardCodeInline,
});
final TextStyle displayLarge;
@@ -111,48 +113,207 @@ class FlowTypography {
final TextStyle labelSmall;
final TextStyle labelSmallEmphasised;
- final TextStyle? _code;
- final TextStyle? _codeInline;
-
/// The code block's body, in Google Sans Code: 13 on a taller line
/// (1.6), so a block reads as inset material, not a continuing
/// paragraph.
- TextStyle get code => _code ?? _standardCode;
+ final TextStyle code;
/// The mono face a step under prose size, for inline code spans.
- TextStyle get codeInline => _codeInline ?? _standardCodeInline;
+ final TextStyle codeInline;
/// The Flow type scale: Google Sans, with the code roles in Google Sans
/// Code.
- static final FlowTypography standard = FlowTypography(
- displayLarge: _sans(52, FontWeight.w400, 1.15),
- displayMedium: _sans(46, FontWeight.w400, 1.15),
- displaySmall: _sans(40, FontWeight.w400, 1.15),
- headlineLarge: _sans(36, FontWeight.w400, 1.2),
+ static const FlowTypography standard = FlowTypography(
+ displayLarge: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 52,
+ fontWeight: FontWeight.w400,
+ height: 1.15,
+ ),
+ displayMedium: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 46,
+ fontWeight: FontWeight.w400,
+ height: 1.15,
+ ),
+ displaySmall: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 40,
+ fontWeight: FontWeight.w400,
+ height: 1.15,
+ ),
+ headlineLarge: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 36,
+ fontWeight: FontWeight.w400,
+ height: 1.2,
+ ),
// The greeting: 32 regular.
- headlineMedium: _sans(32, FontWeight.w400, 1.2),
- headlineSmall: _sans(28, FontWeight.w400, 1.2),
- titleLarge: _sans(24, FontWeight.w400, 1.3),
- titleLargeEmphasised: _sans(24, FontWeight.w600, 1.3),
- titleMedium: _sans(21, FontWeight.w400, 1.3),
- titleMediumEmphasised: _sans(21, FontWeight.w600, 1.3),
- titleSmall: _sans(18, FontWeight.w400, 1.3),
- titleSmallEmphasised: _sans(18, FontWeight.w600, 1.3),
- bodyLarge: _sans(16, FontWeight.w400, 1.5),
- bodyLargeEmphasised: _sans(16, FontWeight.w500, 1.5),
- bodyLargeDark: _sans(16, FontWeight.w600, 1.5),
- bodyMedium: _sans(14, FontWeight.w400, 1.5),
- bodyMediumEmphasised: _sans(14, FontWeight.w500, 1.5),
- bodyMediumDark: _sans(14, FontWeight.w600, 1.5),
- bodySmall: _sans(12, FontWeight.w400, 1.5),
- bodySmallEmphasised: _sans(12, FontWeight.w500, 1.5),
- bodySmallDark: _sans(12, FontWeight.w600, 1.5),
- labelLarge: _sans(16, FontWeight.w400, 1.3),
- labelLargeEmphasised: _sans(16, FontWeight.w500, 1.3),
- labelMedium: _sans(14, FontWeight.w400, 1.3),
- labelMediumEmphasised: _sans(14, FontWeight.w500, 1.3),
- labelSmall: _sans(12, FontWeight.w400, 1.3),
- labelSmallEmphasised: _sans(12, FontWeight.w500, 1.3),
+ headlineMedium: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 32,
+ fontWeight: FontWeight.w400,
+ height: 1.2,
+ ),
+ headlineSmall: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 28,
+ fontWeight: FontWeight.w400,
+ height: 1.2,
+ ),
+ titleLarge: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 24,
+ fontWeight: FontWeight.w400,
+ height: 1.3,
+ ),
+ titleLargeEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 24,
+ fontWeight: FontWeight.w600,
+ height: 1.3,
+ ),
+ titleMedium: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 21,
+ fontWeight: FontWeight.w400,
+ height: 1.3,
+ ),
+ titleMediumEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 21,
+ fontWeight: FontWeight.w600,
+ height: 1.3,
+ ),
+ titleSmall: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 18,
+ fontWeight: FontWeight.w400,
+ height: 1.3,
+ ),
+ titleSmallEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ height: 1.3,
+ ),
+ bodyLarge: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ height: 1.5,
+ ),
+ bodyLargeEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ height: 1.5,
+ ),
+ bodyLargeDark: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ height: 1.5,
+ ),
+ bodyMedium: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ height: 1.5,
+ ),
+ bodyMediumEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ height: 1.5,
+ ),
+ bodyMediumDark: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 14,
+ fontWeight: FontWeight.w600,
+ height: 1.5,
+ ),
+ bodySmall: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 12,
+ fontWeight: FontWeight.w400,
+ height: 1.5,
+ ),
+ bodySmallEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ height: 1.5,
+ ),
+ bodySmallDark: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 12,
+ fontWeight: FontWeight.w600,
+ height: 1.5,
+ ),
+ labelLarge: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ height: 1.3,
+ ),
+ labelLargeEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ height: 1.3,
+ ),
+ labelMedium: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ height: 1.3,
+ ),
+ labelMediumEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ height: 1.3,
+ ),
+ labelSmall: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 12,
+ fontWeight: FontWeight.w400,
+ height: 1.3,
+ ),
+ labelSmallEmphasised: TextStyle(
+ fontFamily: _fontFamily,
+ package: _fontPackage,
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ height: 1.3,
+ ),
);
/// The same scale set in [fontFamily] instead of Google Sans.
@@ -160,6 +321,10 @@ class FlowTypography {
/// [withCodeFontFamily].
///
/// Pass [package] when the font ships inside a package rather than the app.
+ /// flow_ui's own default is `GoogleSans` in `package: 'flow_ui'`, which is a
+ /// different family from a `GoogleSans` an app declares itself — call this
+ /// with no [package] to point the scale at the app's copy.
+ ///
/// Each style is rebuilt from the four things the scale carries — size,
/// weight, line height and tracking — so anything else set on a customised
/// style is dropped rather than half-kept.
@@ -223,34 +388,23 @@ class FlowTypography {
return copyWith(code: reface(code), codeInline: reface(codeInline));
}
- /// [style] at [fontWeight] and/or [fontStyle], in the matching cut.
+ /// [style] at [fontWeight] and/or [fontStyle].
///
- /// google_fonts registers each cut as its own font family, so `copyWith`
- /// on a token would leave the engine faking the weight; this loads the
- /// real cut. Styles in any other face pass through `copyWith`.
+ /// Equivalent to `style.copyWith(fontWeight: …, fontStyle: …)`. It existed
+ /// only while the typefaces came from `google_fonts`, which registered each
+ /// cut as its own family (`GoogleSans_600`) that `copyWith` could not
+ /// reach. The families now ship with the package with every cut declared,
+ /// so the engine resolves the real cut from the weight on the style — and
+ /// for any other face, the nearest weight that face declares.
+ @Deprecated(
+ 'Use style.copyWith(fontWeight:, fontStyle:) — the bundled families '
+ 'declare every cut. Will be removed in flow_ui 0.6.0.',
+ )
static TextStyle recut(
TextStyle style, {
FontWeight? fontWeight,
FontStyle? fontStyle,
- }) {
- // google_fonts names each cut `_`.
- final family = style.fontFamily ?? '';
- if (family.startsWith('${_fontFamily}_')) {
- return GoogleFonts.googleSans(
- textStyle: style,
- fontWeight: fontWeight,
- fontStyle: fontStyle,
- );
- }
- if (family.startsWith('${_monoFontFamily}_')) {
- return GoogleFonts.googleSansCode(
- textStyle: style,
- fontWeight: fontWeight,
- fontStyle: fontStyle,
- );
- }
- return style.copyWith(fontWeight: fontWeight, fontStyle: fontStyle);
- }
+ }) => style.copyWith(fontWeight: fontWeight, fontStyle: fontStyle);
FlowTypography copyWith({
TextStyle? displayLarge,
diff --git a/packages/flow_ui/lib/src/utils/flow_chip_text.dart b/packages/flow_ui/lib/src/utils/flow_chip_text.dart
index 6ace396..f5ae6e6 100644
--- a/packages/flow_ui/lib/src/utils/flow_chip_text.dart
+++ b/packages/flow_ui/lib/src/utils/flow_chip_text.dart
@@ -1,7 +1,6 @@
import 'package:flutter/rendering.dart';
import 'package:material_ui/material_ui.dart';
-import '../theme/flow_typography.dart';
import 'flow_selection.dart';
/// Internal — not exported from the package barrel.
@@ -83,7 +82,7 @@ class _ChipParagraph extends StatelessWidget {
final defaults = DefaultTextStyle.of(context);
var style = defaults.style;
if (MediaQuery.boldTextOf(context)) {
- style = FlowTypography.recut(style, fontWeight: FontWeight.bold);
+ style = style.copyWith(fontWeight: FontWeight.bold);
}
final registrar = SelectionContainer.maybeOf(context);
final selectionStyle = DefaultSelectionStyle.of(context);
diff --git a/packages/flow_ui/lib/src/widgets/flow_attachment_group.dart b/packages/flow_ui/lib/src/widgets/flow_attachment_group.dart
index 29402c3..b3735cd 100644
--- a/packages/flow_ui/lib/src/widgets/flow_attachment_group.dart
+++ b/packages/flow_ui/lib/src/widgets/flow_attachment_group.dart
@@ -5,7 +5,6 @@ import 'package:material_ui/material_ui.dart';
import '../models/flow_attachment.dart';
import '../theme/flow_theme.dart';
-import '../theme/flow_typography.dart';
import '../utils/flow_attachment_error.dart';
import '../utils/flow_circle_button.dart';
import 'flow_attachment_preview.dart';
@@ -476,10 +475,10 @@ class _TypePill extends StatelessWidget {
child: SelectionContainer.disabled(
child: Text(
kind,
- style: FlowTypography.recut(
- context.flowTypography.labelSmall,
+ style: context.flowTypography.labelSmall.copyWith(
fontWeight: FontWeight.w600,
- ).copyWith(color: colors.surfaceBright),
+ color: colors.surfaceBright,
+ ),
),
),
),
diff --git a/packages/flow_ui/lib/src/widgets/flow_confirmation.dart b/packages/flow_ui/lib/src/widgets/flow_confirmation.dart
index 8b03a1b..8b07ba2 100644
--- a/packages/flow_ui/lib/src/widgets/flow_confirmation.dart
+++ b/packages/flow_ui/lib/src/widgets/flow_confirmation.dart
@@ -266,8 +266,7 @@ class FlowConfirmation extends StatelessWidget {
_ConfirmationButton(
label: approveLabel,
onTap: onApprove!,
- textStyle: FlowTypography.recut(
- typography.labelMedium,
+ textStyle: typography.labelMedium.copyWith(
fontWeight: FontWeight.w600,
),
background: effective?.approveButtonColor ?? colors.inverseSurface,
diff --git a/packages/flow_ui/lib/src/widgets/flow_error_state.dart b/packages/flow_ui/lib/src/widgets/flow_error_state.dart
index 26da2cb..41d78f1 100644
--- a/packages/flow_ui/lib/src/widgets/flow_error_state.dart
+++ b/packages/flow_ui/lib/src/widgets/flow_error_state.dart
@@ -2,7 +2,6 @@ import 'package:material_ui/material_ui.dart';
import '../styles/flow_error_state_style.dart';
import '../theme/flow_theme.dart';
-import '../theme/flow_typography.dart';
import '../utils/flow_selection.dart';
/// A failure surface: an error glyph and a host-written explanation on a
@@ -255,10 +254,10 @@ class _RetryButtonState extends State<_RetryButton> {
SelectionContainer.disabled(
child: Text(
label,
- style: FlowTypography.recut(
- typography.labelMedium,
+ style: typography.labelMedium.copyWith(
fontWeight: FontWeight.w600,
- ).copyWith(color: foreground),
+ color: foreground,
+ ),
),
),
],
diff --git a/packages/flow_ui/lib/src/widgets/flow_markdown.dart b/packages/flow_ui/lib/src/widgets/flow_markdown.dart
index 9d03756..4f15223 100644
--- a/packages/flow_ui/lib/src/widgets/flow_markdown.dart
+++ b/packages/flow_ui/lib/src/widgets/flow_markdown.dart
@@ -810,10 +810,10 @@ class _FlowMarkdownState extends State {
.merge(_mdStyle?.inlineCodeStyle);
}
if (run.bold) {
- style = FlowTypography.recut(style, fontWeight: FontWeight.w600);
+ style = style.copyWith(fontWeight: FontWeight.w600);
}
if (run.italic) {
- style = FlowTypography.recut(style, fontStyle: FontStyle.italic);
+ style = style.copyWith(fontStyle: FontStyle.italic);
}
final linked = run.linkHref != null && widget.onLinkTap != null;
diff --git a/packages/flow_ui/pubspec.yaml b/packages/flow_ui/pubspec.yaml
index 74037cc..5e2a1b4 100644
--- a/packages/flow_ui/pubspec.yaml
+++ b/packages/flow_ui/pubspec.yaml
@@ -21,9 +21,6 @@ resolution: workspace
dependencies:
flutter:
sdk: flutter
- # Google Sans and Google Sans Code, fetched from Google Fonts at runtime —
- # the one dependency outside flutter.dev's packages; no font files ship.
- google_fonts: ^8.2.1
material_ui: ^1.0.0
# The composer's built-in attach button opens this. flutter.dev-published
# and endorsed on all six platforms, and — unlike image_picker — it adds
@@ -40,3 +37,75 @@ dev_dependencies:
sdk: flutter
flutter_lints: ^6.0.0
+flutter:
+ # Google Sans is the typeface of the Flow UI design file, and Google Sans
+ # Code its code face. Both ship with the package (SIL Open Font License, see
+ # fonts/OFL.txt and fonts/OFL-GoogleSansCode.txt) so the default theme
+ # renders as designed without the host bundling a font, without a font
+ # dependency, and without a network round trip before the first paint.
+ #
+ # Static instances rather than the variable TTFs: Flutter maps FontWeight to
+ # the nearest declared weight, so a weight axis would not be interpolated.
+ # Google Sans is a Latin subset — at 1.95 MB a cut it would otherwise be the
+ # largest thing every host app inherits, and the scripts it drops fall back
+ # to the platform face the way CJK already does. tool/subset_fonts.sh
+ # rebuilds the directory.
+ fonts:
+ - family: GoogleSans
+ fonts:
+ - asset: fonts/GoogleSans-Regular.ttf
+ weight: 400
+ - asset: fonts/GoogleSans-Italic.ttf
+ weight: 400
+ style: italic
+ - asset: fonts/GoogleSans-Medium.ttf
+ weight: 500
+ - asset: fonts/GoogleSans-MediumItalic.ttf
+ weight: 500
+ style: italic
+ - asset: fonts/GoogleSans-SemiBold.ttf
+ weight: 600
+ - asset: fonts/GoogleSans-SemiBoldItalic.ttf
+ weight: 600
+ style: italic
+ - asset: fonts/GoogleSans-Bold.ttf
+ weight: 700
+ - asset: fonts/GoogleSans-BoldItalic.ttf
+ weight: 700
+ style: italic
+ - family: GoogleSansCode
+ fonts:
+ - asset: fonts/GoogleSansCode-Light.ttf
+ weight: 300
+ - asset: fonts/GoogleSansCode-LightItalic.ttf
+ weight: 300
+ style: italic
+ - asset: fonts/GoogleSansCode-Regular.ttf
+ weight: 400
+ - asset: fonts/GoogleSansCode-Italic.ttf
+ weight: 400
+ style: italic
+ - asset: fonts/GoogleSansCode-Medium.ttf
+ weight: 500
+ - asset: fonts/GoogleSansCode-MediumItalic.ttf
+ weight: 500
+ style: italic
+ - asset: fonts/GoogleSansCode-SemiBold.ttf
+ weight: 600
+ - asset: fonts/GoogleSansCode-SemiBoldItalic.ttf
+ weight: 600
+ style: italic
+ - asset: fonts/GoogleSansCode-Bold.ttf
+ weight: 700
+ - asset: fonts/GoogleSansCode-BoldItalic.ttf
+ weight: 700
+ style: italic
+ - asset: fonts/GoogleSansCode-ExtraBold.ttf
+ weight: 800
+ - asset: fonts/GoogleSansCode-ExtraBoldItalic.ttf
+ weight: 800
+ style: italic
+ licenses:
+ - fonts/OFL.txt
+ - fonts/OFL-GoogleSansCode.txt
+
diff --git a/packages/flow_ui/tool/subset_fonts.sh b/packages/flow_ui/tool/subset_fonts.sh
new file mode 100755
index 0000000..fbc17e7
--- /dev/null
+++ b/packages/flow_ui/tool/subset_fonts.sh
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+#
+# Rebuilds packages/flow_ui/fonts/ from a Google Fonts download.
+#
+# pip install fonttools
+# tool/subset_fonts.sh ~/Downloads/Google_Sans,Google_Sans_Code
+#
+# Google Sans ships as a Latin subset: the full cuts are ~1.95 MB each, most
+# of it scripts a chat UI gets from the platform face anyway. Google Sans Code
+# is copied whole — it is already small, and the coverage a subset would drop
+# (box drawing, arrows, technical symbols) is what a code face is for.
+#
+# The static cuts, not the variable TTFs: Flutter maps FontWeight to the
+# nearest declared weight, so a weight axis would not be interpolated. The
+# non-suffixed set, not the _17pt one — opsz runs 17-18 with a default of 18,
+# so these are the default instance.
+
+set -euo pipefail
+
+SRC="${1:-}"
+if [[ -z "$SRC" || ! -d "$SRC/Google_Sans/static" ]]; then
+ echo "usage: $0 " >&2
+ exit 1
+fi
+
+OUT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/fonts"
+mkdir -p "$OUT"
+
+# Google Fonts' latin, latin-ext and vietnamese ranges, plus the combining
+# marks those need decomposed, and the symbols flow_ui draws itself — the
+# nested list bullets in flow_markdown.dart are U+25E6 and U+25AA, which sit
+# outside every Latin range.
+RANGES="U+0000-00FF,U+0100-024F,U+0259,U+0300-036F,U+1E00-1EFF"
+RANGES="$RANGES,U+2000-206F,U+2070-209F,U+20A0-20CF,U+2100-218F,U+2190-21FF"
+RANGES="$RANGES,U+2212,U+2215,U+25A0-25FF,U+2713-2714,U+FB00-FB04,U+FEFF,U+FFFD"
+
+SANS=(Regular Italic Medium MediumItalic SemiBold SemiBoldItalic Bold BoldItalic)
+MONO=(
+ Light LightItalic Regular Italic Medium MediumItalic
+ SemiBold SemiBoldItalic Bold BoldItalic ExtraBold ExtraBoldItalic
+)
+
+for cut in "${SANS[@]}"; do
+ # --name-IDs='*' keeps the copyright, trademark and licence records, which
+ # pyftsubset drops by default and the OFL requires us to carry.
+ pyftsubset "$SRC/Google_Sans/static/GoogleSans-$cut.ttf" \
+ --output-file="$OUT/GoogleSans-$cut.ttf" \
+ --unicodes="$RANGES" \
+ --name-IDs='*' \
+ --notdef-outline \
+ --recalc-timestamp=0
+done
+
+for cut in "${MONO[@]}"; do
+ cp "$SRC/Google_Sans_Code/static/GoogleSansCode-$cut.ttf" "$OUT/"
+done
+
+cp "$SRC/Google_Sans/OFL.txt" "$OUT/OFL.txt"
+cp "$SRC/Google_Sans_Code/OFL.txt" "$OUT/OFL-GoogleSansCode.txt"
+
+ls -l "$OUT"
+du -sh "$OUT"
diff --git a/packages/stacflow/example/linux/flutter/generated_plugins.cmake b/packages/stacflow/example/linux/flutter/generated_plugins.cmake
index a630fa9..2db3c22 100644
--- a/packages/stacflow/example/linux/flutter/generated_plugins.cmake
+++ b/packages/stacflow/example/linux/flutter/generated_plugins.cmake
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
- jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/packages/stacflow/example/windows/flutter/generated_plugins.cmake b/packages/stacflow/example/windows/flutter/generated_plugins.cmake
index 277a56f..a423a02 100644
--- a/packages/stacflow/example/windows/flutter/generated_plugins.cmake
+++ b/packages/stacflow/example/windows/flutter/generated_plugins.cmake
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
- jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/playground/android/app/src/main/AndroidManifest.xml b/playground/android/app/src/main/AndroidManifest.xml
index c1b2d6b..6fa5570 100644
--- a/playground/android/app/src/main/AndroidManifest.xml
+++ b/playground/android/app/src/main/AndroidManifest.xml
@@ -1,6 +1,4 @@
-
- {
const SizedBox(width: _glyphGap),
Text(
widget.label,
- style: FlowTypography.recut(
- typography.labelMedium,
+ style: typography.labelMedium.copyWith(
fontWeight: FontWeight.w600,
- ).copyWith(color: foreground),
+ color: foreground,
+ ),
),
],
),
diff --git a/playground/lib/src/shell_text.dart b/playground/lib/src/shell_text.dart
index 78f6ac5..d25fa6d 100644
--- a/playground/lib/src/shell_text.dart
+++ b/playground/lib/src/shell_text.dart
@@ -1,8 +1,9 @@
-import 'package:google_fonts/google_fonts.dart';
import 'package:material_ui/material_ui.dart';
-/// A chrome text style in Google Sans. The chrome keeps its bespoke sizes
-/// from the Claude Design prototype and colors them from flow_ui's tokens.
+/// A chrome text style in Google Sans — the face flow_ui bundles, addressed
+/// by its package, so the playground declares no font of its own. The chrome
+/// keeps its bespoke sizes from the Claude Design prototype and colors them
+/// from flow_ui's tokens.
TextStyle shellText({
double? size,
FontWeight? weight,
@@ -10,7 +11,9 @@ TextStyle shellText({
double? letterSpacing,
double? height,
}) {
- return GoogleFonts.googleSans(
+ return TextStyle(
+ fontFamily: 'GoogleSans',
+ package: 'flow_ui',
fontSize: size,
fontWeight: weight,
color: color,
diff --git a/playground/linux/flutter/generated_plugins.cmake b/playground/linux/flutter/generated_plugins.cmake
index a630fa9..2db3c22 100644
--- a/playground/linux/flutter/generated_plugins.cmake
+++ b/playground/linux/flutter/generated_plugins.cmake
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
- jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/playground/macos/Runner/DebugProfile.entitlements b/playground/macos/Runner/DebugProfile.entitlements
index 566fcb1..0f1b1b5 100644
--- a/playground/macos/Runner/DebugProfile.entitlements
+++ b/playground/macos/Runner/DebugProfile.entitlements
@@ -10,7 +10,5 @@
com.apple.security.network.server
- com.apple.security.network.client
-
diff --git a/playground/macos/Runner/Release.entitlements b/playground/macos/Runner/Release.entitlements
index 625af03..18aff0c 100644
--- a/playground/macos/Runner/Release.entitlements
+++ b/playground/macos/Runner/Release.entitlements
@@ -6,7 +6,5 @@
com.apple.security.files.user-selected.read-only
- com.apple.security.network.client
-
diff --git a/playground/pubspec.yaml b/playground/pubspec.yaml
index 299473c..58c7405 100644
--- a/playground/pubspec.yaml
+++ b/playground/pubspec.yaml
@@ -52,8 +52,6 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
- # Google Sans for the chrome.
- google_fonts: ^8.2.1
dev_dependencies:
flutter_test:
diff --git a/playground/windows/flutter/generated_plugins.cmake b/playground/windows/flutter/generated_plugins.cmake
index 277a56f..a423a02 100644
--- a/playground/windows/flutter/generated_plugins.cmake
+++ b/playground/windows/flutter/generated_plugins.cmake
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
- jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/pubspec.lock b/pubspec.lock
index 95b9e70..bf57990 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -81,14 +81,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.2"
- code_assets:
- dependency: transitive
- description:
- name: code_assets
- sha256: cfd4f5f575a49c5f10ca856e9846073f1e6c3ee94912377eea5f6cefc5272941
- url: "https://pub.dev"
- source: hosted
- version: "2.0.0"
collection:
dependency: transitive
description:
@@ -113,14 +105,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.5+4"
- crypto:
- dependency: transitive
- description:
- name: crypto
- sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
- url: "https://pub.dev"
- source: hosted
- version: "3.0.7"
cupertino_icons:
dependency: transitive
description:
@@ -277,14 +261,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "18.0.1"
- google_fonts:
- dependency: transitive
- description:
- name: google_fonts
- sha256: e3cb3ee6b47fd2472c23de6da5744796a4da195137759ddb3fbcc9467b7b3c7d
- url: "https://pub.dev"
- source: hosted
- version: "8.2.1"
graphs:
dependency: transitive
description:
@@ -293,14 +269,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
- hooks:
- dependency: transitive
- description:
- name: hooks
- sha256: eaac480a35ec0814146c2c48d96aaa829e0e44a7662c88ae84c9edf4bc35651f
- url: "https://pub.dev"
- source: hosted
- version: "2.2.0"
http:
dependency: transitive
description:
@@ -333,30 +301,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
- jni:
- dependency: transitive
- description:
- name: jni
- sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3
- url: "https://pub.dev"
- source: hosted
- version: "1.0.3"
- jni_flutter:
- dependency: transitive
- description:
- name: jni_flutter
- sha256: "7b717011ea40d04fd47c2731d3d1d36eb99eba3435c2753d62489e8c3c9991d5"
- url: "https://pub.dev"
- source: hosted
- version: "1.0.2"
- jni_util:
- dependency: transitive
- description:
- name: jni_util
- sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f"
- url: "https://pub.dev"
- source: hosted
- version: "1.0.0"
json_annotation:
dependency: transitive
description:
@@ -453,22 +397,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.5"
- objective_c:
- dependency: transitive
- description:
- name: objective_c
- sha256: ad56fd53a78ff6b1472fa59ff2a4e8b8ccabafc586fc263a1dfad0b99b5553e3
- url: "https://pub.dev"
- source: hosted
- version: "9.6.0"
- package_config:
- dependency: transitive
- description:
- name: package_config
- sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
- url: "https://pub.dev"
- source: hosted
- version: "2.2.0"
path:
dependency: transitive
description:
@@ -485,54 +413,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
- path_provider:
- dependency: transitive
- description:
- name: path_provider
- sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
- url: "https://pub.dev"
- source: hosted
- version: "2.1.6"
- path_provider_android:
- dependency: transitive
- description:
- name: path_provider_android
- sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
- url: "https://pub.dev"
- source: hosted
- version: "2.3.1"
- path_provider_foundation:
- dependency: transitive
- description:
- name: path_provider_foundation
- sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
- url: "https://pub.dev"
- source: hosted
- version: "2.6.0"
- path_provider_linux:
- dependency: transitive
- description:
- name: path_provider_linux
- sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
- url: "https://pub.dev"
- source: hosted
- version: "2.2.2"
- path_provider_platform_interface:
- dependency: transitive
- description:
- name: path_provider_platform_interface
- sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
- url: "https://pub.dev"
- source: hosted
- version: "2.1.3"
- path_provider_windows:
- dependency: transitive
- description:
- name: path_provider_windows
- sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
- url: "https://pub.dev"
- source: hosted
- version: "2.3.0"
petitparser:
dependency: transitive
description:
@@ -613,14 +493,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.6.0"
- record_use:
- dependency: transitive
- description:
- name: record_use
- sha256: "1cb8564af8d43b464294411db9217f5ec04891c6f22ee2c32d73ae05e88a6bd2"
- url: "https://pub.dev"
- source: hosted
- version: "1.1.1"
sky_engine:
dependency: transitive
description: flutter
@@ -738,14 +610,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
- xdg_directories:
- dependency: transitive
- description:
- name: xdg_directories
- sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
- url: "https://pub.dev"
- source: hosted
- version: "1.1.0"
xml:
dependency: transitive
description: