292 lines
7.7 KiB
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);
|
|
}
|
|
}
|