Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions lib/core/themes/app_theme.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,59 @@
import 'package:bb_mobile/core/themes/colors.dart';
import 'package:bb_mobile/core/themes/fonts.dart';
import 'package:bull_ui/bull_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

enum AppThemeType { light, dark }

/// Builds the `bull_ui` [BullTheme] colour extension from an [AppColors]
/// palette. `bull_ui` is brightness-agnostic — the app hands it the right
/// palette per brightness, so `AppColors` stays the single source of truth.
BullTheme _bullThemeFrom(AppColors colors) {
return BullTheme(
primary: colors.primary,
onPrimary: colors.onPrimary,
primaryFixed: colors.primaryFixed,
onPrimaryFixed: colors.onPrimaryFixed,
secondary: colors.secondary,
onSecondary: colors.onSecondary,
secondaryFixed: colors.secondaryFixed,
secondaryFixedDim: colors.secondaryFixedDim,
onSecondaryFixed: colors.onSecondaryFixed,
tertiary: colors.tertiary,
onTertiary: colors.onTertiary,
tertiaryContainer: colors.tertiaryContainer,
background: colors.background,
surface: colors.surface,
surfaceContainer: colors.surfaceContainer,
surfaceContainerHighest: colors.surfaceContainerHighest,
surfaceBright: colors.surfaceBright,
onSurface: colors.onSurface,
onSurfaceVariant: colors.onSurfaceVariant,
inverseSurface: colors.inverseSurface,
cardBackground: colors.cardBackground,
text: colors.text,
textMuted: colors.textMuted,
border: colors.border,
outline: colors.outline,
outlineVariant: colors.outlineVariant,
error: colors.error,
onError: colors.onError,
errorContainer: colors.errorContainer,
success: colors.success,
warning: colors.warning,
warningContainer: colors.warningContainer,
info: colors.info,
scrim: colors.scrim,
overlay: colors.overlay,
transparent: colors.transparent,
surfaceFixed: colors.surfaceFixed,
onSurfaceFixed: colors.onSurfaceFixed,
shimmerBase: colors.shimmerBase,
shimmerHighlight: colors.shimmerHighlight,
);
}

