fix: page navigation

This commit is contained in:
DenserMeerkat
2024-06-11 13:11:57 +05:30
parent f0175ff70a
commit aa5fec6558
12 changed files with 367 additions and 138 deletions

View File

@ -4,6 +4,7 @@ import 'package:inner_drawer/inner_drawer.dart';
final mobileDrawerKeyProvider = StateProvider<GlobalKey<InnerDrawerState>>(
(ref) => GlobalKey<InnerDrawerState>());
final leftDrawerStateProvider = StateProvider<bool>((ref) => false);
final navRailIndexStateProvider = StateProvider<int>((ref) => 0);
final selectedIdEditStateProvider = StateProvider<String?>((ref) => null);
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
@ -29,4 +30,5 @@ final nameTextFieldFocusNodeProvider =
return focusNode;
});
final searchQueryProvider = StateProvider<String>((ref) => '');
final collectionSearchQueryProvider = StateProvider<String>((ref) => '');
final environmentSearchQueryProvider = StateProvider<String>((ref) => '');

View File

@ -58,11 +58,13 @@ class Dashboard extends ConsumerWidget {
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: bottomButton(context, ref, railIdx, 2,
child: bottomButton(context, ref, railIdx, 2,
Icons.help, Icons.help_outline),
),
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: bottomButton(context, ref, railIdx, 3,
child: bottomButton(context, ref, railIdx, 3,
Icons.settings, Icons.settings_outlined),
),
@ -95,6 +97,7 @@ class Dashboard extends ConsumerWidget {
children: const [
HomePage(),
SizedBox(),
SizedBox(),
IntroPage(),
SettingsPage(),
],

View File

View File

@ -0,0 +1,129 @@
import 'package:apidash/consts.dart';
import 'package:apidash/extensions/extensions.dart';
import 'package:apidash/models/environment_model.dart';
import 'package:apidash/providers/providers.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class EnvironmentsPane extends ConsumerWidget {
const EnvironmentsPane({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return const SizedBox();
}
}
class EnvironmentsList extends HookConsumerWidget {
const EnvironmentsList({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final environmentSequence = ref.watch(environmentSequenceProvider);
final environmentItems = ref.watch(environmentsStateNotifierProvider)!;
final alwaysShowEnvironmentsPaneScrollbar = ref.watch(settingsProvider
.select((value) => value.alwaysShowCollectionPaneScrollbar));
final filterQuery = ref.watch(environmentSearchQueryProvider).trim();
ScrollController scrollController = useScrollController();
return Scrollbar(
controller: scrollController,
thumbVisibility: alwaysShowEnvironmentsPaneScrollbar,
radius: const Radius.circular(12),
child: filterQuery.isEmpty
? ReorderableListView.builder(
padding: context.isMediumWindow
? EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom,
right: 8,
)
: kPe8,
scrollController: scrollController,
buildDefaultDragHandles: false,
itemCount: environmentSequence.length,
onReorder: (int oldIndex, int newIndex) {
if (oldIndex < newIndex) {
newIndex -= 1;
}
if (oldIndex != newIndex) {
ref
.read(collectionStateNotifierProvider.notifier)
.reorder(oldIndex, newIndex);
}
},
itemBuilder: (context, index) {
var id = environmentSequence[index];
if (kIsMobile) {
return ReorderableDelayedDragStartListener(
key: ValueKey(id),
index: index,
child: Padding(
padding: kP1,
child: EnvironmentItem(
id: id,
environmentModel: environmentItems[id]!,
),
),
);
}
return ReorderableDragStartListener(
key: ValueKey(id),
index: index,
child: Padding(
padding: kP1,
child: EnvironmentItem(
id: id,
environmentModel: environmentItems[id]!,
),
),
);
},
)
: ListView(
padding: context.isMediumWindow
? EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom,
right: 8,
)
: kPe8,
controller: scrollController,
children: environmentSequence.map((id) {
var item = environmentItems[id]!;
if (item.name.toLowerCase().contains(filterQuery)) {
return Padding(
padding: kP1,
child: EnvironmentItem(
id: id,
environmentModel: item,
),
);
}
return const SizedBox();
}).toList(),
),
);
}
}
class EnvironmentItem extends ConsumerWidget {
const EnvironmentItem({
super.key,
required this.id,
required this.environmentModel,
});
final String id;
final EnvironmentModel environmentModel;
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedId = ref.watch(selectedEnvironmentIdProvider);
return Text(environmentModel.name);
}
}

View File

