Flutter組件封裝

前言

在 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)支持管理組件內部狀態;適合動態內容代碼相對較多;不適合過多狀態屬性,可能降低性能有動態狀態需求的組件;需要根據交互更新界面的組件
高階組件封裝包含多個子組件,適合構建複雜組件;可以包含業務邏輯代碼層級增加,調試困難;渲染開銷高於基礎組件複雜組件;包含多子組件和多層結構的組件
帶有主題的封裝可以共享主題或狀態;支持全局的樣式配置組件複雜度提高;可能導致性能開銷;設置和調整較複雜需要在多個組件間共享主題、樣式或狀態的應用場景
參數化的抽象組件提高組件靈活性;用戶可自定義部分內容代碼增加;過度抽象可能導致調試困難用戶需自定義的部分可變內容;提高組件的重用性
帶有回調的封裝支持傳遞回調,適合多交互場景;支持狀態或數據回傳複雜度增高,維護相對較困難;回調過多可能難以管理需要與父組件交互;動態數據輸入/輸出場景