Flutter 128: 圖解 ColorTween 顏色補間動畫 & ButtonBar 按鈕容器

    小菜在嘗試做主題顏色切換時,希望背景色有一個自然的過渡過程,於是瞭解到 ColorTween 顏色補間差值器,配合 AnimationController 實現兩種顏色間的自然過渡;小菜簡單嘗試一下;

ColorTween

源碼分析

    ColorTween 的源碼很簡單,繼承自 Tween 補間動畫,與 Tween 相同,只是 beginendColor 替代;其中若需要透明狀態,可以將 begin / end 設置爲 nullColors.transparent 再此代表黑色透明,會淡入淡出黑色;

class ColorTween extends Tween<Color?> {
  ColorTween({ Color? begin, Color? end }) : super(begin: begin, end: end);

  @override
  Color? lerp(double t) => Color.lerp(begin, end, t);
}

案例源碼

    小菜預先設置好需要主題顏色切換的 UI Widget,之後通過混入 TickerProviderStateMixin,在 initState() 初始化時設置好 AnimationController,將顏色傳遞給背景色;

AnimationController _controller;
Animation<Color> _colors;
Color _currentColor = Colors.black;

@override
void initState() {
  super.initState();
  _controller = AnimationController(duration: Duration(seconds: 3), vsync: this);
  _colors = ColorTween(begin: _currentColor, end: Colors.amber).animate(_controller);
}

_bodyWid() => Material(
    child: AnimatedBuilder(
        animation: _colors,
        builder: (BuildContext _, Widget childWidget) {
          return Scaffold(backgroundColor: _colors.value, body: _itemListWid());
        }));

    通過 AnimationController 控制淡入淡出時機;reset() 重置控制器,forward()beginend 顏色切換;reward()endbegin 顏色切換;repeat() 重複循環切換;

_changeColorWid() => Container(
    color: Colors.white,
    child: Column(children: [
      ListTile(title: Text('切換 ThemeColor:')),
      Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
        _itemColorWid(Colors.deepOrange), _itemColorWid(Colors.teal),
        _itemColorWid(Colors.blue), _itemColorWid(Colors.pink),
        _itemColorWid(Colors.indigoAccent)
      ])
    ]));

_itemColorWid(color) => GestureDetector(
    child: Container(width: 50.0, height: 50.0, color: color),
    onTap: () {
      _colors = ColorTween(begin: _currentColor, end: color).animate(_controller);
      setState(() {
        _controller.reset();
        _controller.forward();
      });
      _currentColor = color;
    });

ButtonBar

    小菜在很多場景中設置水平均分或右對齊,爲此小菜瞭解到一個新的容器方式,ButtonBar 默認水平方式放置子 Widget 當水平寬度無法完全放置所有子 Widget 時會豎直方向放置,小菜簡單學習一下;

源碼分析

const ButtonBar({
    Key key,
    this.alignment,         // 對齊方式
    this.mainAxisSize,      // 主軸上佔據空間範圍
    this.buttonTextTheme,   // 按鈕文本主題
    this.buttonMinWidth,    // 子按鈕最小寬度
    this.buttonHeight,      // 子按鈕最高度
    this.buttonPadding,     // 子按鈕內邊距
    this.buttonAlignedDropdown, // 下拉菜單是否與子按鈕對齊
    this.layoutBehavior,
    this.overflowDirection, // 子按鈕排列順序
    this.overflowButtonSpacing, // 子按鈕之間間距
    this.children = const <Widget>[],
})

    簡單分析源碼,ButtonBar 作爲一個無狀態的 StatelessWidgetRow 類似,作爲一個存放子 Widget 的容器,其中包括了類似於對齊方式等屬性方便應用;小菜簡單理解爲變形的 Row,實際是繼承自 Flex_ButtonBarRow

案例嘗試

構造方法

    ButtonBar 作爲一個 Widget 容器,用於水平存放各 Widget,若子 Widget 佔據空間範圍大於分配空間時,則豎直方向展示;

_buttonBarWid01() => ButtonBar(children: <Widget>[
      RaisedButton(child: Text('Button 01'), onPressed: null),
      RaisedButton(child: Text('Button 02'), onPressed: null) ]);
    
_buttonBarWid02() => ButtonBar(children: <Widget>[
      RaisedButton(child: Text('Button 01'), onPressed: null),
      RaisedButton(child: Text('Button 02'), onPressed: null),
      RaisedButton(child: Text('Button 03'), onPressed: null),
      RaisedButton(child: Text('Button 04'), onPressed: null) ]);
    
