想實現一個帶文字 IOS 風格的 Switcher,Flutter 自己有一個 IOS 風格的 CupertinoSwitch 控件,可是添加不了文字,想繼承 CupertinoSwitch 重寫,但又無從下手,有思路的求安利我。還好控件邏輯不是很複雜。我本身手寫了一個,效果以下:web
效果圖
實現思路
先畫控件
畫一個背景控件(Container), 一個圓形滑動控件(由於我看有陰影,因此我用 Card),一個 Text 展現文字的控件,還有裝這些控件的 Stack 控件。微信
加入動畫控制控件滑動
點擊控件,啓用動畫,還有額外一些點擊回調。ide
控件被我稍微封裝了一下,代碼很差拆開說,其實邏輯也很簡單。感興趣的直接複製代碼運行,貼一下控件代碼:學習
用法代碼
HclSwitcher(
height: 60,
width: 120,
label: "ZZZ",
activeColor: Colors.orange,
dissColor: Colors.grey,
isOpen: true,
onChange: (flag) {},
)
HclSwitcher 控件代碼
import 'package:flutter/material.dart';
//暫時還未添加白板縮放動畫,仿IOS的Switcher控件
class HclSwitcher extends StatefulWidget {
//傳入的高
final double height;
//傳入的寬
final double width;
//開着的顏色
final Color activeColor;
//關閉的顏色
final Color dissColor;
//顯示的文字
final String label;
//開關標識
final bool isOpen;
//原生IOS控件關閉時候有一個白色背景板
final isShowWhiteBg;
//回調
final ValueChanged<bool> onChange;
//構造方法,我這裏規定傳入寬必須比高大,目的就是畫圓的時候用的是最小的寬或 //高的值(此處最小的值就是高的值)
HclSwitcher({
Key key,
this.height,
this.width,
this.label,
this.activeColor,
this.dissColor,
this.isOpen,
this.onChange,
this.isShowWhiteBg = false,
}) : super(key: key) {
if (this.height >= this.width) throw "寬必須必高大";
}
@override
_HclSwitcherState createState() => _HclSwitcherState();
}
class _HclSwitcherState extends State<HclSwitcher>
with TickerProviderStateMixin {
//控制開關
bool _isOpen;
//動畫控制器,動畫原理就是經過animation值的變化, //控制Positioned的左右邊距
AnimationController controller;
//動畫值
Animation<double> animation;
bool isInit = true; //第一次初始化不用調用動畫
@override
void initState() {
// TODO: implement initState
super.initState();
_isOpen = widget.isOpen;
controller = new AnimationController(
vsync: this, duration: Duration(milliseconds: 200));
animation = Tween(begin: 0.0, end: 1.0)
.animate(CurvedAnimation(parent: controller, curve: Curves.linear))
..addListener(() {
setState(() {
print("value${animation.value}");
});
})
..addStatusListener((status) {
print(status);
});
print("init${animation.value}");
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
controller.reset();
controller.forward();
isInit = false;
setState(() {
_isOpen = !_isOpen;
if (null != widget.onChange) widget.onChange(_isOpen);
});
},
child: Container(
//大背景控件
height: widget.height,
width: widget.width,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(widget.height / 2)),
color: _isOpen ? widget.activeColor : widget.dissColor,
),
child: Stack(
children: <Widget>[
_isOpen
? Container()
: widget.isShowWhiteBg
? Positioned(
top: 2,
left: 2,
right: 2,
child: Container(
alignment: Alignment.center,
height: widget.height - 4,
width: widget.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(widget.height / 2)),
color: Colors.white,
)),
)
: Container(),
Container(
margin: EdgeInsets.only(left: 12),
alignment: Alignment.centerLeft,
child: _isOpen
? Text(
widget.label,
style: TextStyle(fontSize: 18, color: Colors.white),
)
: Container(),
),
isInit
? Positioned(
right: _isOpen ? 0 : widget.width - widget.height,
child: Container(
height: widget.height,
width: widget.height,
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(widget.height / 2))),
),
))
: Positioned(
left: !_isOpen
? null
: (widget.width - widget.height) * animation.value,
right: _isOpen
? null
: (widget.width - widget.height) * animation.value,
child: Container(
height: widget.height,
width: widget.height,
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(widget.height / 2))),
),
))
],
),
),
);
}
@override
void deactivate() {
// TODO: implement deactivate
super.deactivate();
controller?.stop();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
controller?.dispose();
}
}
本文分享自微信公衆號 - Flutter學習簿(gh_d739155d3b2c)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。動畫