Merge branch 'main' into resolve-issue-env-field

This commit is contained in:
Ragul Raj
2024-07-11 23:26:36 +05:30
committed by GitHub
17 changed files with 258 additions and 256 deletions

View File

@ -124,6 +124,9 @@ const kP8CollectionPane = EdgeInsets.only(
const kPt8 = EdgeInsets.only(
top: 8,
);
const kPt20 = EdgeInsets.only(
top: 20,
);
const kPt28 = EdgeInsets.only(
top: 28,
);

View File

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
class NavbarButton extends ConsumerWidget {
const NavbarButton({
super.key,
required this.railIdx,
this.buttonIdx,
required this.selectedIcon,
required this.icon,
required this.label,
this.showLabel = true,
this.isCompact = false,
this.onTap,
});
final int railIdx;
final int? buttonIdx;
final IconData selectedIcon;
final IconData icon;
final String label;
final bool showLabel;
final Function()? onTap;
final bool isCompact;
@override
Widget build(BuildContext context, WidgetRef ref) {
final bool isSelected = railIdx == buttonIdx;
final Size size = isCompact ? const Size(56, 32) : const Size(65, 32);
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: isSelected
? null
: () {
if (buttonIdx != null) {
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx!;
if (railIdx > 1 && buttonIdx! <= 1) {
ref.read(leftDrawerStateProvider.notifier).state = false;
}
}
onTap?.call();
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
style: isSelected
? TextButton.styleFrom(
fixedSize: size,
backgroundColor:
Theme.of(context).colorScheme.secondaryContainer,
)
: TextButton.styleFrom(
fixedSize: size,
),
onPressed: isSelected
? null
: () {
if (buttonIdx != null) {
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx!;
if (railIdx > 1 && buttonIdx! <= 1) {
ref.read(leftDrawerStateProvider.notifier).state =
false;
}
}
onTap?.call();
},
child: Icon(
isSelected ? selectedIcon : icon,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
showLabel ? const SizedBox(height: 4) : const SizedBox.shrink(),
showLabel
? Text(
label,
style: Theme.of(context).textTheme.labelSmall!.copyWith(
fontWeight: FontWeight.w600,
color: isSelected
? Theme.of(context)
.colorScheme
.onSecondaryContainer
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.65),
),
)
: const SizedBox.shrink(),
],
),
),
);
}
}

View File

@ -1,3 +1,4 @@
export 'button_navbar.dart';
export 'editor_title.dart';
export 'editor_title_actions.dart';
export 'envfield_url.dart';

View File

@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/consts.dart';
import 'common_widgets/common_widgets.dart';
import 'envvar/environment_page.dart';
import 'home_page/home_page.dart';
import 'intro_page.dart';
import 'settings_page.dart';
class Dashboard extends ConsumerWidget {
@ -52,6 +53,19 @@ class Dashboard extends ConsumerWidget {
'Variables',
style: Theme.of(context).textTheme.labelSmall,
),
kVSpacer10,
// IconButton(
// isSelected: railIdx == 2,
// onPressed: () {
// ref.read(navRailIndexStateProvider.notifier).state = 2;
// },
// icon: const Icon(Icons.history_outlined),
// selectedIcon: const Icon(Icons.history),
// ),
// Text(
// 'History',
// style: Theme.of(context).textTheme.labelSmall,
// ),
],
),
Expanded(
@ -60,30 +74,34 @@ class Dashboard extends ConsumerWidget {
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: bottomButton(context, ref, railIdx, 2,
Icons.help, Icons.help_outline),
child: NavbarButton(
railIdx: railIdx,
selectedIcon: Icons.help,
icon: Icons.help_outline,
label: 'About',
showLabel: false,
isCompact: true,
onTap: () {
showAboutAppDialog(context);
},
),
),
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: bottomButton(context, ref, railIdx, 3,
Icons.settings, Icons.settings_outlined),
child: NavbarButton(
railIdx: railIdx,
buttonIdx: 2,
selectedIcon: Icons.settings,
icon: Icons.settings_outlined,
label: 'Settings',
showLabel: false,
isCompact: true,
),
),
],
),
),
],
// destinations: const <NavigationRailDestination>[
// // NavigationRailDestination(
// // icon: Icon(Icons.home_outlined),
// // selectedIcon: Icon(Icons.home),
// // label: Text('Home'),
// // ),
// NavigationRailDestination(
// icon: Icon(Icons.auto_awesome_mosaic_outlined),
// selectedIcon: Icon(Icons.auto_awesome_mosaic),
// label: Text('Requests'),
// ),
// ],
),
VerticalDivider(
thickness: 1,
@ -99,7 +117,6 @@ class Dashboard extends ConsumerWidget {
EnvironmentPage(
scaffoldKey: mobileScaffoldKey,
),
const IntroPage(),
const SettingsPage(),
],
),
@ -109,31 +126,4 @@ class Dashboard extends ConsumerWidget {
),
);
}
TextButton bottomButton(
BuildContext context,
WidgetRef ref,
int railIdx,
int buttonIdx,
IconData selectedIcon,
IconData icon,
) {
bool isSelected = railIdx == buttonIdx;
return TextButton(
style: isSelected
? TextButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
)
: null,
onPressed: isSelected
? null
: () {
ref.read(navRailIndexStateProvider.notifier).state = buttonIdx;
},
child: Icon(
isSelected ? selectedIcon : icon,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
);
}
}

