【Flutter 元件集錄】 DecoratedBox | 8 月更文挑戰

張風捷特烈發表於2021-08-27
前言:

這是我參與8月更文挑戰的第 27 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰,我準備在本月挑選 31 個以前沒有介紹過的元件,進行全面分析和屬性介紹。這些文章將來會作為 Flutter 元件集錄 的重要素材。希望可以堅持下去,你的支援將是我最大的動力~

本系列元件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility14. Padding15. AnimatedContainer
16.CircleAvatar17.PhysicalShape18.Divider
19.Flexible、Expanded 和 Spacer 20.Card21.SizedBox
22.ConstrainedBox23.Stack24.Positioned
25.OverflowBox26.SizedOverflowBox27. DecoratedBox

1.認識 DecoratedBox 元件

DecoratedBox 元件可能單獨使用的頻率不是很高,因為它被整合在了 Container 元件中,但裝飾的使用方式是共通的,原始碼中說 DecoratedBox 會在其孩子的前景或背景上繪製 Decoration 裝飾物件。這說明 Decoration 才是裝飾的重點,我們需要了解或自定義 Decoration


下面是 DecoratedBox 元件類的定義構造方法,可以看出它繼承自 SingleChildRenderObjectWidget 。構造時必須傳入尺寸 decoration 引數,可以傳入 position 入參。

decoration 成員的型別是 Decoration ,表示裝飾物件。position 成員的型別是 DecorationPosition 列舉,表示在前景繪製還是在背景繪製。

final Decoration decoration;
final DecorationPosition position;

enum DecorationPosition {
  background,
  foreground,
}
複製程式碼

2.認識 Decoration 及其子類

首先需要注意的是: Decoration 是一個抽象類,無法直接例項化使用。


Flutter 框架中提供了四個實現類,其中 BoxDecoration 是我們最常用的。

比如下面通過 BoxDecorationborderRadius 可以指定裝飾的圓角,通過 color 指定填充的顏色。

DecoratedBox(
  decoration: BoxDecoration(
      color: Colors.orangeAccent,
      borderRadius: BorderRadius.only(
          topLeft: Radius.circular(20),
          bottomRight: Radius.circular(20),
        bottomLeft: Radius.circular(5),
        topRight: Radius.circular(5),
      ),
  ),
  child: buildContent(),
);

Widget buildContent() {
  return SizedBox(
    width: 80,
    height: 80,
    child: Icon(Icons.android, size: 50, color: Colors.white),
  );
}
複製程式碼

下面是 BoxDecoration 的構造方法,可以看出除了圓角和顏色,還有很多入參,比如背景圖片邊線陰影漸變形狀等。

如下裝飾,有紅色史詩碎片那味了:

BoxDecoration(
    color: Colors.transparent,
    borderRadius: BorderRadius.only(
      topLeft: Radius.circular(20),
      bottomRight: Radius.circular(20),
      bottomLeft: Radius.circular(5),
      topRight: Radius.circular(5),
    ),
    border: Border.all(color: Colors.red, width: 2),
    image: DecorationImage(
      image: AssetImage('assets/images/bg_6.jpeg'),
      fit: BoxFit.cover,
    ),
    boxShadow: [
      const BoxShadow(
          color: Colors.red,
          offset: Offset.zero,
          blurRadius: 2,
          spreadRadius: 2),
    ]
),
複製程式碼

另外還有一個 ShapeDecoration 子類,顧名思義,它可以進行一些形狀的處理。其中入參必須傳入 ShapeBorder 型別的 shape 引數。關於 ShapeBorder ,詳見 : 《Path在手,天下我有》

如下,通過一個 Flutter 自帶的 CircleBorder 來測試一下:

DecoratedBox(
  position: DecorationPosition.background,
  decoration: ShapeDecoration(
    shape: CircleBorder(),
    shadows: const [BoxShadow(
        color: Colors.red,
        offset: Offset.zero,
        blurRadius: 2,
        spreadRadius: 2)],
    image: DecorationImage(
      image: AssetImage('assets/images/bg_6.jpeg'),
      fit: BoxFit.cover,
    ),
  ),
  child: buildContent(),
);
複製程式碼

3.自定義裝飾

很多人問過我,如何加虛線邊框。本質上就是在問如何通過繪製虛線,通過 DecoratedBox 裝飾而已。在我釋出的 dash_painter 包中有實現虛線裝飾,就以此來講述一下如何自定義裝飾

dependencies:
	...
	dash_painter: ^1.0.2
複製程式碼

import 'package:dash_painter/dash_decoration.dart';

DecoratedBox(
  decoration: DashDecoration(
      pointWidth: 2,
      step: 5,
      pointCount: 1,
      radius: Radius.circular(15),
      gradient: SweepGradient(colors: [
        Colors.blue,
        Colors.red,
        Colors.yellow,
        Colors.green
      ])),
  child: buildContent(),
);

Widget buildContent() {
  return SizedBox(
    width: 70,
    height: 70,
    child: Icon(
      Icons.add, color: Colors.orangeAccent, size: 40),
  );
}
複製程式碼

下面看一下 DashDecoration 的原始碼實現,首先定義可配置的引數,通過建構函式初始化。

DashDecoration 中必須實現抽象方法 createBoxPainter ,返回一個 BoxPainter 物件。

然後繼承 BoxPainter ,將配置物件傳進來,在 paint 裡畫就完事了。本身流程很簡單,關鍵在於如何繪製。在 《Flutter 繪製指南 - 妙筆生花》 小冊中系統地介紹了 Flutter 繪製相關的基礎知識,感興趣的可以看一看。


4. DecoratedBox 的原始碼實現

DecoratedBox 繼承自 SingleChildRenderObjectWidget ,內部維護 RenderDecoratedBox 渲染物件來實現裝飾功能。


核心程式碼是下面的 paint 方法,執行繪製。其中 super.paint(context, offset); 是繪製子元件,可見 background 是在繪製孩子前繪製,也就是作為背景,孩子在前面。而 foreground 會覆蓋在孩子前面,也就是前景。畫筆是通過 _decoration#createBoxPainter 建立的,也就是那個 BoxPainter 物件。

那本文到這裡就結束了,謝謝觀看,明天見~

相關文章