feat: add base widgets with PageStandards (Button, TextField, Card, ListTile)
This commit is contained in:
105
lib/src/widgets/base/standard_button.dart
Normal file
105
lib/src/widgets/base/standard_button.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mom_kitchen/src/standards/page_standards.dart';
|
||||
|
||||
enum StandardButtonType { primary, secondary, outline, text }
|
||||
|
||||
class StandardButton extends StatelessWidget {
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
final StandardButtonType type;
|
||||
final bool isLoading;
|
||||
final IconData? icon;
|
||||
final bool isFullWidth;
|
||||
final double? width;
|
||||
final double? height;
|
||||
|
||||
const StandardButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
this.type = StandardButtonType.primary,
|
||||
this.isLoading = false,
|
||||
this.icon,
|
||||
this.isFullWidth = false,
|
||||
this.width,
|
||||
this.height,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final standards = PageStandards.of(context);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: isLoading ? null : onPressed,
|
||||
child: Container(
|
||||
width: isFullWidth ? double.infinity : width,
|
||||
height: height ?? standards.scaledHeight(48),
|
||||
padding: standards.scaledPadding(
|
||||
EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
),
|
||||
decoration: _buildDecoration(standards),
|
||||
child: _buildChild(standards),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
BoxDecoration _buildDecoration(PageStandards standards) {
|
||||
switch (type) {
|
||||
case StandardButtonType.primary:
|
||||
return BoxDecoration(
|
||||
color: standards.primaryColor,
|
||||
borderRadius: BorderRadius.circular(standards.scaledRadius(12)),
|
||||
);
|
||||
case StandardButtonType.secondary:
|
||||
return BoxDecoration(
|
||||
color: standards.secondaryColor,
|
||||
borderRadius: BorderRadius.circular(standards.scaledRadius(12)),
|
||||
);
|
||||
case StandardButtonType.outline:
|
||||
return BoxDecoration(
|
||||
border: Border.all(color: standards.primaryColor, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(standards.scaledRadius(12)),
|
||||
);
|
||||
case StandardButtonType.text:
|
||||
return const BoxDecoration();
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildChild(PageStandards standards) {
|
||||
if (isLoading) {
|
||||
return Center(
|
||||
child: CupertinoActivityIndicator(color: _getTextColor(standards)),
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisSize: isFullWidth ? MainAxisSize.max : MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (icon != null) ...[
|
||||
Icon(icon, color: _getTextColor(standards), size: standards.fontSize + 4),
|
||||
SizedBox(width: standards.scaledWidth(8)),
|
||||
],
|
||||
Text(
|
||||
text,
|
||||
style: standards.textStyle.copyWith(
|
||||
color: _getTextColor(standards),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Color _getTextColor(PageStandards standards) {
|
||||
switch (type) {
|
||||
case StandardButtonType.primary:
|
||||
case StandardButtonType.secondary:
|
||||
return CupertinoColors.white;
|
||||
case StandardButtonType.outline:
|
||||
case StandardButtonType.text:
|
||||
return standards.primaryColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
lib/src/widgets/base/standard_card.dart
Normal file
62
lib/src/widgets/base/standard_card.dart
Normal file
@@ -0,0 +1,62 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mom_kitchen/src/standards/page_standards.dart';
|
||||
|
||||
class StandardCard extends StatelessWidget {
|
||||
final Widget child;
|
||||
final VoidCallback? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final EdgeInsetsGeometry? margin;
|
||||
final Color? backgroundColor;
|
||||
final double? borderRadius;
|
||||
final bool showShadow;
|
||||
final bool showBorder;
|
||||
final Color? borderColor;
|
||||
|
||||
const StandardCard({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.onTap,
|
||||
this.padding,
|
||||
this.margin,
|
||||
this.backgroundColor,
|
||||
this.borderRadius,
|
||||
this.showShadow = true,
|
||||
this.showBorder = false,
|
||||
this.borderColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final standards = PageStandards.of(context);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
margin: margin ?? standards.scaledPadding(EdgeInsets.all(8)),
|
||||
padding: padding ?? standards.scaledPadding(EdgeInsets.all(16)),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor ?? standards.backgroundColor,
|
||||
borderRadius: BorderRadius.circular(
|
||||
borderRadius ?? standards.scaledRadius(16),
|
||||
),
|
||||
boxShadow: showShadow
|
||||
? [
|
||||
BoxShadow(
|
||||
color: standards.textColor.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
border: showBorder
|
||||
? Border.all(
|
||||
color: borderColor ?? standards.textColor.withOpacity(0.1),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
93
lib/src/widgets/base/standard_list_tile.dart
Normal file
93
lib/src/widgets/base/standard_list_tile.dart
Normal file
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mom_kitchen/src/standards/page_standards.dart';
|
||||
|
||||
class StandardListTile extends StatelessWidget {
|
||||
final IconData? leadingIcon;
|
||||
final Widget? leading;
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final Widget? trailing;
|
||||
final VoidCallback? onTap;
|
||||
final bool showChevron;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
const StandardListTile({
|
||||
super.key,
|
||||
this.leadingIcon,
|
||||
this.leading,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.trailing,
|
||||
this.onTap,
|
||||
this.showChevron = false,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final standards = PageStandards.of(context);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
padding: padding ??
|
||||
standards.scaledPadding(
|
||||
EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
if (leading != null) leading!,
|
||||
if (leadingIcon != null)
|
||||
Container(
|
||||
width: standards.scaledWidth(40),
|
||||
height: standards.scaledHeight(40),
|
||||
decoration: BoxDecoration(
|
||||
color: standards.primaryColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(standards.scaledRadius(10)),
|
||||
),
|
||||
child: Icon(
|
||||
leadingIcon,
|
||||
color: standards.primaryColor,
|
||||
size: standards.fontSize + 8,
|
||||
),
|
||||
),
|
||||
if (leadingIcon != null || leading != null)
|
||||
SizedBox(width: standards.scaledWidth(12)),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: standards.textStyle.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ...[
|
||||
SizedBox(height: standards.scaledHeight(2)),
|
||||
Text(
|
||||
subtitle!,
|
||||
style: standards.textStyle.copyWith(
|
||||
color: standards.textColor.withOpacity(0.6),
|
||||
fontSize: standards.fontSize - 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (trailing != null) trailing!,
|
||||
if (showChevron)
|
||||
Icon(
|
||||
CupertinoIcons.chevron_right,
|
||||
color: standards.textColor.withOpacity(0.4),
|
||||
size: standards.fontSize + 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
129
lib/src/widgets/base/standard_text_field.dart
Normal file
129
lib/src/widgets/base/standard_text_field.dart
Normal file
@@ -0,0 +1,129 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mom_kitchen/src/standards/page_standards.dart';
|
||||
|
||||
class StandardTextField extends StatelessWidget {
|
||||
final String? placeholder;
|
||||
final String? initialValue;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final VoidCallback? onEditingComplete;
|
||||
final ValueChanged<String>? onSubmitted;
|
||||
final bool obscureText;
|
||||
final TextInputType? keyboardType;
|
||||
final TextEditingController? controller;
|
||||
final FocusNode? focusNode;
|
||||
final IconData? prefixIcon;
|
||||
final IconData? suffixIcon;
|
||||
final VoidCallback? onSuffixIconPressed;
|
||||
final String? errorText;
|
||||
final int? maxLines;
|
||||
final bool enabled;
|
||||
final bool autofocus;
|
||||
|
||||
const StandardTextField({
|
||||
super.key,
|
||||
this.placeholder,
|
||||
this.initialValue,
|
||||
this.onChanged,
|
||||
this.onEditingComplete,
|
||||
this.onSubmitted,
|
||||
this.obscureText = false,
|
||||
this.keyboardType,
|
||||
this.controller,
|
||||
this.focusNode,
|
||||
this.prefixIcon,
|
||||
this.suffixIcon,
|
||||
this.onSuffixIconPressed,
|
||||
this.errorText,
|
||||
this.maxLines = 1,
|
||||
this.enabled = true,
|
||||
this.autofocus = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final standards = PageStandards.of(context);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: standards.backgroundColor,
|
||||
borderRadius: BorderRadius.circular(standards.scaledRadius(12)),
|
||||
border: Border.all(
|
||||
color: errorText != null
|
||||
? standards.secondaryColor
|
||||
: standards.textColor.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
if (prefixIcon != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: standards.scaledWidth(12)),
|
||||
child: Icon(
|
||||
prefixIcon,
|
||||
color: standards.textColor.withOpacity(0.5),
|
||||
size: standards.fontSize + 4,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CupertinoTextField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
placeholder: placeholder,
|
||||
placeholderStyle: TextStyle(
|
||||
color: standards.textColor.withOpacity(0.5),
|
||||
fontSize: standards.fontSize,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: standards.textColor,
|
||||
fontSize: standards.fontSize,
|
||||
),
|
||||
onChanged: onChanged,
|
||||
onEditingComplete: onEditingComplete,
|
||||
onSubmitted: onSubmitted,
|
||||
obscureText: obscureText,
|
||||
keyboardType: keyboardType,
|
||||
maxLines: maxLines,
|
||||
enabled: enabled,
|
||||
autofocus: autofocus,
|
||||
decoration: const BoxDecoration(),
|
||||
padding: standards.scaledPadding(
|
||||
EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (suffixIcon != null)
|
||||
GestureDetector(
|
||||
onTap: onSuffixIconPressed,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(right: standards.scaledWidth(12)),
|
||||
child: Icon(
|
||||
suffixIcon,
|
||||
color: standards.textColor.withOpacity(0.5),
|
||||
size: standards.fontSize + 4,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (errorText != null) ...[
|
||||
SizedBox(height: standards.scaledHeight(4)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: standards.scaledWidth(12)),
|
||||
child: Text(
|
||||
errorText!,
|
||||
style: TextStyle(
|
||||
color: standards.secondaryColor,
|
||||
fontSize: standards.fontSize - 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user