前言
在 Flutter 中,封裝組件的方式有多種,根據需求的複雜性和靈活性,可以選擇不同的方法。以下是一些常見的組件封裝寫法:
函式組件封裝
使用函式封裝組件適合簡單、靜態的 UI 組件,能提高代碼的重用性。
Widget customButton({required String label, required VoidCallback onPressed}) {
return ElevatedButton(
onPressed: onPressed,
child: Text(label),
);
}
使用方式:
customButton(label: "Click Me", onPressed: () => print("Button pressed"));
無狀態組件封裝 (StatelessWidget)
當組件不需要管理狀態時,可以使用 StatelessWidget 封裝。這適用於靜態 UI 組件,並可以接受參數來設置組件屬性。
class CustomButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
const CustomButton({Key? key, required this.label, required this.onPressed}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(label),
);
}
}
使用方式:
CustomButton(label: "Click Me", onPressed: () => print("Button pressed"));
有狀態組件封裝 (StatefulWidget)
如果組件需要管理內部狀態(例如:點擊按鈕後改變文本),可以使用 StatefulWidget 封裝。
class CounterButton extends StatefulWidget {
final String label;
const CounterButton({Key? key, required this.label}) : super(key: key);
@override
_CounterButtonState createState() => _CounterButtonState();
}
class _CounterButtonState extends State<CounterButton> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _incrementCounter,
child: Text('${widget.label} $_counter'),
);
}
}
使用方式:
CounterButton(label: "Count");
高階組件封裝
將組件封裝成一個包含組件組合的容器,適用於包含多個子組件的複雜組件,並且可以包含業務邏輯或狀態管理。
class CustomCard extends StatelessWidget {
final String title;
final String subtitle;
final VoidCallback onTap;
const CustomCard({Key? key, required this.title, required this.subtitle, required this.onTap}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Card(
child: ListTile(
title: Text(title),
subtitle: Text(subtitle),
),
),
);
}
}
使用方式:
CustomCard(
title: "Card Title",
subtitle: "Card Subtitle",
onTap: () => print("Card tapped"),
);
擴展帶有主題的封裝
使用 Theme 或 InheritedWidget 封裝,適合複雜的 UI 組件,或在多處共享組件的樣式和狀態。
class CustomAppTheme extends InheritedWidget {
final Color primaryColor;
final Widget child;
const CustomAppTheme({Key? key, required this.primaryColor, required this.child}) : super(key: key, child: child);
static CustomAppTheme? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CustomAppTheme>();
}
@override
bool updateShouldNotify(CustomAppTheme oldWidget) => primaryColor != oldWidget.primaryColor;
}
使用方式:
CustomAppTheme(
primaryColor: Colors.blue,
child: CustomButton(label: "Themed Button", onPressed: () {}),
)
封裝帶有參數的抽象組件
將組件封裝為具有更靈活的組件結構,讓調用者可自定義部分內容。
class CustomContainer extends StatelessWidget {
final Widget child;
final Color backgroundColor;
const CustomContainer({Key? key, required this.child, this.backgroundColor = Colors.white}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: backgroundColor,
padding: EdgeInsets.all(16.0),
child: child,
);
}
}
使用方式:
CustomContainer(
backgroundColor: Colors.amber,
child: Text("Custom Content"),
);
帶有回調的封裝
可以封裝需要回傳數據的組件,使用 Function 參數來傳遞數據或進行狀態改變。
class CustomInputField extends StatelessWidget {
final ValueChanged<String> onTextChanged;
const CustomInputField({Key? key, required this.onTextChanged}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextField(
onChanged: onTextChanged,
decoration: InputDecoration(
labelText: "Enter Text",
),
);
}
}
使用方式:
CustomInputField(
onTextChanged: (text) {
print("Input text: $text");
},
);
如何選擇封裝方式
可以根據組件的複雜度、狀態管理需求和靈活性需求,來確定具體的封裝方式。
封裝方式 | 優點 | 缺點 | 適用時機 |
---|---|---|---|
函式組件封裝 | 輕量、簡潔;易於重複使用;執行效率高 | 無法管理狀態;只能適用於靜態 UI 組件 | 簡單、靜態的組件;不需要狀態的輕量 UI 元素 |
無狀態組件 (StatelessWidget ) | 易於重複使用;支持更多配置選項 | 無法管理狀態 | 需要靜態 UI 並接受外部配置的組件;不涉及狀態管理的組件 |
有狀態組件 (StatefulWidget ) | 支持管理組件內部狀態;適合動態內容 | 代碼相對較多;不適合過多狀態屬性,可能降低性能 | 有動態狀態需求的組件;需要根據交互更新界面的組件 |
高階組件封裝 | 包含多個子組件,適合構建複雜組件;可以包含業務邏輯 | 代碼層級增加,調試困難;渲染開銷高於基礎組件 | 複雜組件;包含多子組件和多層結構的組件 |
帶有主題的封裝 | 可以共享主題或狀態;支持全局的樣式配置 | 組件複雜度提高;可能導致性能開銷;設置和調整較複雜 | 需要在多個組件間共享主題、樣式或狀態的應用場景 |
參數化的抽象組件 | 提高組件靈活性;用戶可自定義部分內容 | 代碼增加;過度抽象可能導致調試困難 | 用戶需自定義的部分可變內容;提高組件的重用性 |
帶有回調的封裝 | 支持傳遞回調,適合多交互場景;支持狀態或數據回傳 | 複雜度增高,維護相對較困難;回調過多可能難以管理 | 需要與父組件交互;動態數據輸入/輸出場景 |