Tests / test (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
153 lines
4.5 KiB
Dart
153 lines
4.5 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'sliding_pill_nav.dart';
|
|
|
|
class CompactBottomNavigationBar extends StatelessWidget {
|
|
const CompactBottomNavigationBar({
|
|
super.key,
|
|
required this.items,
|
|
required this.currentIndex,
|
|
required this.onTap,
|
|
this.onItemLongPress,
|
|
});
|
|
|
|
final List<PillNavItem> items;
|
|
final int currentIndex;
|
|
final ValueChanged<int> onTap;
|
|
final void Function(int index, Offset position)? onItemLongPress;
|
|
|
|
static double contentHeight(BuildContext context) =>
|
|
math.max(50, 34 + MediaQuery.textScalerOf(context).scale(10) * 1.2);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (MediaQuery.viewInsetsOf(context).bottom > 0) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
final cs = Theme.of(context).colorScheme;
|
|
return DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
color: cs.surface,
|
|
border: Border(top: BorderSide(color: cs.outlineVariant, width: 0.5)),
|
|
),
|
|
child: SafeArea(
|
|
top: false,
|
|
child: SizedBox(
|
|
height: contentHeight(context),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
for (var index = 0; index < items.length; index++)
|
|
Expanded(
|
|
child: _CompactTab(
|
|
item: items[index],
|
|
selected: index == currentIndex,
|
|
onTap: () => onTap(index),
|
|
onLongPress:
|
|
items[index].longPressable && onItemLongPress != null
|
|
? (position) => onItemLongPress!(index, position)
|
|
: null,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _CompactTab extends StatefulWidget {
|
|
const _CompactTab({
|
|
required this.item,
|
|
required this.selected,
|
|
required this.onTap,
|
|
this.onLongPress,
|
|
});
|
|
|
|
final PillNavItem item;
|
|
final bool selected;
|
|
final VoidCallback onTap;
|
|
final ValueChanged<Offset>? onLongPress;
|
|
|
|
@override
|
|
State<_CompactTab> createState() => _CompactTabState();
|
|
}
|
|
|
|
class _CompactTabState extends State<_CompactTab> {
|
|
bool _pressed = false;
|
|
|
|
void _setPressed(bool value) {
|
|
if (_pressed != value) setState(() => _pressed = value);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
final color = widget.selected ? cs.primary : cs.onSurfaceVariant;
|
|
return Semantics(
|
|
button: true,
|
|
selected: widget.selected,
|
|
label: widget.item.label,
|
|
onTap: widget.onTap,
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
excludeFromSemantics: true,
|
|
onTapDown: (_) => _setPressed(true),
|
|
onTapUp: (_) => _setPressed(false),
|
|
onTapCancel: () => _setPressed(false),
|
|
onTap: widget.onTap,
|
|
onLongPressStart: widget.onLongPress == null
|
|
? null
|
|
: (details) {
|
|
_setPressed(false);
|
|
widget.onLongPress!(details.globalPosition);
|
|
},
|
|
child: ExcludeSemantics(
|
|
child: AnimatedOpacity(
|
|
opacity: _pressed ? 0.55 : 1,
|
|
duration: const Duration(milliseconds: 100),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Badge(
|
|
isLabelVisible: widget.item.unreadCount > 0,
|
|
label: Text(
|
|
widget.item.unreadCount > 99
|
|
? '99+'
|
|
: '${widget.item.unreadCount}',
|
|
),
|
|
child: Icon(
|
|
widget.item.icon,
|
|
color: color,
|
|
size: 24,
|
|
fill: widget.selected ? 1 : 0,
|
|
weight: widget.selected ? 500 : 400,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
widget.item.label,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(
|
|
color: color,
|
|
fontSize: 10,
|
|
height: 1.2,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|