一键导入
form-widgets
Text fields, switches, pickers, date pickers, and form validation for iOS Design System
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Text fields, switches, pickers, date pickers, and form validation for iOS Design System
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Specialized widgets and themes for Music, Stocks, Weather, App Store, and Books apps
Button widgets in iOS Design System - CupertinoButtonWidget and ButtonWidget with all variants
Guide to creating new widgets following iOS Design System patterns and conventions
Icons, tags, titles, descriptions, dividers, and markdown rendering for iOS Design System
Using flutter_hooks for cleaner state management in iOS Design System widgets
ScaffoldWidget, GroupedTableWidget, RowWidget, NavigationBar, and ToolBar for iOS Design System
| name | form-widgets |
| description | Text fields, switches, pickers, date pickers, and form validation for iOS Design System |
iOS-style input controls with automatic theme adaptation and validation patterns.
iOS text input with automatic clear button.
// Basic text field
const CupertinoTextFieldWidget(
placeholder: 'Enter your name',
)
// With controller
final nameController = useTextEditingController(); // or TextEditingController()
CupertinoTextFieldWidget(
placeholder: 'Name',
controller: nameController,
onChanged: (value) {
print('Name changed: $value');
},
)
// Email field
const CupertinoTextFieldWidget(
placeholder: 'Email',
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
)
// Password field
const CupertinoTextFieldWidget(
placeholder: 'Password',
obscureText: true,
keyboardType: TextInputType.visiblePassword,
)
// Multiline
const CupertinoTextFieldWidget(
placeholder: 'Description',
maxLines: 5,
minLines: 3,
)
Default Styling:
EdgeInsets.only(left: 16, top: 11, bottom: 11)bodyRegularprimaryLight / primaryDarkElevatedSearch field with magnifying glass icon.
// Basic search
const CupertinoSearchTextFieldWidget()
// With controller
final searchController = useTextEditingController();
CupertinoSearchTextFieldWidget(
controller: searchController,
placeholder: 'Search contacts',
onChanged: (value) {
// Filter results
},
onSubmitted: (value) {
// Perform search
},
)
Default Styling:
defaultFillColors.tertiaryiOS toggle switch.
// Basic switch
SwitchWidget(
value: true,
onChanged: (value) {
print('Switch toggled: $value');
},
)
// In row
RowWidget.standard(
title: 'Notifications',
description: null,
leftWidget: null,
rightWidget: SwitchWidget(
value: _notificationsEnabled,
onChanged: (value) {
setState(() => _notificationsEnabled = value);
},
),
displayDivider: false,
onPressed: null,
onLongPress: null,
decorationCallback: null,
)
// Disabled
SwitchWidget(
value: true,
onChanged: null, // Disabled
)
// Custom color
SwitchWidget(
value: true,
onChanged: (value) {},
activeTrackColorCallback: (theme) => theme.defaultColors.systemBlue,
)
// Stocks variant (green when on)
SwitchWidget.stocks(
value: true,
onChanged: (value) {},
)
Default Colors:
systemGreenfillColors.secondarysystemWhiteButton displaying selected picker value.
// Date picker button
PickerButtonWidget.dateTime(
dateTime: DateTime(2020, 6, 20),
dateFormat: DateFormat.yMMMMd('en_US'), // "June 20, 2020"
onPressed: () {
// Show date picker
},
)
// Time picker button
PickerButtonWidget.dateTime(
dateTime: DateTime(2020, 6, 20, 13, 30),
dateFormat: DateFormat.jm(), // "1:30 PM"
onPressed: () {
// Show time picker
},
)
// In a row
RowWidget.standard(
title: 'Birthday',
description: null,
leftWidget: IconWidget.background(
iconData: CupertinoIcons.calendar,
),
rightWidget: PickerButtonWidget.dateTime(
dateTime: _selectedDate,
dateFormat: DateFormat.yMMMMd('en_US'),
onPressed: () async {
await CupertinoDatePickerWidget.show(
context: context,
mode: CupertinoDatePickerMode.date,
initialDateTime: _selectedDate,
onDateTimeChanged: (newDate) {
setState(() => _selectedDate = newDate);
},
);
},
),
displayDivider: false,
onPressed: null,
onLongPress: null,
decorationCallback: null,
)
Full-screen date/time picker with gradients.
// Date picker
await CupertinoDatePickerWidget.show(
context: context,
mode: CupertinoDatePickerMode.date,
initialDateTime: DateTime.now(),
minimumDate: DateTime(1900),
maximumDate: DateTime.now(),
onDateTimeChanged: (newDate) {
print('Selected date: $newDate');
},
)
// Time picker
await CupertinoDatePickerWidget.show(
context: context,
mode: CupertinoDatePickerMode.time,
initialDateTime: DateTime.now(),
use24hFormat: false,
minuteInterval: 15,
onDateTimeChanged: (newTime) {
print('Selected time: $newTime');
},
)
// Date and time
await CupertinoDatePickerWidget.show(
context: context,
mode: CupertinoDatePickerMode.dateAndTime,
initialDateTime: DateTime.now(),
minimumDate: DateTime.now(),
showDayOfWeek: true,
onDateTimeChanged: (newDateTime) {},
)
// With blur barrier
await CupertinoDatePickerWidget.show(
context: context,
mode: CupertinoDatePickerMode.date,
initialDateTime: DateTime.now(),
barrierFilter: true,
onDateTimeChanged: (newDate) {},
)
Modes:
time - Hours and minutesdate - Month, day, yeardateAndTime - All componentsmonthYear - Month and year onlyGeneric scrollable picker for custom options.
// Simple text picker
final options = ['Small', 'Medium', 'Large'];
await CupertinoPickerWidget.show(
context: context,
itemExtent: 40,
initialItem: 1,
children: options.map((option) {
return Center(
child: Text(
option,
style: theme.typography.bodyRegular,
),
);
}).toList(),
onSelectedItemChanged: (index) {
print('Selected: ${options[index]}');
},
)
// With magnifier effect
await CupertinoPickerWidget.show(
context: context,
itemExtent: 40,
useMagnifier: true,
magnification: 1.2,
children: List.generate(10, (i) {
return Center(child: Text('Option ${i + 1}'));
}),
onSelectedItemChanged: (index) {},
)
class ProfileForm extends HookWidget {
const ProfileForm({super.key});
@override
Widget build(BuildContext context) {
final theme = IosTheme.of(context);
final nameController = useTextEditingController();
final emailController = useTextEditingController();
final isValid = useState(false);
final isLoading = useState(false);
// Auto-validation
useEffect(() {
void validate() {
isValid.value = nameController.text.isNotEmpty &&
emailController.text.contains('@');
}
nameController.addListener(validate);
emailController.addListener(validate);
return () {
nameController.removeListener(validate);
emailController.removeListener(validate);
};
}, [nameController, emailController]);
return Column(
children: [
GroupedTableWidget(
rows: [
CupertinoTextFieldWidget(
placeholder: 'Name',
controller: nameController,
),
CupertinoTextFieldWidget(
placeholder: 'Email',
controller: emailController,
),
],
),
ButtonWidget.label(
size: const LargeButtonSize(),
color: const BlueButtonColor(),
label: 'Save',
displayCupertinoActivityIndicator: isLoading.value,
onPressed: !isValid.value || isLoading.value
? null
: () async {
isLoading.value = true;
await Future.delayed(Duration(seconds: 2));
isLoading.value = false;
},
),
],
);
}
}
class ValidatedFormScreen extends StatefulWidget {
@override
State<ValidatedFormScreen> createState() => _ValidatedFormScreenState();
}
class _ValidatedFormScreenState extends State<ValidatedFormScreen> {
final _emailController = TextEditingController();
String? _emailError;
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
bool _validate() {
setState(() {
if (!_emailController.text.contains('@')) {
_emailError = 'Please enter a valid email address';
} else {
_emailError = null;
}
});
return _emailError == null;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
GroupedTableWidget.rounded(
title: const TitleWidget(
text: 'EMAIL',
size: TitleSize.small,
),
rows: [
CupertinoTextFieldWidget(
placeholder: 'Email',
controller: _emailController,
keyboardType: TextInputType.emailAddress,
onChanged: (_) {
if (_emailError != null) _validate();
},
),
],
description: _emailError != null
? DescriptionWidget(text: _emailError!)
: null,
),
],
);
}
}
GroupedTableWidget.rounded(
title: const TitleWidget(
text: 'PREFERENCES',
size: TitleSize.small,
),
rows: [
RowWidget.standard(
title: 'Notifications',
description: 'Receive push notifications',
leftWidget: null,
rightWidget: SwitchWidget(
value: _notifications,
onChanged: (value) => setState(() => _notifications = value),
),
displayDivider: true,
onPressed: null,
onLongPress: null,
decorationCallback: null,
),
RowWidget.standard(
title: 'Reminder Time',
description: null,
leftWidget: null,
rightWidget: PickerButtonWidget.dateTime(
dateTime: _reminderTime,
dateFormat: DateFormat.jm(),
onPressed: () {
// Show time picker
},
),
displayDivider: false,
onPressed: null,
onLongPress: null,
decorationCallback: null,
),
],
description: const DescriptionWidget(
text: 'Notification settings can be changed anytime.',
),
)
/lib/src/widgets/cupertino_text_field_widget.dart/lib/src/widgets/cupertino_search_text_field_widget.dart/lib/src/widgets/switch_widget.dart/lib/src/widgets/picker_button_widget.dart/lib/src/widgets/cupertino_date_picker_widget.dart/lib/src/widgets/cupertino_picker_widget.dart