组件生成
使用 flu-cli add widget 和 flu-cli add component 命令生成可复用的 UI 组件。
Widget vs Component
Widget
简单的、单一职责的 UI 组件:
- 按钮、输入框、卡片等
- 无复杂业务逻辑
- 高度可复用
Component
复合组件,包含多个 Widget:
- 用户卡片、产品列表等
- 可能包含业务逻辑
- 特定场景使用
添加 Widget
基本用法
bash
flu-cli add widget <name> [options]
flu-cli a widget <name> [options]参数
| 参数 | 说明 |
|---|---|
--stateful | 创建 StatefulWidget |
-f, --feature | 所属功能模块 |
示例
bash
# 创建 StatelessWidget
flu-cli a widget custom_button
# 创建 StatefulWidget
flu-cli a widget animated_card --stateful
# 在功能模块中创建
flu-cli a widget user_avatar -f user生成的代码
StatelessWidget:
dart
import 'package:flutter/material.dart';
class CustomButtonWidget extends StatelessWidget {
const CustomButtonWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
child: const Text('Custom Button'),
);
}
}StatefulWidget:
dart
import 'package:flutter/material.dart';
class AnimatedCardWidget extends StatefulWidget {
const AnimatedCardWidget({super.key});
@override
State<AnimatedCardWidget> createState() => _AnimatedCardWidgetState();
}
class _AnimatedCardWidgetState extends State<AnimatedCardWidget> {
@override
Widget build(BuildContext context) {
return Container(
child: const Text('Animated Card'),
);
}
}添加 Component
基本用法
bash
flu-cli add component <name> [options]
flu-cli a component <name> [options]示例
bash
# 创建 Component
flu-cli a component user_card
# 在功能模块中创建
flu-cli a component product_list -f shop生成的代码
dart
import 'package:flutter/material.dart';
class UserCardComponent extends StatelessWidget {
const UserCardComponent({super.key});
@override
Widget build(BuildContext context) {
return Card(
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text('User Card'),
),
);
}
}生成位置
Lite 模板
lib/
├── widgets/
│ └── custom_button_widget.dart
└── components/
└── user_card_component.dartModular/Clean 模板
lib/features/user/
├── widgets/
│ └── user_avatar_widget.dart
└── components/
└── user_card_component.dart使用场景
Widget 适用场景
通用 UI 组件
bashflu-cli a widget custom_button flu-cli a widget loading_indicator flu-cli a widget empty_state表单组件
bashflu-cli a widget text_input flu-cli a widget date_picker flu-cli a widget dropdown动画组件
bashflu-cli a widget fade_in --stateful flu-cli a widget slide_transition --stateful
Component 适用场景
业务组件
bashflu-cli a component user_card -f user flu-cli a component product_item -f shop列表项
bashflu-cli a component todo_item -f todo flu-cli a component message_bubble -f chat复合表单
bashflu-cli a component address_form -f user flu-cli a component payment_form -f order
最佳实践
1. 命名规范
bash
# Widget - 描述性名称
custom_button
loading_indicator
user_avatar
# Component - 业务相关名称
user_card
product_list
order_summary2. 参数传递
为组件添加参数:
dart
class CustomButtonWidget extends StatelessWidget {
final String text;
final VoidCallback? onPressed;
final Color? color;
const CustomButtonWidget({
super.key,
required this.text,
this.onPressed,
this.color,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: color,
),
child: Text(text),
);
}
}3. 组件复用
将通用组件放在 shared/widgets/:
bash
# 通用组件
lib/shared/widgets/
├── custom_button_widget.dart
├── loading_indicator_widget.dart
└── empty_state_widget.dart
# 模块专用组件
lib/features/user/widgets/
└── user_avatar_widget.dart4. 样式统一
使用主题和常量:
dart
class CustomButtonWidget extends StatelessWidget {
const CustomButtonWidget({super.key});
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
child: const Text('Button'),
);
}
}完整示例
创建用户卡片组件
bash
flu-cli a component user_card -f user编辑生成的文件:
dart
import 'package:flutter/material.dart';
import '../models/user_model.dart';
class UserCardComponent extends StatelessWidget {
final User user;
final VoidCallback? onTap;
const UserCardComponent({
super.key,
required this.user,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(8.0),
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
CircleAvatar(
radius: 30,
child: Text(user.name[0]),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
user.name,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
Text(
user.email,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
const Icon(Icons.chevron_right),
],
),
),
),
);
}
}