Files
meshcore-open/lib/widgets/empty_state.dart
T
zjs81 51d6210920 Add shared UI components for mesh application
- Introduced `mesh_ui.dart` with reusable widgets including SectionHeader, MeshCard, StatusChip, StatTile, AvatarCircle, SignalBars, RouteChip, PulseDot, BottomSheetHeader, ErrorRetryCard, and ListEntrance.
- Implemented `path_map_ui.dart` for path map screens, featuring path distance calculations, playback controls, and a summary list of observed paths.
- Created `themed_map_tile_layer.dart` for shared cached map tiles with automatic dark-mode treatment.
2026-06-12 21:04:02 -07:00

115 lines
3.3 KiB
Dart

import 'package:flutter/material.dart';
/// A centered empty state display with icon, title, and optional subtitle/action.
/// Features a tinted icon circle, fade+slide entrance animation, and clear
/// typography hierarchy using the MeshCore design system.
class EmptyState extends StatefulWidget {
final IconData icon;
final String title;
final String? subtitle;
final Widget? action;
const EmptyState({
super.key,
required this.icon,
required this.title,
this.subtitle,
this.action,
});
@override
State<EmptyState> createState() => _EmptyStateState();
}
class _EmptyStateState extends State<EmptyState>
with SingleTickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 420),
);
late final CurvedAnimation _curve = CurvedAnimation(
parent: _controller,
curve: Curves.easeOutCubic,
);
@override
void initState() {
super.initState();
_controller.forward();
}
@override
void dispose() {
_curve.dispose();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return FadeTransition(
opacity: _curve,
child: SlideTransition(
position: Tween(
begin: const Offset(0, 0.06),
end: Offset.zero,
).animate(_curve),
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: scheme.primary.withValues(alpha: 0.08),
border: Border.all(
color: scheme.primary.withValues(alpha: 0.18),
width: 1.5,
),
),
child: Icon(
widget.icon,
size: 36,
color: scheme.onSurfaceVariant,
),
),
const SizedBox(height: 20),
Text(
widget.title,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
color: scheme.onSurface,
letterSpacing: -0.1,
),
textAlign: TextAlign.center,
),
if (widget.subtitle != null) ...[
const SizedBox(height: 8),
Text(
widget.subtitle!,
style: TextStyle(
fontSize: 13.5,
color: scheme.onSurfaceVariant,
height: 1.45,
),
textAlign: TextAlign.center,
),
],
if (widget.action != null) ...[
const SizedBox(height: 28),
widget.action!,
],
],
),
),
),
),
);
}
}