View File

@ -1,11 +0,0 @@
import 'package:flutter/material.dart';
import 'package:apidash/widgets/widgets.dart';
class IntroPage extends StatelessWidget {
const IntroPage({super.key});
@override
Widget build(BuildContext context) {
return const IntroMessage();
}
}

View File

@ -4,7 +4,6 @@ import 'package:flutter_riverpod/flutter_riverpod.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 'requests_page/requests_page.dart';
@ -73,12 +72,13 @@ class PageBranch extends ConsumerWidget {
return EnvironmentPage(
scaffoldKey: scaffoldKey,
);
// case 2:
// // TODO: Implement history page
// return const PageBase(
// title: 'History',
// scaffoldBody: SizedBox(),
// );
case 2:
return const PageBase(
title: 'About',
scaffoldBody: IntroPage(),
);
case 3:
return const PageBase(
title: 'Settings',
scaffoldBody: SettingsPage(),

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
import '../common_widgets/common_widgets.dart';
class BottomNavBar extends ConsumerWidget {
const BottomNavBar({super.key});
@ -30,39 +31,39 @@ class BottomNavBar extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: customNavigationDestination(context, ref, railIdx, 0,
Icons.dashboard, Icons.dashboard_outlined, 'Requests'),
),
Expanded(
child: customNavigationDestination(
context,
ref,
railIdx,
1,
Icons.laptop_windows,
Icons.laptop_windows_outlined,
'Variables'),
),
Expanded(
child: customNavigationDestination(
context,
ref,
railIdx,
2,
Icons.help,
Icons.help_outline,
'About',
child: NavbarButton(
railIdx: railIdx,
buttonIdx: 0,
selectedIcon: Icons.dashboard,
icon: Icons.dashboard_outlined,
label: 'Requests',
),
),
Expanded(
child: customNavigationDestination(
context,
ref,
railIdx,
3,
Icons.settings,
Icons.settings_outlined,
'Settings',
child: NavbarButton(
railIdx: railIdx,
buttonIdx: 1,
selectedIcon: Icons.laptop_windows,
icon: Icons.laptop_windows_outlined,
label: 'Variables',
),
),
// Expanded(
// child: NavbarButton(
// railIdx: railIdx,
// buttonIdx: 2,
// selectedIcon: Icons.history,
// icon: Icons.history_outlined,
// label: 'History',
// ),
// ),
Expanded(
child: NavbarButton(
railIdx: railIdx,
buttonIdx: 2,
selectedIcon: Icons.settings,
icon: Icons.settings_outlined,
label: 'Settings',
),
),
],
@ -73,82 +74,3 @@ class BottomNavBar extends ConsumerWidget {
);
}
}
Widget customNavigationDestination(
BuildContext context,
WidgetRef ref,
int railIdx,
int buttonIdx,
IconData selectedIcon,
IconData icon,
String label, {
bool showLabel = true,
Function()? onTap,
}) {
bool isSelected = railIdx == buttonIdx;
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: isSelected
? null
: () {
ref.read(navRailIndexStateProvider.notifier).state = buttonIdx;
if (railIdx > 1 && buttonIdx <= 1) {
ref.read(leftDrawerStateProvider.notifier).state = false;
}
onTap?.call();
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Ink(
width: 65,
height: 32,
decoration: BoxDecoration(
color: isSelected
? Theme.of(context).colorScheme.secondaryContainer
: Colors.transparent,
borderRadius: BorderRadius.circular(30),
),
child: InkWell(
borderRadius: BorderRadius.circular(30),
onTap: isSelected
? null
: () {
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx;
if (railIdx > 1 && buttonIdx <= 1) {
ref.read(leftDrawerStateProvider.notifier).state =
false;
}
onTap?.call();
},
child: Icon(
isSelected ? selectedIcon : icon,
color: isSelected
? Theme.of(context).colorScheme.onSecondaryContainer
: Theme.of(context).colorScheme.onSurface.withOpacity(0.65),
),
),
),
showLabel ? const SizedBox(height: 4) : const SizedBox.shrink(),
showLabel
? Text(
label,
style: Theme.of(context).textTheme.labelSmall!.copyWith(
fontWeight: FontWeight.w600,
color: isSelected
? Theme.of(context).colorScheme.onSecondaryContainer
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.65),
),
)
: const SizedBox.shrink(),
],
),
),
);
}