@ -94,7 +94,7 @@ class CollectionPane extends ConsumerWidget {
style: Theme.of(context).textTheme.bodyMedium,
hintText: "Filter by name or URL",
onChanged: (value) {
ref.read(searchQueryProvider.notifier).state =
ref.read(collectionSearchQueryProvider.notifier).state =
value.toLowerCase();
},
),
@ -142,7 +142,7 @@ class _RequestListState extends ConsumerState<RequestList> {
final requestItems = ref.watch(collectionStateNotifierProvider)!;
final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider
.select((value) => value.alwaysShowCollectionPaneScrollbar));
final filterQuery = ref.watch(searchQueryProvider).trim();
final filterQuery = ref.watch(collectionSearchQueryProvider).trim();
return Scrollbar(
controller: controller,

View File

@ -1,4 +1,4 @@
import 'package:apidash/consts.dart';
import 'package:apidash/widgets/splitviews.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -6,11 +6,13 @@ import 'package:inner_drawer/inner_drawer.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:apidash/extensions/extensions.dart';
import 'package:apidash/providers/providers.dart';
import '../intro_page.dart';
import '../settings_page.dart';
import 'navbar.dart';
import 'widgets/left_drawer.dart';
import 'requests_page.dart';
import 'response_drawer.dart';
import '../home_page/collection_pane.dart';
import 'widgets/page_base.dart';
class MobileDashboard extends ConsumerStatefulWidget {
const MobileDashboard({super.key});
@ -20,31 +22,14 @@ class MobileDashboard extends ConsumerStatefulWidget {
}
class _MobileDashboardState extends ConsumerState<MobileDashboard> {
late Color backgroundColor;
bool isLeftDrawerOpen = false;
ValueNotifier<double> dragPosition = ValueNotifier(0);
ValueNotifier<InnerDrawerDirection?> drawerDirection =
ValueNotifier(InnerDrawerDirection.start);
Color calculateBackgroundColor(double dragPosition) {
Color start = Theme.of(context).colorScheme.surface;
Color end = Theme.of(context).colorScheme.onInverseSurface;
return dragPosition == 0 ? start : end;
}
@override
void dispose() {
super.dispose();
dragPosition.dispose();
drawerDirection.dispose();
}
@override
Widget build(
BuildContext context,
) {
final railIdx = ref.watch(navRailIndexStateProvider);
final GlobalKey<InnerDrawerState> innerDrawerKey =
ref.watch(mobileDrawerKeyProvider);
final isLeftDrawerOpen = ref.watch(leftDrawerStateProvider);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: FlexColorScheme.themedSystemNavigationBar(
context,
@ -54,65 +39,9 @@ class _MobileDashboardState extends ConsumerState<MobileDashboard> {
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
InnerDrawer(
key: innerDrawerKey,
swipe: true,
swipeChild: true,
onTapClose: true,
offset: !context.isCompactWindow
? const IDOffset.only(left: 0.1, right: 1)
: const IDOffset.only(left: 0.7, right: 1),
boxShadow: [
BoxShadow(
offset: const Offset(1, 0),
color: Theme.of(context).colorScheme.onInverseSurface,
blurRadius: 0,
),
],
colorTransitionChild: Colors.transparent,
colorTransitionScaffold: Colors.transparent,
rightAnimationType: InnerDrawerAnimation.linear,
backgroundDecoration: BoxDecoration(
color: Theme.of(context).colorScheme.onInverseSurface),
onDragUpdate: (value, direction) {
drawerDirection.value = direction;
if (value > 0.98 && direction == InnerDrawerDirection.start) {
dragPosition.value = 1;
} else {
dragPosition.value = 0;
}
},
innerDrawerCallback: (isOpened) {
if (drawerDirection.value == InnerDrawerDirection.start) {
setState(() {
isLeftDrawerOpen = isOpened;
});
}
},
leftChild: const LeftDrawer(
drawerContent: CollectionPane(),
),
rightChild: const ResponseDrawer(),
scaffold: ValueListenableBuilder<double>(
valueListenable: dragPosition,
builder: (context, value, child) {
return Container(
color: calculateBackgroundColor(value),
child: child,
);
},
child: ClipRRect(
borderRadius:
const BorderRadius.only(topLeft: Radius.circular(8)),
child: SafeArea(
minimum: kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero,
bottom: false,
child: RequestsPage(
innerDrawerKey: innerDrawerKey,
),
),
),
),
PageBranch(
pageIndex: railIdx,
innerDrawerKey: innerDrawerKey,
),
if (context.isCompactWindow)
AnimatedPositioned(
@ -131,3 +60,53 @@ class _MobileDashboardState extends ConsumerState<MobileDashboard> {
);
}
}
class PageBranch extends StatelessWidget {
const PageBranch({
super.key,
required this.pageIndex,
required this.innerDrawerKey,
});
final int pageIndex;
final GlobalKey<InnerDrawerState> innerDrawerKey;
@override
Widget build(BuildContext context) {
switch (pageIndex) {
case 1:
return TwoDrawerSplitView(
key: const ValueKey('env'),
innerDrawerKey: innerDrawerKey,
offset: !context.isCompactWindow
? const IDOffset.only(left: 0.1)
: const IDOffset.only(left: 0.7),
leftDrawerContent: const SizedBox(),
mainContent: const SizedBox(),
);
case 2:
return const PageBase(
title: 'About',
scaffoldBody: IntroPage(),
);
case 3:
return const PageBase(
title: 'Settings',
scaffoldBody: SettingsPage(),
);
default:
return TwoDrawerSplitView(
key: const ValueKey('home'),
innerDrawerKey: innerDrawerKey,
offset: !context.isCompactWindow
? const IDOffset.only(left: 0.1, right: 1)
: const IDOffset.only(left: 0.7, right: 1),
leftDrawerContent: const CollectionPane(),
rightDrawerContent: const ResponseDrawer(),
mainContent: RequestsPage(
innerDrawerKey: innerDrawerKey,
),
);
}
}
}

View File

@ -2,9 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/extensions/context_extensions.dart';
import 'package:apidash/providers/ui_providers.dart';
import 'package:apidash/screens/mobile/widgets/page_base.dart';
import '../settings_page.dart';
import '../intro_page.dart';
class BottomNavBar extends ConsumerWidget {
const BottomNavBar({super.key});
@ -48,30 +45,26 @@ class BottomNavBar extends ConsumerWidget {
'Variables'),
),
Expanded(
child: customNavigationDestination(context, ref, railIdx, 2,
Icons.help, Icons.help_outline, 'About',
isNavigator: true, onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const PageBase(
title: 'About',
scaffoldBody: IntroPage(),
)),
);
}),
child: customNavigationDestination(
context,
ref,
railIdx,
2,
Icons.help,
Icons.help_outline,
'About',
),
),
Expanded(
child: customNavigationDestination(context, ref, railIdx, 3,
Icons.settings, Icons.settings_outlined, 'Settings',
isNavigator: true, onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const PageBase(
title: 'Settings',
scaffoldBody: SettingsPage(),
)),
);
}),
child: customNavigationDestination(
context,
ref,
railIdx,
3,
Icons.settings,
Icons.settings_outlined,
'Settings',
),
),
],
),
@ -131,17 +124,6 @@ class NavRail extends ConsumerWidget {
Icons.help,
Icons.help_outline,
'About',
isNavigator: true,
showLabel: false,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const PageBase(
title: 'About',
scaffoldBody: IntroPage(),
)),
);
},
),
const SizedBox(height: 24),
customNavigationDestination(
@ -152,17 +134,6 @@ class NavRail extends ConsumerWidget {
Icons.settings,
Icons.settings_outlined,
'Settings',
isNavigator: true,
showLabel: false,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const PageBase(
title: 'Settings',
scaffoldBody: SettingsPage(),
)),
);
},
),
],
),
@ -179,7 +150,6 @@ Widget customNavigationDestination(
IconData selectedIcon,
IconData icon,
String label, {
bool isNavigator = false,
bool showLabel = true,
Function()? onTap,
}) {
@ -195,9 +165,9 @@ Widget customNavigationDestination(
onTap: isSelected
? null
: () {
if (!isNavigator) {
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx;
ref.read(navRailIndexStateProvider.notifier).state = buttonIdx;
if (railIdx > 1 && buttonIdx <= 1) {
ref.read(leftDrawerStateProvider.notifier).state = false;
}
onTap?.call();
},
@ -218,9 +188,11 @@ Widget customNavigationDestination(
onTap: isSelected
? null
: () {
if (!isNavigator) {
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx;
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx;
if (railIdx > 1 && buttonIdx <= 1) {
ref.read(leftDrawerStateProvider.notifier).state =
false;
}
onTap?.call();
},

View File

@ -16,7 +16,8 @@ class PageBase extends ConsumerWidget {
return Stack(
children: [
Container(
padding: kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero,
padding: const EdgeInsets.only(bottom: 70) +
(kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero),
color: Theme.of(context).colorScheme.surface,
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,

View File

@ -152,3 +152,41 @@ class RequestDetailsCard extends StatelessWidget {
);
}
}
class SidebarEnvironmentCard extends StatelessWidget {
const SidebarEnvironmentCard({
super.key,
required this.id,
this.isGlobal = false,
this.isSelected = false,
this.isActive = false,
this.name,
this.editRequestId,
this.onTap,
this.onDoubleTap,
this.onSecondaryTap,
this.onChangedNameEditor,
this.focusNode,
this.onTapOutsideNameEditor,
this.onMenuSelected,
});
final String id;
final bool isGlobal;
final bool isSelected;
final bool isActive;
final String? name;
final String? editRequestId;
final void Function()? onTap;
final void Function()? onDoubleTap;
final void Function()? onSecondaryTap;
final Function(String)? onChangedNameEditor;
final FocusNode? focusNode;
final Function()? onTapOutsideNameEditor;
final Function(RequestItemMenuOption)? onMenuSelected;
@override
Widget build(BuildContext context) {
return const SizedBox();
}
}

View File

@ -1,6 +1,11 @@
import 'package:apidash/screens/mobile/widgets/left_drawer.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:inner_drawer/inner_drawer.dart';
import 'package:multi_split_view/multi_split_view.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/providers/ui_providers.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class DashboardSplitView extends StatefulWidget {
const DashboardSplitView({
@ -110,3 +115,85 @@ class _EqualSplitViewState extends State<EqualSplitView> {
super.dispose();
}
}
class TwoDrawerSplitView extends HookConsumerWidget {
const TwoDrawerSplitView({
super.key,
required this.innerDrawerKey,
required this.offset,
required this.mainContent,
required this.leftDrawerContent,
this.rightDrawerContent,
});
final GlobalKey<InnerDrawerState> innerDrawerKey;
final IDOffset offset;
final Widget mainContent;
final Widget leftDrawerContent;
final Widget? rightDrawerContent;
@override
Widget build(BuildContext context, WidgetRef ref) {
ValueNotifier<double> dragPosition = useState(0.0);
ValueNotifier<InnerDrawerDirection?> drawerDirection =
ValueNotifier(InnerDrawerDirection.start);
Color calculateBackgroundColor(double dragPosition) {
Color start = Theme.of(context).colorScheme.surface;
Color end = Theme.of(context).colorScheme.onInverseSurface;
return dragPosition == 0 ? start : end;
}
return InnerDrawer(
key: innerDrawerKey,
swipe: true,
swipeChild: true,
onTapClose: true,
offset: offset,
boxShadow: [
BoxShadow(
offset: const Offset(1, 0),
color: Theme.of(context).colorScheme.onInverseSurface,
blurRadius: 0,
),
],
colorTransitionChild: Colors.transparent,
colorTransitionScaffold: Colors.transparent,
rightAnimationType: InnerDrawerAnimation.linear,
backgroundDecoration:
BoxDecoration(color: Theme.of(context).colorScheme.onInverseSurface),
onDragUpdate: (value, direction) {
drawerDirection.value = direction;
if (value > 0.98 && direction == InnerDrawerDirection.start) {
dragPosition.value = 1;
} else {
dragPosition.value = 0;
}
},
innerDrawerCallback: (isOpened) {
if (drawerDirection.value == InnerDrawerDirection.start) {
ref.read(leftDrawerStateProvider.notifier).state = isOpened;
}
},
leftChild: LeftDrawer(drawerContent: leftDrawerContent),
rightChild: rightDrawerContent,
scaffold: ValueListenableBuilder<double>(
valueListenable: dragPosition,
builder: (context, value, child) {
return Container(
color: calculateBackgroundColor(value),
child: child,
);
},
child: ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(8)),
child: SafeArea(
minimum: kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero,
bottom: false,
child: mainContent,
),
),
),
);
}
}

View File

@ -390,6 +390,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_hooks:
dependency: "direct main"
description:
name: flutter_hooks
sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70
url: "https://pub.dev"
source: hosted
version: "0.20.5"
flutter_keyboard_visibility:
dependency: transitive
description:
@ -576,6 +584,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
hooks_riverpod:
dependency: "direct main"
description:
name: hooks_riverpod
sha256: "45b2030a18bcd6dbd680c2c91bc3b33e3fe7c323e3acb5ecec93a613e2fbaa8a"
url: "https://pub.dev"
source: hosted
version: "2.5.1"
html:
dependency: transitive
description:

View File

@ -61,6 +61,8 @@ dependencies:
flex_color_scheme: ^7.3.1
data_table_2: ^2.5.11
file_selector: ^1.0.3
hooks_riverpod: ^2.5.1
flutter_hooks: ^0.20.5
dependency_overrides:
web: ^0.5.0