Drawer navigation

This commit is contained in:
LittleSheep 2024-07-12 11:39:44 +08:00
parent a6d8e2e311
commit 3b1b6ec8d6
10 changed files with 93 additions and 58 deletions

View File

@ -19,6 +19,7 @@ import 'package:solian/providers/content/realm.dart';
import 'package:solian/providers/friend.dart'; import 'package:solian/providers/friend.dart';
import 'package:solian/providers/account_status.dart'; import 'package:solian/providers/account_status.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/shells/root_shell.dart';
import 'package:solian/shells/system_shell.dart'; import 'package:solian/shells/system_shell.dart';
import 'package:solian/theme.dart'; import 'package:solian/theme.dart';
import 'package:solian/translations.dart'; import 'package:solian/translations.dart';
@ -87,9 +88,11 @@ class SolianApp extends StatelessWidget {
onInit: () => _initializeProviders(context), onInit: () => _initializeProviders(context),
builder: (context, child) { builder: (context, child) {
return SystemShell( return SystemShell(
child: RootShell(
child: ScaffoldMessenger( child: ScaffoldMessenger(
child: child ?? const SizedBox(), child: child ?? const SizedBox(),
), ),
),
); );
}, },
); );

View File

@ -20,27 +20,18 @@ import 'package:solian/screens/realms/realm_view.dart';
import 'package:solian/screens/feed.dart'; import 'package:solian/screens/feed.dart';
import 'package:solian/screens/posts/post_editor.dart'; import 'package:solian/screens/posts/post_editor.dart';
import 'package:solian/shells/basic_shell.dart'; import 'package:solian/shells/basic_shell.dart';
import 'package:solian/shells/root_shell.dart';
import 'package:solian/shells/title_shell.dart'; import 'package:solian/shells/title_shell.dart';
import 'package:solian/theme.dart'; import 'package:solian/theme.dart';
import 'package:solian/widgets/sidebar/empty_placeholder.dart'; import 'package:solian/widgets/sidebar/empty_placeholder.dart';
abstract class AppRouter { abstract class AppRouter {
static GoRouter instance = GoRouter( static GoRouter instance = GoRouter(
routes: [
ShellRoute(
builder: (context, state, child) => RootShell(
state: state,
child: child,
),
routes: [ routes: [
_feedRoute, _feedRoute,
_chatRoute, _chatRoute,
_realmRoute, _realmRoute,
_accountRoute, _accountRoute,
], ],
),
],
); );
static final ShellRoute _feedRoute = ShellRoute( static final ShellRoute _feedRoute = ShellRoute(
@ -232,7 +223,7 @@ abstract class AppRouter {
name: 'account', name: 'account',
builder: (context, state) => SolianTheme.isExtraLargeScreen(context) builder: (context, state) => SolianTheme.isExtraLargeScreen(context)
? const EmptyPagePlaceholder() ? const EmptyPagePlaceholder()
: const AccountScreen(), : TitleShell(state: state, child: const AccountScreen()),
), ),
GoRoute( GoRoute(
path: '/account/friend', path: '/account/friend',

View File

@ -87,7 +87,7 @@ class _AccountScreenState extends State<AccountScreen> {
return ListView( return ListView(
children: [ children: [
const AccountHeading().paddingOnly(bottom: 8, top: 16), const AccountHeading().paddingOnly(bottom: 8, top: 8),
...(actionItems.map( ...(actionItems.map(
(x) => ListTile( (x) => ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 34), contentPadding: const EdgeInsets.symmetric(horizontal: 34),

View File

@ -12,6 +12,7 @@ import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/channel/channel_list.dart'; import 'package:solian/widgets/channel/channel_list.dart';
import 'package:solian/widgets/chat/call/chat_call_indicator.dart'; import 'package:solian/widgets/chat/call/chat_call_indicator.dart';
import 'package:solian/widgets/current_state_action.dart'; import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/drawer_button.dart' as drawer;
class ChatScreen extends StatefulWidget { class ChatScreen extends StatefulWidget {
const ChatScreen({super.key}); const ChatScreen({super.key});
@ -94,7 +95,7 @@ class _ChatScreenState extends State<ChatScreen> {
title: AppBarTitle('chat'.tr), title: AppBarTitle('chat'.tr),
centerTitle: false, centerTitle: false,
floating: true, floating: true,
titleSpacing: SolianTheme.titleSpacing(context), leading: const drawer.DrawerButton(),
toolbarHeight: SolianTheme.toolbarHeight(context), toolbarHeight: SolianTheme.toolbarHeight(context),
actions: [ actions: [
const BackgroundStateWidget(), const BackgroundStateWidget(),
@ -191,7 +192,8 @@ class _ChatScreenState extends State<ChatScreen> {
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: () => getChannels(), onRefresh: () => getChannels(),
child: ChannelListWidget( child: ChannelListWidget(
channels: _channels.where((x) => x.type == 1).toList(), channels:
_channels.where((x) => x.type == 1).toList(),
selfId: _accountId ?? 0, selfId: _accountId ?? 0,
noCategory: true, noCategory: true,
), ),

View File

@ -11,6 +11,7 @@ import 'package:solian/theme.dart';
import 'package:solian/widgets/app_bar_title.dart'; import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/current_state_action.dart'; import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/feed/feed_list.dart'; import 'package:solian/widgets/feed/feed_list.dart';
import 'package:solian/widgets/drawer_button.dart' as drawer;
class FeedScreen extends StatefulWidget { class FeedScreen extends StatefulWidget {
const FeedScreen({super.key}); const FeedScreen({super.key});
@ -62,8 +63,8 @@ class _FeedScreenState extends State<FeedScreen> {
title: AppBarTitle('feed'.tr), title: AppBarTitle('feed'.tr),
centerTitle: false, centerTitle: false,
floating: true, floating: true,
titleSpacing: SolianTheme.titleSpacing(context),
toolbarHeight: SolianTheme.toolbarHeight(context), toolbarHeight: SolianTheme.toolbarHeight(context),
leading: const drawer.DrawerButton(),
actions: [ actions: [
const BackgroundStateWidget(), const BackgroundStateWidget(),
const NotificationButton(), const NotificationButton(),

View File

@ -11,6 +11,7 @@ import 'package:solian/theme.dart';
import 'package:solian/widgets/account/signin_required_overlay.dart'; import 'package:solian/widgets/account/signin_required_overlay.dart';
import 'package:solian/widgets/app_bar_title.dart'; import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/current_state_action.dart'; import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/drawer_button.dart' as drawer;
class RealmListScreen extends StatefulWidget { class RealmListScreen extends StatefulWidget {
const RealmListScreen({super.key}); const RealmListScreen({super.key});
@ -79,7 +80,7 @@ class _RealmListScreenState extends State<RealmListScreen> {
title: AppBarTitle('realm'.tr), title: AppBarTitle('realm'.tr),
centerTitle: false, centerTitle: false,
floating: true, floating: true,
titleSpacing: SolianTheme.titleSpacing(context), leading: const drawer.DrawerButton(),
toolbarHeight: SolianTheme.toolbarHeight(context), toolbarHeight: SolianTheme.toolbarHeight(context),
actions: [ actions: [
const BackgroundStateWidget(), const BackgroundStateWidget(),

View File

@ -1,36 +1,33 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:go_router/go_router.dart';
import 'package:solian/router.dart';
import 'package:solian/theme.dart'; import 'package:solian/theme.dart';
import 'package:solian/widgets/navigation/app_navigation.dart'; import 'package:solian/widgets/navigation/app_navigation_drawer.dart';
import 'package:solian/widgets/navigation/app_navigation_bottom_bar.dart';
import 'package:solian/widgets/navigation/app_navigation_rail.dart'; import 'package:solian/widgets/navigation/app_navigation_rail.dart';
final GlobalKey<ScaffoldState> rootScaffoldKey = GlobalKey<ScaffoldState>();
class RootShell extends StatelessWidget { class RootShell extends StatelessWidget {
final bool showSidebar; final bool showSidebar;
final bool showNavigation; final bool showNavigation;
final bool? showBottomNavigation; final bool? showBottomNavigation;
final GoRouterState state;
final Widget child; final Widget child;
const RootShell({ const RootShell({
super.key, super.key,
required this.state,
required this.child, required this.child,
this.showSidebar = true, this.showSidebar = true,
this.showNavigation = true, this.showNavigation = true,
this.showBottomNavigation, this.showBottomNavigation,
}); });
void closeDrawer() {
rootScaffoldKey.currentState!.closeDrawer();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final routeName = AppRouter
.instance.routerDelegate.currentConfiguration.lastOrNull?.route.name;
final showBottom = showBottomNavigation ??
AppNavigation.destinationPages.contains(routeName);
return Scaffold( return Scaffold(
key: rootScaffoldKey,
drawer: const AppNavigationDrawer(),
body: SolianTheme.isLargeScreen(context) body: SolianTheme.isLargeScreen(context)
? Row( ? Row(
children: [ children: [
@ -40,24 +37,7 @@ class RootShell extends StatelessWidget {
Expanded(child: child), Expanded(child: child),
], ],
) )
: Stack( : child,
children: [
child,
Positioned(
bottom: 0,
left: 0,
right: 0,
child: const AppNavigationBottomBar()
.animate(target: showBottom ? 0 : 1)
.slideY(
duration: 250.ms,
begin: 0,
end: 1,
curve: Curves.easeInToLinear,
),
),
],
),
); );
} }
} }

View File

@ -4,7 +4,8 @@ import 'package:go_router/go_router.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/theme.dart'; import 'package:solian/theme.dart';
import 'package:solian/widgets/app_bar_title.dart'; import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/prev_page.dart'; import 'package:solian/widgets/prev_page.dart' as prev;
import 'package:solian/widgets/drawer_button.dart' as drawer;
class TitleShell extends StatelessWidget { class TitleShell extends StatelessWidget {
final bool showAppBar; final bool showAppBar;
@ -28,7 +29,9 @@ class TitleShell extends StatelessWidget {
title: AppBarTitle(state.topRoute?.name?.tr ?? 'page'.tr), title: AppBarTitle(state.topRoute?.name?.tr ?? 'page'.tr),
centerTitle: false, centerTitle: false,
toolbarHeight: SolianTheme.toolbarHeight(context), toolbarHeight: SolianTheme.toolbarHeight(context),
leading: canPop ? const PrevPageButton() : null, leading: canPop
? const prev.PrevPageButton()
: const drawer.DrawerButton(),
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
) )
: null, : null,

View File

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:solian/shells/root_shell.dart';
class DrawerButton extends StatelessWidget {
const DrawerButton({super.key});
void openDrawer() {
rootScaffoldKey.currentState!.openDrawer();
}
@override
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.menu),
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
onPressed: openDrawer,
);
}
}

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:solian/router.dart';
import 'package:solian/shells/root_shell.dart';
import 'package:solian/widgets/navigation/app_navigation.dart';
class AppNavigationDrawer extends StatefulWidget {
const AppNavigationDrawer({super.key});
@override
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
}
class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return NavigationDrawer(
selectedIndex: _selectedIndex,
onDestinationSelected: (idx) {
setState(() => _selectedIndex = idx);
AppRouter.instance.goNamed(AppNavigation.destinations[idx].page);
rootScaffoldKey.currentState!.closeDrawer();
},
children: [
...AppNavigation.destinations.map(
(e) => NavigationDrawerDestination(
icon: e.icon,
label: Text(e.label),
),
)
],
);
}
}