View File

@ -37,10 +37,8 @@ class SettingsPage extends ConsumerWidget {
Expanded(
child: ListView(
shrinkWrap: true,
padding: kPh20,
children: [
SwitchListTile(
contentPadding: EdgeInsets.zero,
hoverColor: kColorTransparent,
title: const Text('Switch Theme Mode'),
subtitle: Text(
@ -51,7 +49,6 @@ class SettingsPage extends ConsumerWidget {
},
),
SwitchListTile(
contentPadding: EdgeInsets.zero,
hoverColor: kColorTransparent,
title: const Text('Collection Pane Scrollbar Visiblity'),
subtitle: Text(
@ -64,7 +61,6 @@ class SettingsPage extends ConsumerWidget {
},
),
ListTile(
contentPadding: kPb10,
hoverColor: kColorTransparent,
title: const Text('Default URI Scheme'),
subtitle: Text(
@ -109,7 +105,6 @@ class SettingsPage extends ConsumerWidget {
),
),
ListTile(
contentPadding: kPb10,
hoverColor: kColorTransparent,
title: const Text('Default Code Generator'),
trailing: Container(
@ -151,7 +146,6 @@ class SettingsPage extends ConsumerWidget {
),
),
CheckboxListTile(
contentPadding: EdgeInsets.zero,
title: const Text("Save Responses"),
subtitle:
const Text("Save disk space by not storing API responses"),
@ -163,7 +157,6 @@ class SettingsPage extends ConsumerWidget {
},
),
CheckboxListTile(
contentPadding: EdgeInsets.zero,
title: const Text("Show Save Alert on App Close"),
subtitle: const Text(
"Show a confirmation dialog to save workspace when the user closes the app"),
@ -175,7 +168,6 @@ class SettingsPage extends ConsumerWidget {
},
),
ListTile(
contentPadding: EdgeInsets.zero,
hoverColor: kColorTransparent,
title: const Text('Export Data'),
subtitle: const Text(
@ -195,7 +187,6 @@ class SettingsPage extends ConsumerWidget {
),
),
ListTile(
contentPadding: EdgeInsets.zero,
hoverColor: kColorTransparent,
title: const Text('Clear Data'),
subtitle: const Text('Delete all requests data from the disk'),
@ -250,6 +241,15 @@ class SettingsPage extends ConsumerWidget {
),
),
),
ListTile(
title: const Text('About'),
subtitle: const Text(
'Release Details, Support Channel, Report Bug / Request New Feature'),
onTap: () {
showAboutAppDialog(context);
},
),
kVSpacer20,
],
),
),

View File

@ -0,0 +1,29 @@
import 'package:apidash/consts.dart';
import 'package:flutter/material.dart';
import 'package:apidash/widgets/widgets.dart';
showAboutAppDialog(
BuildContext context,
) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
contentPadding: kPt20 + kPh20 + kPb10,
content: Container(
width: double.infinity,
height: double.infinity,
constraints: const BoxConstraints(maxWidth: 540, maxHeight: 544),
child: const IntroMessage(),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Close"),
),
],
);
});
}