_buttonBarWid03() => ButtonBar(children: <Widget>[
      RaisedButton(child: Text('Button 01'), onPressed: null),
      RaisedButton(child: Text('Button 02'), onPressed: null),
      RaisedButton(child: Text('Button 03'), onPressed: null),
      RaisedButton(child: Text('Button 04'), onPressed: null),
      RaisedButton(child: Text('Button 05'), onPressed: null) ]);

1. alignment

    alignment 爲容器內子 Widget 的對齊方式,不設置或爲 null 時默認爲 end 方式對齊,此時與 ltr / rtl 相關;

_buttonBarWid01(index) {
  MainAxisAlignment alignment;
  if (index == 0) {
    alignment = MainAxisAlignment.start;
  } else if (index == 1) {
    alignment = MainAxisAlignment.center;
  } else if (index == 2) {
    alignment = MainAxisAlignment.spaceAround;
  } else if (index == 3) {
    alignment = MainAxisAlignment.spaceBetween;
  } else if (index == 4) {
    alignment = MainAxisAlignment.spaceEvenly;
  } else {
    alignment = MainAxisAlignment.end;
  }
  return ButtonBar(alignment: alignment, children: <Widget>[
    RaisedButton(child: Text('Button'), onPressed: null),
    RaisedButton(child: Text('${alignment.toString()}'), onPressed: null) ]);
}

2. mainAxisSize

    mainAxisSize 爲主軸上佔據空間範圍,與 Row / Column 一致,分爲 min / max 最小範圍和最大填充範圍兩種;

_buttonBarWid05(mainAxisSize) => Container(
    color: Colors.blue.withOpacity(0.3),
    child: ButtonBar(mainAxisSize: mainAxisSize, children: <Widget>[
      RaisedButton(child: Text('Button 01'), onPressed: null),
      RaisedButton(child: Text('Button 02'), onPressed: null),
      RaisedButton(child: Text('Button 03'), onPressed: null)
    ]));

3. buttonTextTheme

    buttonTextTheme 爲子 Widget 按鈕主題,主要包括 normal / accent / primary 三種主題樣式,分別對應 ThemeData.brightness / accentColor / primaryColor

_buttonBarWid04(theme) =>
    ButtonBar(buttonTextTheme: theme, children: <Widget>[
      RaisedButton(child: Text('Button 01'), onPressed: null),
      RaisedButton(child: Text('Button 02', style: TextStyle(color: Colors.blue)), onPressed: null),
      RaisedButton(child: Text('${theme.toString()}'), onPressed: null),
    ]);

4. buttonMinWidth & buttonHeight

    buttonMinWidth & buttonHeight 分別對應子 Widget 默認的最小按鈕寬度和按鈕高度;

_buttonBarWid06(width, height, alignment) =>
    ButtonBar(buttonMinWidth: width, buttonHeight: height, children: <Widget>[
      RaisedButton(child: Text('Button 01'), onPressed: null),
      RaisedButton(child: Text('Button 02', style: TextStyle(color: Colors.blue)), onPressed: null),
      RaisedButton(child: Text('${alignment.toString()}'), onPressed: null),
    ]);

5. overflowButtonSpacing & buttonPadding

    overflowButtonSpacing 對應子按鈕外間距,類似於 GridView 元素間間距;buttonPadding 對應子按鈕內邊距;

_buttonBarWid07(padding, spacing) => ButtonBar(
      overflowButtonSpacing: spacing,
      buttonPadding: EdgeInsets.all(padding),
      children: <Widget>[
        RaisedButton(child: Text('Button 01'), onPressed: null),
        RaisedButton(child: Text('Button 02', style: TextStyle(color: Colors.blue)), onPressed: null),
        RaisedButton(child: Text('Button 03'), onPressed: null)
      ]);

6. overflowDirection

    overflowDirection 爲若容器內子 Widget 所佔範圍超過最大限制範圍時,垂直排列順序,小菜理解爲順序和倒序兩種;

_buttonBarWid08(direction) =>
    ButtonBar(overflowDirection: direction, children: <Widget>[
      RaisedButton(child: Text('Button 01'), onPressed: null),
      RaisedButton(child: Text('Button 02', style: TextStyle(color: Colors.blue)), onPressed: null),
      RaisedButton(child: Text('Button 03'), onPressed: null),
      RaisedButton(child: Text('${direction.toString()}'), onPressed: null),
    ]);

    ColorTween 案例源碼 & ButtonBar 案例源碼


    ColorTweenButtonBar 的應用非常簡單,這次小菜在實際場景中進行嘗試學習,如有錯誤,請多多指導!

來源: 阿策小和尚

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章