mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 11:33:44 +08:00
feat: 底部TabBar优化
This commit is contained in:
+1251
-1620
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@@ -38,7 +40,9 @@ class _MainShellPageState extends ConsumerState<MainShellPage> {
|
||||
final l = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF7EFE0),
|
||||
body: SafeArea(child: IndexedStack(index: _index, children: _tabs)),
|
||||
body: SafeArea(
|
||||
child: IndexedStack(index: _index, children: _tabs),
|
||||
),
|
||||
bottomNavigationBar: _WarmBottomBar(
|
||||
selectedIndex: _index,
|
||||
onSelected: (i) => setState(() => _index = i),
|
||||
@@ -83,30 +87,91 @@ class _WarmBottomBar extends StatelessWidget {
|
||||
child: Container(
|
||||
color: const Color(0xFFF7EFE0),
|
||||
padding: const EdgeInsets.fromLTRB(21, 12, 21, 21),
|
||||
child: Container(
|
||||
height: 62,
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFBF5E6),
|
||||
borderRadius: BorderRadius.circular(36),
|
||||
border: Border.all(color: const Color(0xFFE2D5B3)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
for (var i = 0; i < items.length; i++)
|
||||
Expanded(
|
||||
child: _WarmBottomBarButton(
|
||||
item: items[i],
|
||||
selected: i == selectedIndex,
|
||||
onTap: () => onSelected(i),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(36),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18),
|
||||
child: Container(
|
||||
height: 62,
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFBF5E6).withValues(alpha: 0.68),
|
||||
borderRadius: BorderRadius.circular(36),
|
||||
border: Border.all(color: Colors.white.withValues(alpha: 0.52)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF5C4520).withValues(alpha: 0.08),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
AnimatedAlign(
|
||||
duration: const Duration(milliseconds: 260),
|
||||
curve: Curves.easeOutCubic,
|
||||
alignment: _indicatorAlignment,
|
||||
child: FractionallySizedBox(
|
||||
widthFactor: 1 / items.length,
|
||||
heightFactor: 1,
|
||||
child: const _WarmBottomBarIndicator(),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
for (var i = 0; i < items.length; i++)
|
||||
Expanded(
|
||||
child: _WarmBottomBarButton(
|
||||
item: items[i],
|
||||
selected: i == selectedIndex,
|
||||
onTap: () => onSelected(i),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Alignment get _indicatorAlignment {
|
||||
if (items.length <= 1) return Alignment.center;
|
||||
final step = 2 / (items.length - 1);
|
||||
return Alignment(-1 + (step * selectedIndex), 0);
|
||||
}
|
||||
}
|
||||
|
||||
class _WarmBottomBarIndicator extends StatelessWidget {
|
||||
const _WarmBottomBarIndicator();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFE5B33A).withValues(alpha: 0.74),
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
border: Border.all(color: Colors.white.withValues(alpha: 0.38)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFFE5B33A).withValues(alpha: 0.24),
|
||||
blurRadius: 16,
|
||||
offset: const Offset(0, 6),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.white.withValues(alpha: 0.18),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WarmBottomBarButton extends StatelessWidget {
|
||||
@@ -126,28 +191,35 @@ class _WarmBottomBarButton extends StatelessWidget {
|
||||
return Tooltip(
|
||||
message: item.label,
|
||||
child: Material(
|
||||
color: selected ? const Color(0xFFE5B33A) : Colors.transparent,
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(26),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(26),
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(item.icon, size: 20, color: color),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
child: AnimatedDefaultTextStyle(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
curve: Curves.easeOut,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
child: IconTheme(
|
||||
data: IconThemeData(size: 20, color: color),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(item.icon),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -156,10 +228,7 @@ class _WarmBottomBarButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _WarmBottomBarItem {
|
||||
const _WarmBottomBarItem({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
});
|
||||
const _WarmBottomBarItem({required this.icon, required this.label});
|
||||
|
||||
final IconData icon;
|
||||
final String label;
|
||||
|
||||
Reference in New Issue
Block a user