bharat_erp/lib/shared/widgets/app_side_panel.dart

244 lines
6.2 KiB
Dart

import 'package:flutter/material.dart';
Future<T?> showSidePanel<T>(
BuildContext context,
Widget panel, {
double? width,
}) {
final screenWidth = MediaQuery.sizeOf(context).width;
final defaultWidth = screenWidth > 1200
? 480.0
: (screenWidth * 0.38).clamp(360.0, 480.0);
final panelWidth = (width ?? defaultWidth).clamp(360.0, screenWidth * 0.95);
return showGeneralDialog<T>(
context: context,
useRootNavigator: true,
barrierDismissible: true,
barrierLabel: 'Dismiss',
barrierColor: Colors.black.withValues(alpha: 0.35),
transitionDuration: const Duration(milliseconds: 280),
pageBuilder: (context, _, __) {
final theme = Theme.of(context);
return Align(
alignment: Alignment.centerRight,
child: Material(
elevation: 16,
color: theme.colorScheme.surface,
borderRadius: const BorderRadius.horizontal(left: Radius.circular(16)),
clipBehavior: Clip.antiAlias,
child: SizedBox(
width: panelWidth,
height: MediaQuery.sizeOf(context).height,
child: panel,
),
),
);
},
transitionBuilder: (context, anim, _, child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOutCubic)),
child: child,
);
},
);
}
class SidePanelScaffold extends StatelessWidget {
const SidePanelScaffold({
super.key,
required this.title,
required this.child,
this.footer,
this.onClose,
});
final String title;
final Widget child;
final Widget? footer;
final VoidCallback? onClose;
void _close(BuildContext context) {
if (onClose != null) {
onClose!();
return;
}
Navigator.of(context, rootNavigator: true).pop();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 12, 16),
child: Row(
children: [
Expanded(
child: Text(
title,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w700,
),
),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => _close(context),
),
],
),
),
const Divider(height: 1),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: child,
),
),
if (footer != null) ...[
const Divider(height: 1),
Padding(padding: const EdgeInsets.all(24), child: footer),
],
],
);
}
}
class SidePanelSection extends StatelessWidget {
const SidePanelSection({
super.key,
required this.title,
required this.children,
});
final String title;
final List<Widget> children;
@override
Widget build(BuildContext context) {
final dividerColor =
Theme.of(context).colorScheme.outline.withValues(alpha: 0.2);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
fontWeight: FontWeight.w700,
letterSpacing: 0.8,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 8),
Divider(height: 1, color: dividerColor),
const SizedBox(height: 16),
...children,
const SizedBox(height: 24),
],
);
}
}
class SidePanelFormRow extends StatelessWidget {
const SidePanelFormRow({
super.key,
required this.left,
required this.right,
this.spacing = 16,
});
final Widget left;
final Widget right;
final double spacing;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final stack = constraints.maxWidth < 420;
if (stack) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
left,
const SizedBox(height: 12),
right,
],
),
);
}
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: left),
SizedBox(width: spacing),
Expanded(child: right),
],
),
);
},
);
}
}
/// Responsive row with up to three equal-width form fields.
class FormRowThree extends StatelessWidget {
const FormRowThree({
super.key,
required this.children,
this.spacing = 12,
this.stackBelowWidth = 768,
});
final List<Widget> children;
final double spacing;
final double stackBelowWidth;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < stackBelowWidth) {
return Padding(
padding: EdgeInsets.only(bottom: spacing),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
for (var i = 0; i < children.length; i++) ...[
if (i > 0) SizedBox(height: spacing),
children[i],
],
],
),
);
}
return Padding(
padding: EdgeInsets.only(bottom: spacing),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (var i = 0; i < children.length; i++) ...[
if (i > 0) SizedBox(width: spacing),
Expanded(child: children[i]),
],
],
),
);
},
);
}
}