class AppTheme {
static ThemeData themeData(AppThemeType themeType) {
final colors = themeType == AppThemeType.dark
Expand All @@ -18,6 +67,7 @@ class AppTheme {
return ThemeData(
useMaterial3: true,
brightness: brightness,
extensions: [_bullThemeFrom(colors)],
colorScheme: ColorScheme.fromSeed(
seedColor: colors.primary,
brightness: brightness,
Expand Down
17 changes: 16 additions & 1 deletion makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all setup clean deps deps-update bootstrap analyze build-runner translations hooks ios-pod-update drift-migrations devcontainer container-tools container-app android release debug beta verify test unit-test integration-test fvm-check
.PHONY: all setup clean deps deps-update bootstrap analyze build-runner translations hooks ios-pod-update drift-migrations devcontainer container-tools container-app android release debug beta verify test unit-test integration-test catalogue fvm-check

fvm-check:
@echo "🔍 Checking FVM"
Expand Down Expand Up @@ -190,6 +190,12 @@ test: unit-test integration-test
unit-test:
@echo "🏃‍ running unit tests"
@fvm flutter test test/ --reporter=compact
@for p in packages/*/; do \
if [ -d "$${p}test" ]; then \
echo "🏃‍ running $${p}test"; \
( cd "$$p" && fvm flutter test --reporter=compact ); \
fi; \
done

# integration_test/all_test.dart is a single aggregator entrypoint: it runs
# Bull.init() once, then every test file's main(isInitialized: true). On the
Expand All @@ -203,3 +209,12 @@ integration-test:
@echo "🧪 integration tests"
@fvm dart run tool/gen_all_test.dart
@fvm flutter test integration_test/all_test.dart --reporter=expanded

# Build & render the bull_ui design-system catalogue (Widgetbook) locally in the
# browser. Dev-only tooling — never shipped in the app. Regenerates the
# @UseCase directories, then runs the catalogue app on Chrome (hot-reload).
catalogue:
@echo "📚 Building & rendering the bull_ui catalogue in the browser"
@cd packages/bull_ui_catalogue && \
fvm dart run build_runner build --delete-conflicting-outputs && \
fvm flutter run -d chrome
39 changes: 39 additions & 0 deletions packages/bull_ui/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Changelog

## Unreleased

- **Foundation tokens now follow the design system / official theme.** `BullTheme`
is the full 1:1 `AppColors` mirror (all ~40 fields, official names); `BullRadius`
adopts the design scale (`0/4/8/12/16/28/32/999`) and `BullSpacing` the design
scale (`0/4/8/12/16/24/32/48/64`); `BullTextStyles` is **removed** — type comes
from the Material `TextTheme` (`AppFonts`). All `Bull*` components were remapped
(`context.bull.red→primary`, `btc→onTertiary`, `muted→textMuted`,
`card→cardBackground`, radius/spacing → new scales). Tokens are fixed (not
screen-scaled); responsiveness is a layout concern.
- `BullSwipeAction` action panel wrapped in `FittedBox(scaleDown)` so the
icon+label never overflows a short row.

- Expand `BullTheme` with `secondary`, `onSecondary`, `secondaryFixedDim`,
`border`, `onSurface`, `onSurfaceVariant` and `scrim` (plus `copyWith`/`lerp`
and the app-side `_bullThemeFrom` builder), sourced from `AppColors`.
- Duplicate the dependency-clean set of `lib/core/widgets/**` as `Bull*`
copies: `BullInputText`, `BullPasteInput`, `BullDropdown`,
`BullSelectableList`, `BullDialPad`, `BullAmountInputFormatter`,
`BullLowerCaseTextFormatter`, `BullCountdown`, `BullFadingLinearProgress`,
`BullScrollableColumn`, `BullStackedPage`, `BullPullableBody`,
`BullOptionsTag`, `BullInfoCard`, `BullPriceCard`, `BullBackupOptionCard`,
`BullBorderedTile`, `BullTransactionDirectionBadge`, `BullSettingsEntryItem`,
`BullTabMenuVerticalButton`, `BullViewerActionButton`. See the README
"Components" / "Not yet migrated" tables.
- Duplicate the remaining dependency-clean widgets: `BullDetailsTable` +
`BullDetailsTableItem` (`DetailsTable` / `DetailsTableItem`),
`BullPickerSheet` (`BBPickerSheet`) and `BullInstructionsSheet`
(`InstructionsBottomSheet`). No new `BullTheme` fields were required — they
reuse the existing `surface`, `border`, `onSurface`, `red` and `text` colours.
- Widget tests for `BullDropdown`, `BullCountdown` and `BullDialPad`.

## 0.0.1

- Seed the `bull_ui` design-system package: `BullTheme` (`ThemeExtension`),
`BullRadius`/`BullSpacing`/`BullTextStyles`/`BullIcon` tokens, and the first
set of `Bull*` components for the Coins view (issue #760).
103 changes: 103 additions & 0 deletions packages/bull_ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# bull_ui

The Bull Bitcoin design-system component library — the go-forward source of truth for
shared UI primitives. It is the first melos workspace member (`packages/bull_ui`).

Every public widget is prefixed `Bull*` (e.g. `BullText`, `BullButton`, `BullScaffold`).
This prefix is provenance: **`BB*` = legacy widgets in `lib/core/widgets`, `Bull*` = here**.
The two coexist during the incremental migration; `BB*` originals are retired
feature-by-feature in later scoped PRs as usages migrate. Nothing is moved or renamed —
`bull_ui` components are duplicated copies, so existing app code is untouched.

## Theme injection

`bull_ui` is **brightness-agnostic** and never hardcodes a colour. The app injects the
palette: it builds a `BullTheme` (a `ThemeExtension`) from `AppColors.light` and another
from `AppColors.dark`, and registers each on the matching `ThemeData` via `extensions:`.
Components read colours through `context.bull` (e.g. `context.bull.primary`). `BullTheme` is a
**1:1 mirror of the app's `AppColors`** — same field names — so the app wires every field through
without translation. Derived fills use `tokenColor.withValues(alpha: …)` so they adapt to both
light and dark automatically.

## Foundation tokens

Brightness-invariant tokens are static consts, from the design system:

- **`BullRadius`** — `zero·xs·sm·md·lg·xl·xxl·full` = `0·4·8·12·16·28·32·999`.
- **`BullSpacing`** — `zero·xxs·xs·sm·md·lg·xl·xxl·xxxl` = `0·4·8·12·16·24·32·48·64`.
- **Type** comes from the Material `TextTheme` (`Theme.of(context).textTheme`, sourced from
`AppFonts`) — there is no separate text-token class.
- **Icons** via `BullIcon(BullIcons.…)`.

**Tokens are fixed — never scaled by screen size.** Responsiveness is a *layout* concern
(breakpoints, `LayoutBuilder`, max-width wrappers, adaptive columns), not a token concern; text
already honours the user's `MediaQuery.textScaler`. The catalogue (`packages/bull_ui_catalogue`)
has a `Foundation/` section showcasing colours, radius, spacing and the text scale.

Fonts (Golos Text, Bebas Neue) are declared at the app root and resolve by family name
across the workspace — `bull_ui` references them by name and does not re-ship the `.ttf`s.

## Import surface

Consumers import a single barrel:

```dart
import 'package:bull_ui/bull_ui.dart';
```

It re-exports a curated `show` list of `package:flutter/widgets.dart` layout/foundation
symbols plus every `Bull*` component and the theme. Internals live under `lib/src/`.

## Components

Duplicated from `lib/core/widgets/**` as dependency-clean `Bull*` copies (the
`BB*`/core originals are left untouched). Grouped by barrel category:

**Buttons** — `BullButton`, `BullToolButton`, `BullTabMenuVerticalButton`
(`TabMenuVerticalButton`), `BullViewerActionButton` (`ViewerActionButton`).

**Inputs** — `BullCheckbox`, `BullFilterChip`, `BullInputText` (`BBInputText`),
`BullPasteInput` (`PasteInput`), `BullDropdown` (`BBDropdown`),
`BullSelectableList` + `BullSelectableListItem` (`SelectableList`),
`BullDialPad` (`DialPad`), `BullAmountInputFormatter` (`AmountInputFormatter`),
`BullLowerCaseTextFormatter` (`LowerCaseTextFormatter`).

**Controls** — `BullSegmented`, `BullSwipeAction`, `BullSwitch` (`BBSwitch`).

**Feedback** — `BullRefreshIndicator` (`BBRefreshIndicator`), `BullShimmerBox`/
`BullShimmerLine`, `BullSnackBar`, `BullCountdown` (`Countdown`),
`BullFadingLinearProgress` (`FadingLinearProgress`).

**Layout** — `BullScrollableColumn` (`ScrollableColumn`), `BullStackedPage`
(`StackedPage`), `BullPullableBody` (`BBPullableBody`).

**Data display** — `BullAddressText`, `BullBadge`, `BullInfoBar`,
`BullLabelChip`, `BullStatTile`, `BullText` (`BBText`), `BullOptionsTag`
(`OptionsTag`), `BullInfoCard` (`InfoCard`), `BullPriceCard` (`PriceCard`),
`BullBackupOptionCard` (`BackupOptionCard`), `BullBorderedTile`
(`BorderedTappableTile`), `BullTransactionDirectionBadge`
(`TransactionDirectionBadge`), `BullSettingsEntryItem` (`SettingsEntryItem`),
`BullDetailsTable` + `BullDetailsTableItem` (`DetailsTable` / `DetailsTableItem`).

**Overlays** — `BullBottomSheet`, `BullDialog`, `BullPickerSheet`
(`BBPickerSheet`), `BullInstructionsSheet` (`InstructionsBottomSheet`).

**Chrome** — `BullScaffold`, `BullTopBar`, `BullSelectionActionBar`.

## Not yet migrated (needs dep abstraction)

These `core/widgets` widgets still reach into `package:bb_mobile/*` (or otherwise
can't preserve their public API on top of the clean kit) and are **deferred**
until the dependency is abstracted out of the widget:

| Widget | Blocker |
| --- | --- |
| `MultiTapTrigger` | `package:bb_mobile/core/widgets/snackbar_utils.dart` — its public `tapsReachedMessageTextColor` / `tapsReachedMessageBackgroundColor` API can't be honoured by `BullSnackBar.show` (String-only), so migrating would lose API. |
| `CopyInput`, `BBKeyboardActions` | depend on `package:bb_mobile/*` localization / app utils. |
| `BBButton` (`buttons/button.dart`) | already superseded by `BullButton`; not a 1:1 copy. |
| Cards: `ActionCard`, `AutoswapWarningCard`, `BackupCard`, `BalanceCard`, `ProviderCart`, `WalletCard` | `package:bb_mobile/*` (router, l10n, feature models, `Assets`). |
| `BBKeyboardActions` | needs `package:keyboard_actions/keyboard_actions.dart`, not declared in `bull_ui/pubspec.yaml` (no new deps rule). |
| Bottom sheets: `AdvancedOptions`, `ComingSoon`, `Warning`, `NotLoggedIn`, `DeleteAccount*`, `Logout*`, `TranslationWarning` | `context.loc` localization and/or `package:bb_mobile/*` router & assets. |
| Cards: `AutoswapWarningCard` | `context.loc` (`autoswapWarningCard*` strings) + `context.font` from `package:bb_mobile/core/utils/build_context_x.dart`. |
| Loading: `ProgressScreen`, `StatusScreen` | `ProgressScreen` imports `package:bb_mobile/generated/flutter_gen/assets.gen.dart`; `StatusScreen` uses `context.loc` + `BBButton`. |
| Viewers: `AddressViewer`, `TransactionViewer`, `InvoiceViewer`, `LogViewer`, `QrScanner`, `NfcScanner`, `ShareLogs`, `Bip85Derivation`, `MnemonicWidget`, `CoinSelectionBottomSheet`, `AppLanguagePicker`, `BackupSuccessScreen`, `PriceInput`, `TransactionsByDayList`, `BalanceRow`, `RecoverbullVaultProviderSelector` | `package:bb_mobile/*` (entities, l10n, assets, feature blocs). |
16 changes: 16 additions & 0 deletions packages/bull_ui/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
include: package:lints/recommended.yaml

linter:
rules:
# A design-system package is the foundation other features build on; hold it
# to a stricter bar than the recommended set.
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- require_trailing_commas
- prefer_final_locals
- prefer_single_quotes
- sort_child_properties_last
- use_super_parameters
- unnecessary_parenthesis
138 changes: 138 additions & 0 deletions packages/bull_ui/lib/bull_ui.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/// The Bull Bitcoin design-system component library.
///
/// Import this single barrel; it re-exports a curated `show` list of Flutter
/// layout/foundation primitives plus every `Bull*` component and the theme.
/// Internals live under `lib/src/`.
library;

// Curated re-export of Flutter foundation/layout primitives (Decision D5).
// A blanket `export 'package:flutter/widgets.dart'` is deliberately avoided —
// the list grows on demand as components need more symbols.
export 'package:flutter/widgets.dart'
show
Align,
Alignment,
AnimatedContainer,
AnimatedPositioned,
AspectRatio,
Axis,
BorderRadius,
BoxConstraints,
BoxDecoration,
BuildContext,
Center,
Color,
Column,
ConstrainedBox,
CrossAxisAlignment,
EdgeInsets,
EdgeInsetsGeometry,
Expanded,
Flexible,
FractionallySizedBox,
GestureDetector,
HitTestBehavior,
IconData,
Key,
ListView,
MainAxisAlignment,
MainAxisSize,
Opacity,
Padding,
Positioned,
Radius,
Row,
SafeArea,
SingleChildScrollView,
SizedBox,
Spacer,
Stack,
State,
StatefulWidget,
StatelessWidget,
Text,
TextAlign,
TextBaseline,
TextDirection,
TextEditingController,
TextOverflow,
TextStyle,
ValueChanged,
VerticalDirection,
VoidCallback,
Widget,
Wrap;

// Material/services symbols required by component public APIs.
export 'package:flutter/material.dart'
show DropdownMenuItem, FormFieldValidator, RefreshCallback;
export 'package:flutter/services.dart' show TextInputFormatter;
export 'package:flutter/widgets.dart' show FocusNode;

export 'package:gap/gap.dart' show Gap;

// Theme.
export 'src/theme/bull_icon.dart';
export 'src/theme/bull_theme.dart';
export 'src/theme/bull_tokens.dart';

// Chrome.
export 'src/chrome/bull_scaffold.dart';
export 'src/chrome/bull_selection_action_bar.dart';
export 'src/chrome/bull_top_bar.dart';

// Buttons.
export 'src/buttons/bull_button.dart';
export 'src/buttons/bull_tab_menu_vertical_button.dart';
export 'src/buttons/bull_tool_button.dart';
export 'src/buttons/bull_viewer_action_button.dart';

// Inputs.
export 'src/inputs/bull_amount_input_formatter.dart';
export 'src/inputs/bull_checkbox.dart';
export 'src/inputs/bull_dial_pad.dart';
export 'src/inputs/bull_dropdown.dart';
export 'src/inputs/bull_filter_chip.dart';
export 'src/inputs/bull_input_text.dart';
export 'src/inputs/bull_lowercase_input_formatter.dart';
export 'src/inputs/bull_paste_input.dart';
export 'src/inputs/bull_selectable_list.dart';

// Controls.
export 'src/controls/bull_segmented.dart';
export 'src/controls/bull_swipe_action.dart';
export 'src/controls/bull_switch.dart';

// Feedback.
export 'src/feedback/bull_countdown.dart';
export 'src/feedback/bull_fading_linear_progress.dart';
export 'src/feedback/bull_refresh_indicator.dart';
export 'src/feedback/bull_shimmer.dart';
export 'src/feedback/bull_snack_bar.dart';

// Layout.
export 'src/layout/bull_pullable_body.dart';
export 'src/layout/bull_scrollable_column.dart';
export 'src/layout/bull_stacked_page.dart';

// Data display.
export 'src/data_display/bull_address_text.dart';
export 'src/data_display/bull_backup_option_card.dart';
export 'src/data_display/bull_badge.dart';
export 'src/data_display/bull_bordered_tile.dart';
export 'src/data_display/bull_details_table.dart';
export 'src/data_display/bull_info_bar.dart';
export 'src/data_display/bull_info_card.dart';
export 'src/data_display/bull_label_chip.dart';
export 'src/data_display/bull_options_tag.dart';
export 'src/data_display/bull_price_card.dart';
export 'src/data_display/bull_settings_entry_item.dart';
export 'src/data_display/bull_stat_tile.dart';
export 'src/data_display/bull_text.dart';
export 'src/data_display/bull_transaction_direction_badge.dart';

// Overlays.
export 'src/overlays/bull_bottom_sheet.dart';
export 'src/overlays/bull_dialog.dart';
export 'src/overlays/bull_instructions_sheet.dart';
export 'src/overlays/bull_picker_sheet.dart';
Loading