View File

@ -4,7 +4,6 @@ import 'package:package_info_plus/package_info_plus.dart';
import '../consts.dart';
import 'markdown.dart';
import 'error_message.dart';
import 'package:apidash/extensions/extensions.dart';
class IntroMessage extends StatelessWidget {
const IntroMessage({
@ -38,7 +37,7 @@ class IntroMessage extends StatelessWidget {
return CustomMarkdown(
data: text,
padding: !context.isMediumWindow ? kPh60 : kPh20,
padding: EdgeInsets.zero,
);
}
return const Center(child: CircularProgressIndicator());

View File

@ -19,8 +19,8 @@ class DashboardSplitView extends StatefulWidget {
class DashboardSplitViewState extends State<DashboardSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(id: "sidebar", size: 250, min: 200),
Area(id: "main", min: 0.7),
Area(id: "sidebar", min: 220, size: 250, max: 350),
Area(id: "main", min: 400),
],
);
@ -44,6 +44,8 @@ class DashboardSplitViewState extends State<DashboardSplitView> {
),
child: MultiSplitView(
controller: _controller,
sizeOverflowPolicy: SizeOverflowPolicy.shrinkFirst,
sizeUnderflowPolicy: SizeUnderflowPolicy.stretchLast,
builder: (context, area) {
return switch (area.id) {
"sidebar" => widget.sidebarWidget,

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:multi_split_view/multi_split_view.dart';
import 'package:apidash/consts.dart';
class EqualSplitView extends StatefulWidget {
class EqualSplitView extends StatelessWidget {
const EqualSplitView({
super.key,
required this.leftWidget,
@ -12,17 +12,17 @@ class EqualSplitView extends StatefulWidget {
final Widget leftWidget;
final Widget rightWidget;
@override
State<EqualSplitView> createState() => _EqualSplitViewState();
}
class _EqualSplitViewState extends State<EqualSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(id: "left", min: kMinRequestEditorDetailsCardPaneSize),
Area(id: "right", min: kMinRequestEditorDetailsCardPaneSize),
],
);
getMinFractionWidth(double width) {
if (width < 900) {
return 0.9;
} else if (width < 1000) {
return 0.7;
} else if (width < 1200) {
return 0.5;
} else {
return 0.4;
}
}
@override
Widget build(BuildContext context) {
@ -37,22 +37,26 @@ class _EqualSplitViewState extends State<EqualSplitView> {
animationEnabled: false,
),
),
child: MultiSplitView(
controller: _controller,
builder: (context, area) {
return switch (area.id) {
"left" => widget.leftWidget,
"right" => widget.rightWidget,
_ => Container(),
};
child: LayoutBuilder(
builder: (context, constraints) {
final minWidth = getMinFractionWidth(constraints.maxWidth);
return MultiSplitView(
controller: MultiSplitViewController(
areas: [
Area(id: "left", flex: 1, min: minWidth),
Area(id: "right", flex: 1, min: minWidth),
],
),
builder: (context, area) {
return switch (area.id) {
"left" => leftWidget,
"right" => rightWidget,
_ => Container(),
};
},
);
},
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@ -10,7 +10,8 @@ export 'card_sidebar_request.dart';
export 'checkbox.dart';
export 'code_previewer.dart';
export 'codegen_previewer.dart';
export 'dialogs.dart';
export 'dialog_about.dart';
export 'dialog_rename.dart';
export 'dropdown_codegen.dart';
export 'dropdown_content_type.dart';
export 'dropdown_formdata.dart';

View File

@ -853,10 +853,10 @@ packages:
dependency: "direct main"
description:
name: multi_split_view
sha256: "2ef6a7ff9d0957bf559075d2703507ea898c8b70e37ff5ac3636298370513b7a"
sha256: "1ee1974d9aae6bdc08e2abdead6066c914cefe4b0c5999cac1a2e4722fcf33ba"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
version: "3.2.2"
multi_trigger_autocomplete:
dependency: "direct main"
description:

View File

@ -10,7 +10,7 @@ environment:
dependencies:
flutter:
sdk: flutter
multi_split_view: ^3.2.1
multi_split_view: ^3.2.2
url_launcher: ^6.2.5
flutter_riverpod: ^2.5.1
riverpod: ^2.5.1

View File

@ -11,7 +11,6 @@ import 'package:apidash/screens/home_page/editor_pane/editor_default.dart';
import 'package:apidash/screens/home_page/editor_pane/editor_pane.dart';
import 'package:apidash/screens/home_page/editor_pane/url_card.dart';
import 'package:apidash/screens/home_page/home_page.dart';
import 'package:apidash/screens/intro_page.dart';
import 'package:apidash/screens/settings_page.dart';
import 'package:apidash/services/hive_services.dart';
import 'package:apidash/widgets/widgets.dart';
@ -58,7 +57,6 @@ void main() {
// Verify that the HomePage is displayed initially
expect(find.byType(HomePage), findsOneWidget);
expect(find.byType(EnvironmentPage), findsNothing);
expect(find.byType(IntroPage), findsNothing);
expect(find.byType(SettingsPage), findsNothing);
});
@ -79,12 +77,11 @@ void main() {
// Verify that the EnvironmentPage is displayed
expect(find.byType(HomePage), findsNothing);
expect(find.byType(EnvironmentPage), findsOneWidget);
expect(find.byType(IntroPage), findsNothing);
expect(find.byType(SettingsPage), findsNothing);
});
testWidgets(
"Dashboard should display IntroPage when navRailIndexStateProvider is 2",
"Dashboard should display SettingsPage when navRailIndexStateProvider is 2",
(WidgetTester tester) async {
await tester.pumpWidget(
ProviderScope(
@ -99,33 +96,9 @@ void main() {
),
);
// Verify that the IntroPage is displayed
expect(find.byType(HomePage), findsNothing);
expect(find.byType(EnvironmentPage), findsNothing);
expect(find.byType(IntroPage), findsOneWidget);
expect(find.byType(SettingsPage), findsNothing);
});
testWidgets(
"Dashboard should display SettingsPage when navRailIndexStateProvider is 3",
(WidgetTester tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
navRailIndexStateProvider.overrideWith((ref) => 3),
],
child: const Portal(
child: MaterialApp(
home: Dashboard(),
),
),
),
);
// Verify that the SettingsPage is displayed
expect(find.byType(HomePage), findsNothing);
expect(find.byType(EnvironmentPage), findsNothing);
expect(find.byType(IntroPage), findsNothing);
expect(find.byType(SettingsPage), findsOneWidget);
});
@ -142,8 +115,8 @@ void main() {
),
);
// Tap on the Intro icon
await tester.tap(find.byIcon(Icons.help_outline));
// Tap on the Settings icon
await tester.tap(find.byIcon(Icons.settings_outlined));
await tester.pump();
// Verify that the navRailIndexStateProvider is updated
@ -184,11 +157,10 @@ void main() {
// Verify that the navRailIndexStateProvider still has the updated value
final dashboard = tester.element(find.byType(Dashboard));
final container = ProviderScope.containerOf(dashboard);
expect(container.read(navRailIndexStateProvider), 3);
expect(container.read(navRailIndexStateProvider), 2);
// Verify that the SettingsPage is still displayed after the rebuild
expect(find.byType(SettingsPage), findsOneWidget);
expect(find.byType(IntroPage), findsNothing);
expect(find.byType(HomePage), findsNothing);
expect(find.byType(EnvironmentPage), findsNothing);
});
@ -219,17 +191,8 @@ void main() {
// Verify that the selected icon is the filled version (selectedIcon)
expect(find.byIcon(Icons.computer_rounded), findsOneWidget);
// Go to IntroPage
container.read(navRailIndexStateProvider.notifier).state = 2;
await tester.pump();
// Verify that the IntroPage is displayed
expect(find.byType(IntroPage), findsOneWidget);
// Verify that the selected icon is the filled version (selectedIcon)
expect(find.byIcon(Icons.help), findsOneWidget);
// Go to SettingsPage
container.read(navRailIndexStateProvider.notifier).state = 3;
container.read(navRailIndexStateProvider.notifier).state = 2;
await tester.pump();
// Verify that the SettingsPage is displayed