feat: add interactive widgets (Dialog, BottomSheet, Picker) with PageStandards
This commit is contained in:
76
lib/src/widgets/interactive/standard_bottom_sheet.dart
Normal file
76
lib/src/widgets/interactive/standard_bottom_sheet.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mom_kitchen/src/standards/page_standards.dart';
|
||||
|
||||
class StandardBottomSheet extends StatelessWidget {
|
||||
final String? title;
|
||||
final Widget child;
|
||||
final double? height;
|
||||
|
||||
const StandardBottomSheet({
|
||||
super.key,
|
||||
this.title,
|
||||
required this.child,
|
||||
this.height,
|
||||
});
|
||||
|
||||
static Future<T?> show<T>({
|
||||
required BuildContext context,
|
||||
String? title,
|
||||
required Widget child,
|
||||
double? height,
|
||||
bool isScrollControlled = false,
|
||||
}) {
|
||||
return showModalBottomSheet<T>(
|
||||
context: context,
|
||||
isScrollControlled: isScrollControlled,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => StandardBottomSheet(
|
||||
title: title,
|
||||
child: child,
|
||||
height: height,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final standards = PageStandards.of(context);
|
||||
|
||||
return Container(
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
color: standards.backgroundColor,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(standards.scaledRadius(20)),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: standards.scaledHeight(8)),
|
||||
width: standards.scaledWidth(36),
|
||||
height: standards.scaledHeight(4),
|
||||
decoration: BoxDecoration(
|
||||
color: standards.textColor.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(standards.scaledRadius(2)),
|
||||
),
|
||||
),
|
||||
if (title != null)
|
||||
Padding(
|
||||
padding: standards.scaledPadding(EdgeInsets.all(16)),
|
||||
child: Text(
|
||||
title!,
|
||||
style: standards.textStyle.copyWith(
|
||||
fontSize: standards.fontSize + 4,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(child: child),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
78
lib/src/widgets/interactive/standard_dialog.dart
Normal file
78
lib/src/widgets/interactive/standard_dialog.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mom_kitchen/src/standards/page_standards.dart';
|
||||
|
||||
class StandardDialog extends StatelessWidget {
|
||||
final String title;
|
||||
final String? message;
|
||||
final String? confirmText;
|
||||
final String? cancelText;
|
||||
final VoidCallback? onConfirm;
|
||||
final VoidCallback? onCancel;
|
||||
final bool isDestructive;
|
||||
|
||||
const StandardDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.message,
|
||||
this.confirmText,
|
||||
this.cancelText,
|
||||
this.onConfirm,
|
||||
this.onCancel,
|
||||
this.isDestructive = false,
|
||||
});
|
||||
|
||||
static Future<bool?> show(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
String? message,
|
||||
String? confirmText,
|
||||
String? cancelText,
|
||||
bool isDestructive = false,
|
||||
}) {
|
||||
return showCupertinoDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => StandardDialog(
|
||||
title: title,
|
||||
message: message,
|
||||
confirmText: confirmText,
|
||||
cancelText: cancelText,
|
||||
isDestructive: isDestructive,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final standards = PageStandards.of(context);
|
||||
final l10n = standards.l10n;
|
||||
|
||||
return CupertinoAlertDialog(
|
||||
title: Text(title, style: standards.textStyle),
|
||||
content: message != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(message!, style: standards.textStyle),
|
||||
)
|
||||
: null,
|
||||
actions: [
|
||||
if (cancelText != null || onCancel != null)
|
||||
CupertinoDialogAction(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
onCancel?.call();
|
||||
},
|
||||
child: Text(cancelText ?? l10n.cancel),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
isDestructiveAction: isDestructive,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
onConfirm?.call();
|
||||
},
|
||||
child: Text(confirmText ?? l10n.confirm),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
115
lib/src/widgets/interactive/standard_picker.dart
Normal file
115
lib/src/widgets/interactive/standard_picker.dart
Normal file
@@ -0,0 +1,115 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mom_kitchen/src/standards/page_standards.dart';
|
||||
|
||||
class StandardPickerItem<T> {
|
||||
final T value;
|
||||
final String label;
|
||||
|
||||
StandardPickerItem({
|
||||
required this.value,
|
||||
required this.label,
|
||||
});
|
||||
}
|
||||
|
||||
class StandardPicker<T> extends StatelessWidget {
|
||||
final List<StandardPickerItem<T>> items;
|
||||
final T? selectedValue;
|
||||
final ValueChanged<T?>? onChanged;
|
||||
final String? title;
|
||||
|
||||
const StandardPicker({
|
||||
super.key,
|
||||
required this.items,
|
||||
this.selectedValue,
|
||||
this.onChanged,
|
||||
this.title,
|
||||
});
|
||||
|
||||
static Future<T?> show<T>({
|
||||
required BuildContext context,
|
||||
required List<StandardPickerItem<T>> items,
|
||||
T? selectedValue,
|
||||
String? title,
|
||||
}) async {
|
||||
final standards = PageStandards.of(context);
|
||||
|
||||
return await showCupertinoModalPopup<T>(
|
||||
context: context,
|
||||
builder: (context) => Container(
|
||||
height: standards.scaledHeight(300),
|
||||
decoration: BoxDecoration(
|
||||
color: standards.backgroundColor,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(standards.scaledRadius(20)),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
if (title != null)
|
||||
Container(
|
||||
padding: standards.scaledPadding(EdgeInsets.all(16)),
|
||||
child: Text(
|
||||
title!,
|
||||
style: standards.textStyle.copyWith(
|
||||
fontSize: standards.fontSize + 4,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CupertinoPicker(
|
||||
itemExtent: standards.scaledHeight(40),
|
||||
scrollController: FixedExtentScrollController(
|
||||
initialItem: selectedValue != null
|
||||
? items.indexWhere((e) => e.value == selectedValue)
|
||||
: 0,
|
||||
),
|
||||
onSelectedItemChanged: (index) {
|
||||
Navigator.pop(context, items[index].value);
|
||||
},
|
||||
children: items
|
||||
.map((item) => Center(
|
||||
child: Text(
|
||||
item.label,
|
||||
style: standards.textStyle,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final standards = PageStandards.of(context);
|
||||
|
||||
return Container(
|
||||
height: standards.scaledHeight(200),
|
||||
color: standards.backgroundColor,
|
||||
child: CupertinoPicker(
|
||||
itemExtent: standards.scaledHeight(40),
|
||||
scrollController: FixedExtentScrollController(
|
||||
initialItem: selectedValue != null
|
||||
? items.indexWhere((e) => e.value == selectedValue)
|
||||
: 0,
|
||||
),
|
||||
onSelectedItemChanged: (index) {
|
||||
onChanged?.call(items[index].value);
|
||||
},
|
||||
children: items
|
||||
.map((item) => Center(
|
||||
child: Text(
|
||||
item.label,
|
||||
style: standards.textStyle,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user