inou/app/lib/design/widgets/inou_footer.dart

292 lines
7.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:inou_app/design/inou_theme.dart';
import 'package:inou_app/design/inou_text.dart';
/// Footer link group
class FooterLinkGroup {
final String title;
final List<FooterLink> links;
const FooterLinkGroup({
required this.title,
required this.links,
});
}
/// Individual footer link
class FooterLink {
final String label;
final String route;
final bool isExternal;
const FooterLink({
required this.label,
required this.route,
this.isExternal = false,
});
}
/// inou Footer - responsive, matches web design
class InouFooter extends StatelessWidget {
final List<FooterLinkGroup> linkGroups;
final Function(FooterLink)? onLinkTap;
final String? copyrightText;
const InouFooter({
super.key,
this.linkGroups = const [],
this.onLinkTap,
this.copyrightText,
});
static final defaultLinkGroups = [
const FooterLinkGroup(
title: 'Product',
links: [
FooterLink(label: 'Features', route: '/features'),
FooterLink(label: 'Security', route: '/security'),
FooterLink(label: 'FAQ', route: '/faq'),
],
),
const FooterLinkGroup(
title: 'Legal',
links: [
FooterLink(label: 'Privacy', route: '/privacy'),
FooterLink(label: 'Terms', route: '/terms'),
FooterLink(label: 'DPA', route: '/dpa'),
],
),
const FooterLinkGroup(
title: 'Connect',
links: [
FooterLink(label: 'Contact', route: '/contact'),
FooterLink(label: 'Invite a friend', route: '/invite'),
],
),
];
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final isMobile = screenWidth < 768;
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: InouTheme.bgCard,
border: Border(
top: BorderSide(color: InouTheme.border, width: 1),
),
),
child: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: InouTheme.maxWidth),
padding: EdgeInsets.symmetric(
horizontal: isMobile ? 16 : 24,
vertical: isMobile ? 32 : 48,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
isMobile
? _buildMobileLinks(context)
: _buildDesktopLinks(context),
const SizedBox(height: 32),
_buildBottomBar(context, isMobile),
],
),
),
),
);
}
Widget _buildDesktopLinks(BuildContext context) {
final groups = linkGroups.isEmpty ? defaultLinkGroups : linkGroups;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Logo and tagline
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'inou',
style: InouText.h3.copyWith(
fontWeight: FontWeight.w700,
color: InouTheme.accent,
letterSpacing: -0.5,
),
),
const SizedBox(height: 12),
Text(
'Your health, understood.',
style: InouText.body.copyWith(
color: InouTheme.textMuted,
fontWeight: FontWeight.w300,
),
),
],
),
),
// Link groups
for (final group in groups) ...[
const SizedBox(width: 48),
Expanded(
child: _buildLinkGroup(context, group),
),
],
],
);
}
Widget _buildMobileLinks(BuildContext context) {
final groups = linkGroups.isEmpty ? defaultLinkGroups : linkGroups;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Logo
Text(
'inou',
style: InouText.h3.copyWith(
fontWeight: FontWeight.w700,
color: InouTheme.accent,
letterSpacing: -0.5,
),
),
const SizedBox(height: 8),
Text(
'Your health, understood.',
style: InouText.body.copyWith(
color: InouTheme.textMuted,
fontWeight: FontWeight.w300,
),
),
const SizedBox(height: 32),
// Links in 2-column grid
Wrap(
spacing: 32,
runSpacing: 24,
children: [
for (final group in groups)
SizedBox(
width: 140,
child: _buildLinkGroup(context, group),
),
],
),
],
);
}
Widget _buildLinkGroup(BuildContext context, FooterLinkGroup group) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
group.title.toUpperCase(),
style: InouText.labelCaps.copyWith(
color: InouTheme.textMuted,
letterSpacing: 1.2,
),
),
const SizedBox(height: 12),
for (final link in group.links)
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: InkWell(
onTap: () => _handleLinkTap(context, link),
borderRadius: BorderRadius.circular(4),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
link.label,
style: InouText.bodySmall.copyWith(
color: InouTheme.text,
),
),
if (link.isExternal) ...[
const SizedBox(width: 4),
Icon(
Icons.open_in_new,
size: 12,
color: InouTheme.textMuted,
),
],
],
),
),
),
),
],
);
}
Widget _buildBottomBar(BuildContext context, bool isMobile) {
final year = DateTime.now().year;
final copyright = copyrightText ?? '© $year inou health. All rights reserved.';
return Container(
padding: const EdgeInsets.only(top: 24),
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: InouTheme.border, width: 1),
),
),
child: isMobile
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
copyright,
style: InouText.bodySmall.copyWith(
color: InouTheme.textMuted,
),
),
const SizedBox(height: 12),
_buildSocialLinks(),
],
)
: Row(
children: [
Text(
copyright,
style: InouText.bodySmall.copyWith(
color: InouTheme.textMuted,
),
),
const Spacer(),
_buildSocialLinks(),
],
),
);
}
Widget _buildSocialLinks() {
// Placeholder for social links if needed
return const SizedBox.shrink();
}
void _handleLinkTap(BuildContext context, FooterLink link) {
if (onLinkTap != null) {
onLinkTap!(link);
return;
}
if (link.isExternal) {
// Handle external links (url_launcher)
return;
}
Navigator.pushNamed(context, link.route);
}
}