Flutter:自定义单选按钮
Posted 一叶飘舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter:自定义单选按钮相关的知识,希望对你有一定的参考价值。
如何在Ffltter
中创建像这样的自定义单选按钮组
推荐答案
这是完整的代码
复制代码
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());
,)
以上是关于Flutter:自定义单选按钮的主要内容,如果未能解决你的问题,请参考以下文章