fix:靠原型不齐页面
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../features/assets/presentation/assets_page.dart';
|
||||
import '../../features/assets/presentation/holding_detail_page.dart';
|
||||
import '../../features/auth/presentation/delete_account_page.dart';
|
||||
import '../../features/auth/presentation/login_page.dart';
|
||||
import '../../features/debug/presentation/debug_info_page.dart';
|
||||
@@ -11,8 +12,11 @@ import '../../features/debug/presentation/debug_shorebird_page.dart';
|
||||
import '../../features/debug/presentation/fancy_dio_inspector_page.dart';
|
||||
import '../../features/debug/presentation/typography_test_page.dart';
|
||||
import '../../features/market/presentation/market_page.dart';
|
||||
import '../../features/market/presentation/market_kline_page.dart';
|
||||
import '../../features/news/presentation/news_page.dart';
|
||||
import '../../features/news/presentation/news_detail_page.dart';
|
||||
import '../../features/profile/presentation/profile_page.dart';
|
||||
import '../../features/profile/presentation/profile_action_pages.dart';
|
||||
import '../../features/settings/presentation/settings_page.dart';
|
||||
import '../../features/settings/presentation/privacy_consent_page.dart';
|
||||
import '../services/device_header_service.dart';
|
||||
@@ -21,8 +25,22 @@ import 'tab_shell.dart';
|
||||
abstract final class AppRoutes {
|
||||
static const assets = '/assets';
|
||||
static const market = '/market';
|
||||
static const marketKline = '/market/kline';
|
||||
static const news = '/news';
|
||||
static const newsDetail = '/news/detail';
|
||||
static const profile = '/profile';
|
||||
static const profileInvite = '/profile/invite';
|
||||
static const profileShare = '/profile/share';
|
||||
static const profileLocalBackup = '/profile/local-backup';
|
||||
static const profileCloudBackup = '/profile/cloud-backup';
|
||||
static const profileWidget = '/profile/widget';
|
||||
static const profilePurity = '/profile/purity';
|
||||
static const profileRecycle = '/profile/recycle';
|
||||
static const profilePriceColor = '/profile/price-color';
|
||||
static const profileCurrency = '/profile/currency';
|
||||
static const profileDisclaimer = '/profile/disclaimer';
|
||||
static const profileAbout = '/profile/about';
|
||||
static const assetDetail = '/assets/detail';
|
||||
static const login = '/login';
|
||||
static const settings = '/settings';
|
||||
static const deleteAccount = '/delete-account';
|
||||
@@ -73,6 +91,77 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.assetDetail,
|
||||
builder: (ctx, s) =>
|
||||
HoldingDetailPage(id: s.uri.queryParameters['id']),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.marketKline,
|
||||
builder: (ctx, s) => MarketKlinePage(
|
||||
metalId: s.uri.queryParameters['metal'],
|
||||
periodId: s.uri.queryParameters['period'],
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.newsDetail,
|
||||
builder: (ctx, s) => NewsDetailPage(id: s.uri.queryParameters['id']),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileInvite,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const InviteRewardPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileShare,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const ShareCardPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileLocalBackup,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const LocalBackupPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileCloudBackup,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const CloudBackupPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileWidget,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const WidgetSettingsPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profilePurity,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const PuritySettingsPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileRecycle,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const RecycleDiscountPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profilePriceColor,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const PriceColorPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileCurrency,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const CurrencyPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileDisclaimer,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const DisclaimerPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.profileAbout,
|
||||
pageBuilder: (ctx, s) =>
|
||||
_bottomUpPage(state: s, child: const AboutPage()),
|
||||
),
|
||||
GoRoute(path: AppRoutes.debug, builder: (_, s) => const DebugPage()),
|
||||
GoRoute(
|
||||
path: AppRoutes.login,
|
||||
@@ -113,3 +202,32 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
CustomTransitionPage<T> _bottomUpPage<T>({
|
||||
required GoRouterState state,
|
||||
required Widget child,
|
||||
}) {
|
||||
return CustomTransitionPage<T>(
|
||||
key: state.pageKey,
|
||||
transitionDuration: const Duration(milliseconds: 260),
|
||||
reverseTransitionDuration: const Duration(milliseconds: 220),
|
||||
child: child,
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, pageChild) {
|
||||
final curved = CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.easeOutCubic,
|
||||
reverseCurve: Curves.easeInCubic,
|
||||
);
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(0, 0.08),
|
||||
end: Offset.zero,
|
||||
).animate(curved),
|
||||
child: FadeTransition(
|
||||
opacity: curved,
|
||||
child: pageChild,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../theme/app_icons.dart';
|
||||
import '../widgets/adaptive.dart';
|
||||
import 'app_router.dart';
|
||||
|
||||
class TabShell extends StatefulWidget {
|
||||
const TabShell({super.key, required this.child, required this.location});
|
||||
@@ -44,129 +41,88 @@ class _TabShellState extends State<TabShell> {
|
||||
return Scaffold(
|
||||
backgroundColor: cs.background,
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: Stack(
|
||||
children: [
|
||||
Scaffold(
|
||||
backgroundColor: cs.background,
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(
|
||||
backgroundColor: cs.background,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
toolbarHeight: 32,
|
||||
leading: canPop
|
||||
? IconButton(
|
||||
onPressed: () => context.pop(),
|
||||
icon: const Icon(Icons.chevron_left, size: 20),
|
||||
)
|
||||
: null,
|
||||
title: AnimatedOpacity(
|
||||
opacity: _showCompactHeader ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 160),
|
||||
curve: Curves.easeOut,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
header.title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: cs.foreground,
|
||||
height: 1.1,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
),
|
||||
if (header.subtitle != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
header.subtitle!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: cs.mutedForeground,
|
||||
height: 1.1,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
appBar: AppBar(
|
||||
backgroundColor: cs.background,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
toolbarHeight: 32,
|
||||
leading: canPop
|
||||
? IconButton(
|
||||
onPressed: () => context.pop(),
|
||||
icon: const Icon(Icons.chevron_left, size: 20),
|
||||
)
|
||||
: null,
|
||||
title: AnimatedOpacity(
|
||||
opacity: _showCompactHeader ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 160),
|
||||
curve: Curves.easeOut,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
header.title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: cs.foreground,
|
||||
height: 1.1,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
body: ColoredBox(
|
||||
color: cs.background,
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: _handleScrollNotification,
|
||||
child: Stack(children: [Positioned.fill(child: widget.child)]),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: cs.background,
|
||||
border: Border(
|
||||
top: BorderSide(color: cs.border, width: 1),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 62,
|
||||
// 平板:tab 行限宽 600 居中(demo .nav .tabs)
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: Adaptive.tabsMaxWidth,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
for (var i = 0; i < _tabs.length; i++)
|
||||
Expanded(
|
||||
child: _TabButton(
|
||||
icon: i == _index
|
||||
? _filledIcons[i]
|
||||
: _outlineIcons[i],
|
||||
label: _labels[i],
|
||||
selected: i == _index,
|
||||
onTap: () => context.go(_tabs[i]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (header.subtitle != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
header.subtitle!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: cs.mutedForeground,
|
||||
height: 1.1,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: ColoredBox(
|
||||
color: cs.background,
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: _handleScrollNotification,
|
||||
child: Stack(children: [Positioned.fill(child: widget.child)]),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: cs.background,
|
||||
border: Border(top: BorderSide(color: cs.border, width: 1)),
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: SizedBox(
|
||||
height: 62,
|
||||
child: Row(
|
||||
children: [
|
||||
for (var i = 0; i < _tabs.length; i++)
|
||||
Expanded(
|
||||
child: _TabButton(
|
||||
icon: i == _index ? _filledIcons[i] : _outlineIcons[i],
|
||||
label: _labels[i],
|
||||
selected: i == _index,
|
||||
onTap: () => context.go(_tabs[i]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!kReleaseMode)
|
||||
Positioned(
|
||||
right: 16,
|
||||
bottom: 88,
|
||||
child: FilledButton(
|
||||
onPressed: () => context.push(AppRoutes.fancyDioInspector),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: cs.primary,
|
||||
foregroundColor: cs.primaryForeground,
|
||||
minimumSize: const Size(60, 60),
|
||||
shape: const CircleBorder(),
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
child: const Text('请求'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user