如何在Ffltter

中建立像這樣的自定義單選按鈕組
flutter dart flutter-layout
推薦答案
這是完整的代碼
複制代碼
class CustomRadio extends StatefulWidget {
@override
createState() {
return new CustomRadioState();
}
}
class CustomRadioState extends State<CustomRadio> {
List<RadioModel> sampleData = new List<RadioModel>();
@override
void initState() {
// TODO: implement initState
super.initState();
sampleData.add(new RadioModel(false, 'A', 'April 18'));
sampleData.add(new RadioModel(false, 'B', 'April 17'));
sampleData.add(new RadioModel(false, 'C', 'April 16'));
sampleData.add(new RadioModel(false, 'D', 'April 15'));
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("ListItem"),
),
body: new ListView.builder(
itemCount: sampleData.length,
itemBuilder: (BuildContext context, int index) {
return new InkWell(
//highlightColor: Colors.red,
splashColor: Colors.blueAccent,
onTap: () {
setState(() {
sampleData.forEach((element) => element.isSelected = false);
sampleData[index].isSelected = true;
});
},
child: new RadioItem(sampleData[index]),
);
},
),
);
}
}
class RadioItem extends StatelessWidget {
final RadioModel _item;
RadioItem(this._item);
@override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.all(15.0),
child: new Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Container(
height: 50.0,
width: 50.0,
child: new Center(
child: new Text(_item.buttonText,
style: new TextStyle(
color:
_item.isSelected ? Colors.white : Colors.black,
//fontWeight: FontWeight.bold,
fontSize: 18.0)),
),
decoration: new BoxDecoration(
color: _item.isSelected
? Colors.blueAccent
: Colors.transparent,
border: new Border.all(
width: 1.0,
color: _item.isSelected
? Colors.blueAccent
: Colors.grey),
borderRadius: const BorderRadius.all(const Radius.circular(2.0)),
),
),
new Container(
margin: new EdgeInsets.only(left: 10.0),
child: new Text(_item.text),
)
],
),
);
}
}
class RadioModel {
bool isSelected;
final String buttonText;
final String text;
RadioModel(this.isSelected, this.buttonText, this.text);
}
要使用,請執行以下操作:
複制代碼
void main() {
runApp(new MaterialApp(
home: new CustomRadio(),
));
}
螢幕截圖:
Flutter自定義元件實作圖檔單選按鈕
效果圖
Flutter中的Radio自定義空間較小,為了實作圖中的效果我們需要自定義,最終要實作的目标是多個元件之間隻要設定同一個controller即可實作單選效果,點選元件後無需外層調用setState,并且可以提供回調函數。
1、先說用法:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_radio/image_radio.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var images = [
"https://file03.16sucai.com/2016/10/1100/16sucai_p20161017095_34f.JPG",
"https://file03.16sucai.com/2016/10/1100/16sucai_p20161017095_34f.JPG",
"https://file03.16sucai.com/2016/10/1100/16sucai_p20161017095_34f.JPG",
];
ImageRadioController controller;
@override
void initState() {
controller = new ImageRadioController();
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ImageRadio(
images[0],
isSeleted: false,
controller: controller,
onChange: (v) => print("ImageRadio_1--->$v"),
),
SizedBox(width: 20,),
ImageRadio(
images[1],
isSeleted: true,
controller: controller,
onChange: (v) => print("ImageRadio_2--->$v"),
),
SizedBox(width: 20,),
ImageRadio(
images[2],
isSeleted: false,
controller: controller,
onChange: (v) => print("ImageRadio_3--->$v"),
),
],
),
),
);
}
}
1、new一個ImageRadioController;
2、直接使用ImageRadio,controller使用同一個ImageRadioController即可實作單選;
2、實作原理
将單選元件的重新整理回調函數儲存在controller中,監聽點選事件,點選後選中目前,并且回調controller中儲存的其它回調函數,以通知同一個controller下其它元件取消選中,代碼如下:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ImageRadio extends StatefulWidget {
ImageRadio(@required this.imageUrl, {
this.isSeleted: false,
this.controller,
this.onChange,
this.width: 60.0,
this.height: 60.0,
this.activeBorderColor: Colors.red,
this.inactiveBorderColor: Colors.transparent,
this.activeBorderWidth: 3.0,
this.inactiveBorderWidth: 3.0,
this.borderRadius: 2.0
});
bool isSeleted;
VoidCallback callMe;
final String imageUrl;
final ImageRadioController controller;
final ValueChanged<bool> onChange;
final double width;
final double height;
final Color activeBorderColor;
final Color inactiveBorderColor;
final double activeBorderWidth;
final double inactiveBorderWidth;
final double borderRadius;
@override
_ImageRadioState createState() => _ImageRadioState();
}
class _ImageRadioState extends State<ImageRadio> {
VoidCallback makeMeUnselect;
@override
void initState() {
// init
makeMeUnselect = () {
setState(() {
widget.isSeleted = false;
});
if (widget.onChange != null) {
widget.onChange(false);
}
};
// backup
widget.callMe = makeMeUnselect;
// add
if (widget.controller != null) {
print("initState() add callback--->$makeMeUnselect");
widget.controller.add(makeMeUnselect);
}
super.initState();
}
@override
void dispose() {
if (widget.controller != null) {
print("dispose() remove callback--->$makeMeUnselect");
widget.controller.remove(makeMeUnselect);
}
super.dispose();
}
@override
void didUpdateWidget(ImageRadio oldWidget) {
if (oldWidget != widget && oldWidget.callMe != makeMeUnselect) {
if (widget.controller != null) {
//print("old callback == new callback ? --->${oldWidget.callMe == makeMeUnselect}");
widget.controller.remove(oldWidget.callMe);
widget.controller.add(makeMeUnselect);
print("didUpdateWidget() remove--->$makeMeUnselect");
print("didUpdateWidget() add--->$makeMeUnselect");
}
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
widget.isSeleted = true;
});
if (widget.onChange != null) {
widget.onChange(true);
}
widget.controller.unselectOthers(makeMeUnselect);
},
child: Container(
width: widget.width,
height: widget.height,
alignment: Alignment.center,
decoration: new BoxDecoration(
border: new Border.all(
width: widget.isSeleted ? widget.activeBorderWidth : widget.inactiveBorderWidth,
color: widget.isSeleted ? widget.activeBorderColor : widget.inactiveBorderColor),
borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius)),
),
child: Image.network(
widget.imageUrl,
fit: BoxFit.cover,
width: widget.width,
height: widget.height,
),
),
);
}
}
class ImageRadioController {
List<VoidCallback> _callbackList;
ImageRadioController() {
_callbackList = [];
}
void add(VoidCallback callback) {
if (_callbackList == null) _callbackList = [];
_callbackList.add(callback);
}
void remove(VoidCallback callback) {
if (_callbackList != null) _callbackList.remove(callback);
}
void dispose() {
if (_callbackList != null) {
_callbackList.clear();
_callbackList = null;
}
}
void unselectOthers(VoidCallback currentCallback) {
if (_callbackList != null && _callbackList.length > 0) {
for(int i = 0, len = _callbackList.length; i < len; i++) {
VoidCallback callback = _callbackList[i];
if (callback == currentCallback) continue;
callback();
}
}
}
}
有需要的可以直接下載下傳:github位址
Flutter 自定義單選控件
在Flutter 應用開發中,經常會遇到各種單選效果,雖然官方提供了Radio元件,但是并不能滿足我們實際的開發需求,是以往往還需要自定義控件才能滿足平時的開發需求。下面就平時開發中用到的單選進行介紹:
自定義SegmentBar
對于分段元件大家肯定不會陌生,主要是實作多個分段,實作單選功能,效果如下圖。
話不多說,直接上代碼:
class SegmentBarView extends StatefulWidget {
List<String> datas;
Function(String) onSelected;
int defaultIndex=0;
SegmentBarView({@required this.datas, this.onSelected,this.defaultIndex});
@override
_SegmentBarViewState createState() => _SegmentBarViewState();
}
class _SegmentBarViewState extends State<SegmentBarView> {
List<String> sdkLists;
String selectItem;
@override
void initState() {
super.initState();
sdkLists = widget.datas;
selectItem=sdkLists[widget.defaultIndex];
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Row(
children: _buildSegments(sdkLists),
),
),
);
}
_buildSegments(List list) {
if(list == null) {
return Container();
}
List<Widget> items = List();
list.forEach((item){
if(item != null) {
items.add(Container(
padding: EdgeInsets.only(top: 8,bottom: 8),
child: _buildItem(item),
));
}
});
return items;
}
_buildItem(String item) {
if(selectItem == item) {
return Container(
height: 34,
child: RaisedButton(
shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(15)
),
color: Color(0xFF00A6DE),
onPressed: (){
},
child: Text(item,style: TextStyle(color: Colors.white),),
),
);
}else {
return Container(
height: 34,
child: OutlineButton(
borderSide: BorderSide(color: Color(0xFFcccccc),width: 0.5),
onPressed: (){
updateGroupValue(item);
},
child: Text(item),
),
);
}
}
updateGroupValue(String item) {
if(item == selectItem) {
return;
}else {
selectItem = item;
widget.onSelected(selectItem);
setState(() {
});
}
}
}
使用的時候,隻需要按照構造函數傳入對應的參數即可。
自定義Radio
當然,開發中還可以遇到下面這種帶圓角的按鈕,效果如下。
//隻能支援兩個按鈕單選
class RadioGroupWidget extends StatefulWidget {
List<String> datas ;
Function(String) onSelected;
double radioWidth=80;
double radioHeight=28;
RadioGroupWidget({@required this.datas,@required this.onSelected,this.radioWidth, this.radioHeight,});
@override
State<StatefulWidget> createState() {
return RadioGroupState();
}
}
class RadioGroupState extends State<RadioGroupWidget> {
var chooseStr;
int choosed=1;
Color choosedBgColor=Colors.blue;
Color choosedCornerColor=Colors.blue;
Color choosedTxtColor=Colors.white;
Color defaultBgColor=Colors.white;
Color defaultCornerColor=Colors.grey;
Color defaultTxtColor=Colors.grey;
@override
void initState() {
super.initState();
chooseStr=widget.datas[0];
}
@override
Widget build(BuildContext context) {
return Row(
children: [
GestureDetector(
onTap: (){
choosed=1;
chooseStr=widget.datas[0];
setState(() {});
widget.onSelected(chooseStr);
},
child: Container(
height: widget.radioHeight,
width: widget.radioWidth,
decoration: BoxDecoration(
color: choosed==1?choosedBgColor:defaultBgColor,
borderRadius: BorderRadius.only(topLeft: Radius.circular(15),bottomLeft: Radius.circular(15)),
border: Border.all(width: 1, color: choosed==1?choosedCornerColor:defaultCornerColor),
),
child: Center(child: Text(widget.datas[0],style: TextStyle(color: choosed==1?choosedTxtColor:defaultTxtColor,fontSize: 12))),
)
),
GestureDetector(
onTap: (){
choosed=2;
chooseStr=widget.datas[1];
setState(() {});
widget.onSelected(chooseStr);
},
child: Container(
height: widget.radioHeight,
width: widget.radioWidth,
decoration: BoxDecoration(
color: choosed==2?choosedBgColor:defaultBgColor,
borderRadius: BorderRadius.only(topRight: Radius.circular(15),bottomRight: Radius.circular(15)),
border: Border.all(width: 1, color: choosed==2?choosedCornerColor:defaultCornerColor),
),
child: Center(child: Text(widget.datas[1],style: TextStyle(color: choosed==2?choosedTxtColor:defaultTxtColor,fontSize: 12))),
)
)
],
);
}
}
List<String> lineRadios = ['實時流水', '累計流水'];
RadioGroupWidget(radioWidth:80,radioHeight:28,datas: lineRadios, onSelected: (value){
print('_buildChartTitle value: '+value.toString());
},)