From 005086d45f8489fcbc73c8d020bd0e7a867b7cef Mon Sep 17 00:00:00 2001 From: Sipho Nkebe Date: Wed, 10 Jun 2026 22:11:30 +0200 Subject: [PATCH 01/10] feat(gallery): scaffold apps/gallery (flutterbits component target) --- apps/gallery/analysis_options.yaml | 6 +++++ apps/gallery/lib/main.dart | 35 ++++++++++++++++++++++++++++++ apps/gallery/pubspec.yaml | 30 +++++++++++++++++++++++++ pubspec.yaml | 1 + 4 files changed, 72 insertions(+) create mode 100644 apps/gallery/analysis_options.yaml create mode 100644 apps/gallery/lib/main.dart create mode 100644 apps/gallery/pubspec.yaml diff --git a/apps/gallery/analysis_options.yaml b/apps/gallery/analysis_options.yaml new file mode 100644 index 0000000..1cec66e --- /dev/null +++ b/apps/gallery/analysis_options.yaml @@ -0,0 +1,6 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + language: + strict-casts: true + strict-raw-types: true diff --git a/apps/gallery/lib/main.dart b/apps/gallery/lib/main.dart new file mode 100644 index 0000000..7bc4519 --- /dev/null +++ b/apps/gallery/lib/main.dart @@ -0,0 +1,35 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutterwindcss/flutterwindcss.dart'; + +void main() => runApp(const GalleryApp()); + +/// TEMPORARY bootstrap root for the gallery. The structure plan REPLACES this +/// raw `WidgetsApp` with the flutterbits `Layout` (the gallery's intended root, +/// which it then demos). Do not entrench this — it is throwaway dev scaffolding. +class GalleryApp extends StatelessWidget { + const GalleryApp({super.key}); + + @override + Widget build(BuildContext context) { + return FwTheme( + tokens: FwTokens.light, + child: WidgetsApp( + title: 'flutterbits gallery', + color: const Color(0xFF2563EB), + debugShowCheckedModeBanner: false, + pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) { + return PageRouteBuilder( + settings: settings, + pageBuilder: (context, _, _) => builder(context), + ); + }, + home: Builder( + builder: (context) => ColoredBox( + color: context.fw.colors.background, + child: const Center(child: Text('flutterbits gallery')), + ), + ), + ), + ); + } +} diff --git a/apps/gallery/pubspec.yaml b/apps/gallery/pubspec.yaml new file mode 100644 index 0000000..4aed856 --- /dev/null +++ b/apps/gallery/pubspec.yaml @@ -0,0 +1,30 @@ +name: flutterbits_gallery +description: >- + flutterbits COMPONENT gallery — a Material-free Flutter app showcasing the + copy-paste components, and the golden-test + compile target for the registry. +publish_to: none +version: 1.0.0+1 + +# Joins the repo's pub workspace (root pubspec.yaml `workspace:`). +resolution: workspace + +environment: + # Match the toolchain floor (AGENTS.md §2): Flutter 3.29 / Dart 3.7. + sdk: '>=3.7.0 <4.0.0' + flutter: '>=3.29.0' + +dependencies: + flutter: + sdk: flutter + # The styling engine every component styles through. + flutterwindcss: + path: ../../packages/flutterwindcss + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: + # Material-free: components run on the pure path (WidgetsApp + FwTheme). + uses-material-design: false diff --git a/pubspec.yaml b/pubspec.yaml index 8b8fcab..85cd912 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,3 +10,4 @@ workspace: - packages/flutterwindcss - packages/flutterwindcss/example - apps/example + - apps/gallery From c394d81325e189b220d7391c9b75436821facda2 Mon Sep 17 00:00:00 2001 From: Sipho Nkebe Date: Wed, 10 Jun 2026 22:23:12 +0200 Subject: [PATCH 02/10] feat(button): skeleton + variant/size enums + semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds ButtonVariant and ButtonSize enums, the minimal Button StatefulWidget skeleton (Semantics wrapper, no styling yet), and the first behavior test asserting label rendering and button/enabled semantics flags. Adapts plan's test to Flutter 3.41.9 API (flagsCollection on SemanticsData, Tristate from dart:ui — hasFlag was deprecated after v3.32.0). Co-Authored-By: Claude Sonnet 4.6 --- apps/gallery/lib/components/ui/button.dart | 54 +++++++++++++++++++++ apps/gallery/lib/main.dart | 9 ++-- apps/gallery/test/button_behavior_test.dart | 26 ++++++++++ 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 apps/gallery/lib/components/ui/button.dart create mode 100644 apps/gallery/test/button_behavior_test.dart diff --git a/apps/gallery/lib/components/ui/button.dart b/apps/gallery/lib/components/ui/button.dart new file mode 100644 index 0000000..3bc96f8 --- /dev/null +++ b/apps/gallery/lib/components/ui/button.dart @@ -0,0 +1,54 @@ +import 'package:flutter/widgets.dart'; + +/// shadcn's button variants. `primary` is shadcn's `default` (`default` is a +/// Dart reserved word, so it cannot be an enum constant). +enum ButtonVariant { primary, secondary, destructive, outline, ghost, link } + +/// shadcn's button sizes. `md` is shadcn's `default` size (same reserved-word +/// reason as above). +enum ButtonSize { sm, md, lg, icon } + +/// A Material-free, themeable button — shadcn parity. Copy-paste source you own. +/// +/// Sources its own interaction states (hover/focus/pressed/disabled) and styles a +/// single box through `.tw` using semantic tokens only (AGENTS.md §3.1/§6). +class Button extends StatefulWidget { + const Button({ + super.key, + required this.child, + this.onPressed, + this.variant = ButtonVariant.primary, + this.size = ButtonSize.md, + this.semanticLabel, + }); + + /// The button's content (a `Text`, an icon widget, or a row of both). + final Widget child; + + /// Tapped/activated callback. `null` disables the button. + final VoidCallback? onPressed; + + final ButtonVariant variant; + final ButtonSize size; + + /// Optional accessibility label (defaults to the child's own semantics). + final String? semanticLabel; + + /// Whether the button is interactive. + bool get enabled => onPressed != null; + + @override + State