hsds_mobile 0.1.0
hsds_mobile: ^0.1.0 copied to clipboard
HeadSpace Design System — mobile component library (Flutter / Dart)
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:hsds_mobile/hsds_mobile.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HSDS Mobile — Components',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF3D3A5C)),
useMaterial3: true,
),
home: const ComponentGallery(),
);
}
}
// ---------------------------------------------------------------------------
// Gallery page — shows every HsdsBottomNav variant stacked vertically
// ---------------------------------------------------------------------------
class ComponentGallery extends StatefulWidget {
const ComponentGallery({super.key});
@override
State<ComponentGallery> createState() => _ComponentGalleryState();
}
class _ComponentGalleryState extends State<ComponentGallery> {
int _sel3 = 0;
int _sel5 = 0;
int _selNoIndicator = 0;
// 3-tab config using real Headspace icons
static final _items3 = [
HsdsBottomNavItem(
label: 'Today',
icon: HsdsBottomNavIcons.today.inactive,
activeIcon: HsdsBottomNavIcons.today.active,
),
HsdsBottomNavItem(
label: 'Explore',
icon: HsdsBottomNavIcons.explore.inactive,
activeIcon: HsdsBottomNavIcons.explore.active,
),
HsdsBottomNavItem(
label: 'Profile',
icon: HsdsBottomNavIcons.profile.inactive,
activeIcon: HsdsBottomNavIcons.profile.active,
),
];
// 5-tab config — the real default Headspace nav
static final _items5 = [
HsdsBottomNavItem(
label: 'Today',
icon: HsdsBottomNavIcons.today.inactive,
activeIcon: HsdsBottomNavIcons.today.active,
),
HsdsBottomNavItem(
label: 'Explore',
icon: HsdsBottomNavIcons.explore.inactive,
activeIcon: HsdsBottomNavIcons.explore.active,
),
HsdsBottomNavItem(
label: 'Ebb',
icon: HsdsBottomNavIcons.ebb.inactive,
activeIcon: HsdsBottomNavIcons.ebb.active,
),
HsdsBottomNavItem(
label: 'Care',
icon: HsdsBottomNavIcons.care.inactive,
activeIcon: HsdsBottomNavIcons.care.active,
),
HsdsBottomNavItem(
label: 'Profile',
icon: HsdsBottomNavIcons.profile.inactive,
activeIcon: HsdsBottomNavIcons.profile.active,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
appBar: AppBar(
title: const Text('HsdsBottomNav'),
backgroundColor: Colors.white,
surfaceTintColor: Colors.transparent,
elevation: 0,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(height: 1, color: const Color(0xFFE2DED9)),
),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 3 tabs (default)
_VariantCard(
label: '3 tabs — with home indicator',
child: HsdsBottomNav(
items: _items3,
selectedIndex: _sel3,
onTabSelected: (i) => setState(() => _sel3 = i),
),
),
// 5 tabs
_VariantCard(
label: '5 tabs — with home indicator',
child: HsdsBottomNav(
items: _items5,
selectedIndex: _sel5,
onTabSelected: (i) => setState(() => _sel5 = i),
),
),
// No home indicator
_VariantCard(
label: '3 tabs — no home indicator',
child: HsdsBottomNav(
items: _items3,
selectedIndex: _selNoIndicator,
onTabSelected: (i) => setState(() => _selNoIndicator = i),
showHomeIndicator: false,
),
),
],
),
),
);
}
}
// ---------------------------------------------------------------------------
// Helper widget — section label + full-width component (no card frame)
// Renders the nav edge-to-edge on a gray background, matching how it sits
// at the bottom of a real device screen.
// ---------------------------------------------------------------------------
class _VariantCard extends StatelessWidget {
const _VariantCard({required this.label, required this.child});
final String label;
final Widget child;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 32, 16, 8),
child: Text(
label,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Color(0xFF63605D),
letterSpacing: 0.5,
),
),
),
// Component renders full-width — no border, no radius, no clipping.
// Its own top border (0.5px) is the only visible edge, matching spec.
child,
],
);
}
}