Flutter中的Radio自定义空间较小,为了实现图中的效果我们需要自定义,最终要实现的目标是多个组件之间只要设置同一个controller即可实现单选效果,点击组件后无需外层调用setState,并且可以提供回调函数。
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 {
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: [
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即可实现单选;
将单选组件的刷新回调函数保存在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 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 {
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 _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();
}
}
}
}