# 通過組合現有Widget實現自定義Widget
在Flutter中頁面UI通常都是由一些低階別的Widget組合而成,當我們需要封裝一些通用Widget時,應該首先考慮是否可以通過組合其它Widget來實現,如果可以則應優先使用組合,因為直接通過現有Widget拼裝會非常方便、簡單、高效。
### 示例:自定義漸變按鈕
Flutter Widget庫中的按鈕默認不支持漸變背景,為了實現漸變背景按鈕,我們自定義一個GradientButton Widget。我們先來看看效果:

我們看看GradientButton實現:
```
import 'package:flutter/material.dart';
class GradientButton extends StatelessWidget {
GradientButton({
this.colors,
this.width,
this.height,
this.onTap,
@required this.child,
});
// 漸變色數組
final List<Color> colors;
// 按鈕寬高
final double width;
final double height;
final Widget child;
//點擊回調
final GestureTapCallback onTap;
@override
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
//確保colors數組不空
List<Color> _colors = colors ??
[theme.primaryColor, theme.primaryColorDark ?? theme.primaryColor];
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(colors: _colors),
),
child: Material(
type: MaterialType.transparency,
child: InkWell(
splashColor: colors.last,
highlightColor: Colors.transparent,
onTap: onTap,
child: ConstrainedBox(
constraints: BoxConstraints.tightFor(height: height, width: width),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: DefaultTextStyle(
style: TextStyle(fontWeight: FontWeight.bold),
child: child),
),
),
),
),
),
);
}
}
```
可以看到GradientButton是由Padding、Center、InkWell等Widget組合而成。當然上面的代碼只是一個示例,作為一個按鈕它還并不完整,比如沒有禁用狀態、不能定義圓角等,讀者可以根據實際需要來完善。
#### 使用GradientButton
```
class GradientButtonRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
GradientButton(
colors: [Colors.orange,Colors.red],
height: 50.0,
child: Text("Submit"),
onTap:onTap ,
),
GradientButton(
height: 50.0,
colors: [Colors.lightGreen, Colors.green[700]],
child: Text("Submit"),
onTap: onTap,
),
GradientButton(
height: 50.0,
colors: [Colors.lightBlue[300], Colors.blueAccent],
child: Text("Submit"),
onTap: onTap,
),
],
),
);
}
onTap(){
print("button click");
}
}
```
### 總結
通過組合的方式定義Widget和我們之前寫界面并無差異,不過在抽離出單獨的Widget時我們要考慮代碼規范性,如必要參數要用`@required` 標注,對于可選參數在特定場景需要判空或設置默認值等。這是由于使用者大多時候可能不了解Widget的內部細節,所以為了保證代碼健壯性,我們需要在用戶錯誤地使用Widget時能夠兼容或報錯提示(使用assert斷言函數)。
- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示