mirror of
https://github.com/foss42/apidash.git
synced 2025-08-06 05:32:26 +08:00
wip: desktop responsiveness
This commit is contained in:
34
lib/app.dart
34
lib/app.dart
@ -6,7 +6,6 @@ import 'package:window_manager/window_manager.dart' hide WindowCaption;
|
||||
import 'widgets/widgets.dart' show WindowCaption;
|
||||
import 'providers/providers.dart';
|
||||
import 'screens/screens.dart';
|
||||
import 'extensions/extensions.dart';
|
||||
import 'consts.dart';
|
||||
|
||||
class App extends ConsumerStatefulWidget {
|
||||
@ -125,24 +124,23 @@ class DashApp extends ConsumerWidget {
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
home: kIsMobile
|
||||
? context.isLargeWidth
|
||||
? const Dashboard()
|
||||
: const MobileDashboard()
|
||||
: Stack(
|
||||
children: [
|
||||
kIsLinux ? const Dashboard() : const App(),
|
||||
if (kIsWindows)
|
||||
SizedBox(
|
||||
height: 29,
|
||||
child: WindowCaption(
|
||||
backgroundColor: Colors.transparent,
|
||||
brightness:
|
||||
isDarkMode ? Brightness.dark : Brightness.light,
|
||||
),
|
||||
),
|
||||
],
|
||||
home: Stack(
|
||||
children: [
|
||||
context.isMediumWindow
|
||||
? const MobileDashboard()
|
||||
: !kIsLinux && !kIsMobile
|
||||
? const App()
|
||||
: const Dashboard(),
|
||||
if (kIsWindows)
|
||||
SizedBox(
|
||||
height: 29,
|
||||
child: WindowCaption(
|
||||
backgroundColor: Colors.transparent,
|
||||
brightness: isDarkMode ? Brightness.dark : Brightness.light,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,14 @@ final kColorLightDanger = Colors.red.withOpacity(0.9);
|
||||
const kColorDarkDanger = Color(0xffcf6679);
|
||||
|
||||
const kWindowTitle = "API Dash";
|
||||
const kMinWindowSize = Size(900, 600);
|
||||
const kMinWindowSize = Size(320, 640);
|
||||
const kMinInitialWindowWidth = 1200.0;
|
||||
const kMinInitialWindowHeight = 800.0;
|
||||
const kMinRequestEditorDetailsCardPaneSize = 300.0;
|
||||
const kLargeMobileWidth = 600.0;
|
||||
const kCompactWindowWidth = 600.0;
|
||||
const kMediumWindowWidth = 840.0;
|
||||
const kExpandedWindowWidth = 1200.0;
|
||||
const kLargeWindowWidth = 1600.0;
|
||||
|
||||
const kColorSchemeSeed = Colors.blue;
|
||||
final kFontFamily = GoogleFonts.openSans().fontFamily;
|
||||
@ -106,6 +109,8 @@ const kP8CollectionPane = EdgeInsets.only(
|
||||
//right: 4.0,
|
||||
// bottom: 8.0,
|
||||
);
|
||||
const kPt28 = EdgeInsets.only(top: 28);
|
||||
const kPt32 = EdgeInsets.only(top: 32);
|
||||
const kPb10 = EdgeInsets.only(
|
||||
bottom: 10,
|
||||
);
|
||||
|
@ -2,9 +2,17 @@ import 'package:apidash/consts.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension MediaQueryExtension on BuildContext {
|
||||
bool get isLargeWidth =>
|
||||
MediaQuery.of(this).size.width > kMinWindowSize.width;
|
||||
bool get isCompactWindow =>
|
||||
MediaQuery.of(this).size.width < kCompactWindowWidth;
|
||||
|
||||
bool get isMobile =>
|
||||
kIsMobile && MediaQuery.of(this).size.width < kMinWindowSize.width;
|
||||
bool get isMediumWindow =>
|
||||
MediaQuery.of(this).size.width < kMediumWindowWidth;
|
||||
|
||||
bool get isExpandedWindow =>
|
||||
MediaQuery.of(this).size.width < kExpandedWindowWidth;
|
||||
|
||||
bool get isLargeWindow => MediaQuery.of(this).size.width < kLargeWindowWidth;
|
||||
|
||||
bool get isExtraLargeWindow =>
|
||||
MediaQuery.of(this).size.width > kLargeWindowWidth;
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ class _RequestListState extends ConsumerState<RequestList> {
|
||||
radius: const Radius.circular(12),
|
||||
child: filterQuery.isEmpty
|
||||
? ReorderableListView.builder(
|
||||
padding: context.isMobile
|
||||
padding: context.isMediumWindow
|
||||
? EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
right: 8,
|
||||
@ -198,7 +198,7 @@ class _RequestListState extends ConsumerState<RequestList> {
|
||||
},
|
||||
)
|
||||
: ListView(
|
||||
padding: kIsMobile
|
||||
padding: context.isMediumWindow
|
||||
? EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
right: 8,
|
||||
|
@ -12,7 +12,7 @@ class RequestEditor extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return context.isMobile
|
||||
return context.isMediumWindow
|
||||
? const Padding(
|
||||
padding: kPb10,
|
||||
child: Column(
|
||||
|
@ -21,9 +21,9 @@ class EditorPaneRequestURLCard extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: !context.isMobile ? 20 : 6,
|
||||
horizontal: !context.isMediumWindow ? 20 : 6,
|
||||
),
|
||||
child: context.isMobile
|
||||
child: context.isMediumWindow
|
||||
? const Row(
|
||||
children: [
|
||||
DropdownButtonHTTPMethod(),
|
||||
|
@ -4,7 +4,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:inner_drawer/inner_drawer.dart';
|
||||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||
import '../../providers/providers.dart';
|
||||
import 'package:apidash/extensions/extensions.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'navbar.dart';
|
||||
import 'widgets/left_drawer.dart';
|
||||
import 'requests_page.dart';
|
||||
@ -44,7 +45,6 @@ class _MobileDashboardState extends ConsumerState<MobileDashboard> {
|
||||
) {
|
||||
final GlobalKey<InnerDrawerState> innerDrawerKey =
|
||||
ref.watch(mobileDrawerKeyProvider);
|
||||
final isLargeMobile = MediaQuery.sizeOf(context).width > kLargeMobileWidth;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: FlexColorScheme.themedSystemNavigationBar(
|
||||
context,
|
||||
@ -59,7 +59,7 @@ class _MobileDashboardState extends ConsumerState<MobileDashboard> {
|
||||
swipe: true,
|
||||
swipeChild: true,
|
||||
onTapClose: true,
|
||||
offset: isLargeMobile
|
||||
offset: !context.isCompactWindow
|
||||
? const IDOffset.only(left: 0.1, right: 1)
|
||||
: const IDOffset.only(left: 0.7, right: 1),
|
||||
boxShadow: [
|
||||
@ -72,8 +72,8 @@ class _MobileDashboardState extends ConsumerState<MobileDashboard> {
|
||||
colorTransitionChild: Colors.transparent,
|
||||
colorTransitionScaffold: Colors.transparent,
|
||||
rightAnimationType: InnerDrawerAnimation.linear,
|
||||
backgroundDecoration:
|
||||
BoxDecoration(color: Theme.of(context).colorScheme.surface),
|
||||
backgroundDecoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface),
|
||||
onDragUpdate: (value, direction) {
|
||||
drawerDirection.value = direction;
|
||||
if (value > 0.98 && direction == InnerDrawerDirection.start) {
|
||||
@ -105,6 +105,7 @@ class _MobileDashboardState extends ConsumerState<MobileDashboard> {
|
||||
borderRadius:
|
||||
const BorderRadius.only(topLeft: Radius.circular(8)),
|
||||
child: SafeArea(
|
||||
minimum: kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero,
|
||||
bottom: false,
|
||||
child: RequestsPage(
|
||||
innerDrawerKey: innerDrawerKey,
|
||||
@ -113,7 +114,7 @@ class _MobileDashboardState extends ConsumerState<MobileDashboard> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isLargeMobile)
|
||||
if (context.isCompactWindow)
|
||||
AnimatedPositioned(
|
||||
bottom: isLeftDrawerOpen
|
||||
? 0
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:apidash/providers/ui_providers.dart';
|
||||
import 'package:apidash/screens/mobile/widgets/page_base.dart';
|
||||
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';
|
||||
|
||||
@ -183,67 +184,76 @@ Widget customNavigationDestination(
|
||||
Function()? onTap,
|
||||
}) {
|
||||
bool isSelected = railIdx == buttonIdx;
|
||||
return Tooltip(
|
||||
message: label,
|
||||
triggerMode: TooltipTriggerMode.longPress,
|
||||
verticalOffset: 42,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: isSelected
|
||||
? null
|
||||
: () {
|
||||
if (!isNavigator) {
|
||||
ref.read(navRailIndexStateProvider.notifier).state = buttonIdx;
|
||||
}
|
||||
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
|
||||
: () {
|
||||
if (!isNavigator) {
|
||||
ref.read(navRailIndexStateProvider.notifier).state =
|
||||
buttonIdx;
|
||||
}
|
||||
onTap?.call();
|
||||
},
|
||||
child: Icon(
|
||||
isSelected ? selectedIcon : icon,
|
||||
return TooltipVisibility(
|
||||
visible: context.isCompactWindow,
|
||||
child: Tooltip(
|
||||
message: label,
|
||||
triggerMode: TooltipTriggerMode.longPress,
|
||||
verticalOffset: 42,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: isSelected
|
||||
? null
|
||||
: () {
|
||||
if (!isNavigator) {
|
||||
ref.read(navRailIndexStateProvider.notifier).state =
|
||||
buttonIdx;
|
||||
}
|
||||
onTap?.call();
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Ink(
|
||||
width: 65,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? Theme.of(context).colorScheme.onSecondaryContainer
|
||||
: Theme.of(context).colorScheme.onSurface.withOpacity(0.65),
|
||||
? Theme.of(context).colorScheme.secondaryContainer
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
onTap: isSelected
|
||||
? null
|
||||
: () {
|
||||
if (!isNavigator) {
|
||||
ref.read(navRailIndexStateProvider.notifier).state =
|
||||
buttonIdx;
|
||||
}
|
||||
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(),
|
||||
],
|
||||
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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import '../home_page/editor_pane/details_card/response_pane.dart';
|
||||
|
||||
class ResponseDrawer extends StatelessWidget {
|
||||
@ -6,27 +7,31 @@ class ResponseDrawer extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
|
||||
return Container(
|
||||
padding: kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_rounded),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
title: const Text("Response"),
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_rounded),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
),
|
||||
child: const ResponsePane(),
|
||||
),
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
title: const Text("Response"),
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
),
|
||||
child: const ResponsePane(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/extensions/extensions.dart';
|
||||
import 'package:apidash/screens/mobile/navbar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@ -8,9 +9,9 @@ class LeftDrawer extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isLargeMobile = MediaQuery.sizeOf(context).width > kLargeMobileWidth;
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: MediaQuery.paddingOf(context).top),
|
||||
padding: EdgeInsets.only(top: MediaQuery.paddingOf(context).top) +
|
||||
(kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero),
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
child: Drawer(
|
||||
backgroundColor: Colors.transparent,
|
||||
@ -20,7 +21,7 @@ class LeftDrawer extends StatelessWidget {
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: MediaQuery.paddingOf(context).left,
|
||||
bottom: isLargeMobile
|
||||
bottom: !context.isCompactWindow
|
||||
? MediaQuery.paddingOf(context).bottom
|
||||
: 70 + MediaQuery.paddingOf(context).bottom),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
@ -29,7 +30,7 @@ class LeftDrawer extends StatelessWidget {
|
||||
borderRadius:
|
||||
const BorderRadius.only(topRight: Radius.circular(8)),
|
||||
),
|
||||
child: isLargeMobile
|
||||
child: !context.isCompactWindow
|
||||
? Row(
|
||||
children: [
|
||||
const NavRail(),
|
||||
|
@ -1,26 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/widgets/window_caption.dart';
|
||||
|
||||
class PageBase extends StatelessWidget {
|
||||
class PageBase extends ConsumerWidget {
|
||||
final String title;
|
||||
final Widget scaffoldBody;
|
||||
const PageBase({super.key, required this.title, required this.scaffoldBody});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
primary: true,
|
||||
title: Text(title),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isDarkMode =
|
||||
ref.watch(settingsProvider.select((value) => value.isDark));
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
padding: kIsWindows || kIsMacOS ? kPt28 : EdgeInsets.zero,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
primary: true,
|
||||
title: Text(title),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
),
|
||||
child: scaffoldBody,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: scaffoldBody,
|
||||
),
|
||||
if (kIsWindows)
|
||||
SizedBox(
|
||||
height: 29,
|
||||
child: WindowCaption(
|
||||
backgroundColor: Colors.transparent,
|
||||
brightness: isDarkMode ? Brightness.dark : Brightness.light,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class SettingsPage extends ConsumerWidget {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
!context.isMobile
|
||||
!context.isMediumWindow
|
||||
? Padding(
|
||||
padding: kPh20t40,
|
||||
child: kIsDesktop
|
||||
|
@ -30,7 +30,7 @@ class DropdownButtonHttpMethod extends StatelessWidget {
|
||||
return DropdownMenuItem<HTTPVerb>(
|
||||
value: value,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: context.isMobile ? 8 : 16),
|
||||
padding: EdgeInsets.only(left: context.isMediumWindow ? 8 : 16),
|
||||
child: Text(
|
||||
value.name.toUpperCase(),
|
||||
style: kCodeStyle.copyWith(
|
||||
|
@ -38,7 +38,7 @@ class IntroMessage extends StatelessWidget {
|
||||
|
||||
return CustomMarkdown(
|
||||
data: text,
|
||||
padding: !context.isMobile ? kPh60 : kPh20,
|
||||
padding: !context.isMediumWindow ? kPh60 : kPh20,
|
||||
);
|
||||
}
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
|
@ -48,7 +48,7 @@ class _RequestPaneState extends State<RequestPane>
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
context.isMobile
|
||||
context.isMediumWindow
|
||||
? const SizedBox.shrink()
|
||||
: Padding(
|
||||
padding: kP8,
|
||||
|
@ -14,7 +14,7 @@ class TabLabel extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: context.isMobile ? kMobileTabHeight : kTabHeight,
|
||||
height: context.isMediumWindow ? kMobileTabHeight : kTabHeight,
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
|
Reference in New Issue
Block a user