💄 Better bottom nav, snowing animation and notification tile

💫 Animated snowing animation
This commit is contained in:
2025-12-22 23:28:38 +08:00
parent b0b227f36b
commit 09abe79f6a
3 changed files with 99 additions and 69 deletions

View File

@@ -212,6 +212,17 @@ class TabsScreen extends HookConsumerWidget {
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: BottomAppBar(
@@ -227,9 +238,8 @@ class TabsScreen extends HookConsumerWidget {
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: () {
final navItems = destinations.asMap().entries.map<Widget>((
entry,
) {
final navItems = destinations.asMap().entries.map<Widget>(
(entry) {
int index = entry.key;
NavigationDestination dest = entry.value;
return IconButton(
@@ -239,7 +249,8 @@ class TabsScreen extends HookConsumerWidget {
? Theme.of(context).colorScheme.primary
: null,
);
}).toList();
},
).toList();
// Add mock item to leave space for FAB based on position
final gapIndex = switch (settings.fabPosition) {
'left' => 0,
@@ -260,6 +271,7 @@ class TabsScreen extends HookConsumerWidget {
),
),
),
),
);
}
}

View File

@@ -35,7 +35,8 @@ class AppWrapper extends HookConsumerWidget {
final networkStateShowing = useState(false);
final wsNotifier = ref.watch(websocketStateProvider.notifier);
final websocketState = ref.watch(websocketStateProvider);
final showSnow = useState(false);
final isShowSnow = useState(false);
final isSnowGone = useState(false);
// Handle network status modal
if (websocketState == WebSocketState.duplicateDevice() &&
@@ -131,10 +132,18 @@ class AppWrapper extends HookConsumerWidget {
settings.festivalFeatures &&
now.month == 12 &&
(now.day >= 22 && now.day <= 28);
useEffect(() {
final now = DateTime.now();
if (doesShowSnow) {
showSnow.value = true;
Future.delayed(const Duration(seconds: 10), () {
showSnow.value = false;
isShowSnow.value = true;
Future.delayed(const Duration(seconds: 60), () {
if (!context.mounted) return;
isShowSnow.value = false;
Future.delayed(const Duration(seconds: 3), () {
if (!context.mounted) return;
isSnowGone.value = true;
});
});
}
@@ -156,18 +165,25 @@ class AppWrapper extends HookConsumerWidget {
}
}
return null;
}, []);
return TourTriggerWidget(
key: const Key("app_tour_trigger"),
child: Stack(
children: [
child,
if (showSnow.value)
if (doesShowSnow && !isSnowGone.value)
IgnorePointer(
child: AnimatedOpacity(
opacity: isShowSnow.value ? 1 : 00,
duration: const Duration(seconds: 3),
child: SnowFallAnimation(
key: const Key("app_snow_animation"),
config: SnowfallConfig(numberOfSnowflakes: 50, speed: 1.0),
),
),
),
],
),
);

View File

@@ -81,14 +81,14 @@ class NotificationTile extends StatelessWidget {
style: compact
? Theme.of(context).textTheme.bodySmall
: Theme.of(context).textTheme.titleMedium,
maxLines: compact ? 2 : null,
maxLines: compact ? 1 : null,
overflow: compact ? TextOverflow.ellipsis : null,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (notification.subtitle.isNotEmpty && !compact)
Text(notification.subtitle).bold(),
Text(notification.subtitle, maxLines: compact ? 3 : null).bold(),
Row(
spacing: 6,
children: [
@@ -114,7 +114,9 @@ class NotificationTile extends StatelessWidget {
],
).opacity(0.75).padding(bottom: compact ? 2 : 4),
MarkdownTextContent(
content: notification.content,
content: (compact && notification.content.length > 60)
? '${notification.content.substring(0, 60).replaceAll('\n', ' ')}...'
: notification.content,
textStyle:
(compact
? Theme.of(context).textTheme.bodySmall