1.問題
最近在做一個類似答題問卷的功能,利用RadioListTile來實現(xiàn)單選。這個需求和方案本身是沒有什么問題的,本來想實現(xiàn)的效果如下所示。

好的,開始碼代碼。
Row(
children: <Widget>[
Flexible(
child: RadioListTile<String>(
title: Text('無'),
groupValue: this.value,
value: AnswerType.none.toString(),
onChanged: onChange),
),
Flexible(
child: RadioListTile<String>(
title: Text(
'有一點',
),
groupValue: this.value,
value: AnswerType.little.toString(),
onChanged: onChange),
),
Flexible(
child: RadioListTile<String>(
title: Text('多'),
groupValue: this.value,
value: AnswerType.more.toString(),
onChanged: onChange),
),
Flexible(
child: RadioListTile<String>(
title: Text('很多'),
groupValue: this.value,
value: AnswerType.lot.toString(),
onChanged: onChange),
),
],
)
走你。我尼瑪!?

2.解決過程
我嘗試了各種方法,比如把字體變小,尋找別的屬性修改都不行。

從圖上看我們需要解決的問題有兩個,1是調(diào)整選中框的內(nèi)邊距;2是調(diào)整選中框和文本之間的間距。沒辦法只能從RadioListTile源碼入手了。
const RadioListTile({
Key? key,
required this.value,
required this.groupValue,
required this.onChanged,
this.toggleable = false,
this.activeColor,
this.title,
this.subtitle,
this.isThreeLine = false,
this.dense,
this.secondary,
this.selected = false,
this.controlAffinity = ListTileControlAffinity.platform,
this.autofocus = false,
this.contentPadding,
this.shape,
this.tileColor,
this.selectedTileColor,
})
2.1 解決第一個問題
看了一圈只有contentPadding這個屬性有用,他的作用是Defines the insets surrounding the contents of the tile.意思大概是控制上圖灰色背景也就是整個控件里面內(nèi)容的內(nèi)邊距。默認(rèn)是EdgeInsets.symmetric(horizontal: 16.0),我這里設(shè)置了contentPadding: EdgeInsets.all( 0.0)之后解決了第一個問題,這個時候三個字的選項還是顯示不開。
2.2 解決第二個問題
繼續(xù)研究,發(fā)現(xiàn)RadioListTile里面實際上的child是ListTile,而ListTile有個屬性是horizontalTitleGap。這個屬性的意思指的是The horizontal gap between the titles and the leading/trailing widgets.,大概意思就是說我們的文本和Radio之間的間隙,leading/trailing 指的是Radio在內(nèi)容 前/后。
return MergeSemantics(
child: ListTileTheme.merge(
selectedColor: activeColor ?? Theme.of(context).accentColor,
child: ListTile(//子類是這個Widget
leading: leading,
title: title,
subtitle: subtitle,
trailing: trailing,
isThreeLine: isThreeLine,
dense: dense,
enabled: onChanged != null,
shape: shape,
tileColor: tileColor,
selectedTileColor: selectedTileColor,
onTap: onChanged != null ? () {
if (toggleable && checked) {
onChanged!(null);
return;
}
if (!checked) {
onChanged!(value);
}
} : null,
selected: selected,
autofocus: autofocus,
contentPadding: contentPadding,
),
),
);
但是RadioListTile屬性里沒有horizontalTitleGap,不能直接對它進行修改,于是需要重寫RadioListTile源碼。我們把RadioListTile的源碼復(fù)制出來改名為RadioListTileSuper,然后將修改為0,就搞定了。
3.總結(jié)
這里把源碼完整的放出來。這里需要注意的是如果你的environment: sdk: '>=2.7.0 <3.0.0'這里沒有>2.12.0你需要把源碼里的?和!都刪除掉。
3.1 修改源碼
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class RadioListTileSuper<T> extends StatelessWidget {
const RadioListTileSuper({
Key key,
this.value,
this.groupValue,
this.onChanged,
this.toggleable = false,
this.activeColor,
this.title,
this.subtitle,
this.isThreeLine = false,
this.dense,
this.secondary,
this.selected = false,
this.controlAffinity = ListTileControlAffinity.platform,
this.autofocus = false,
this.contentPadding,
this.shape,
this.tileColor,
this.selectedTileColor,
}) : assert(toggleable != null),
assert(isThreeLine != null),
assert(!isThreeLine || subtitle != null),
assert(selected != null),
assert(controlAffinity != null),
assert(autofocus != null),
super(key: key);
final T value;
final T groupValue;
final ValueChanged<T> onChanged;
final bool toggleable;
final Color activeColor;
final Widget title;
final Widget subtitle;
final Widget secondary;
final bool isThreeLine;
final bool dense;
final bool selected;
final ListTileControlAffinity controlAffinity;
final bool autofocus;
final EdgeInsetsGeometry contentPadding;
bool get checked => value == groupValue;
final ShapeBorder shape;
final Color tileColor;
final Color selectedTileColor;
@override
Widget build(BuildContext context) {
final Widget control = Radio<T>(
value: value,
groupValue: groupValue,
onChanged: onChanged,
toggleable: toggleable,
activeColor: activeColor,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
autofocus: autofocus,
);
Widget leading, trailing;
switch (controlAffinity) {
case ListTileControlAffinity.leading:
case ListTileControlAffinity.platform:
leading = control;
trailing = secondary;
break;
case ListTileControlAffinity.trailing:
leading = secondary;
trailing = control;
break;
}
return MergeSemantics(
child: ListTileTheme.merge(
selectedColor: activeColor ?? Theme.of(context).accentColor,
child: ListTile(
leading: leading,
title: title,
subtitle: subtitle,
trailing: trailing,
horizontalTitleGap:0,//和源碼的區(qū)別就是加了一個這個
isThreeLine: isThreeLine,
dense: dense,
enabled: onChanged != null,
shape: shape,
tileColor: tileColor,
selectedTileColor: selectedTileColor,
onTap: onChanged != null ? () {
if (toggleable && checked) {
onChanged(null);
return;
}
if (!checked) {
onChanged(value);
}
} : null,
selected: selected,
autofocus: autofocus,
contentPadding: contentPadding,
),
),
);
}
}
3.2 替換RadioListTileSuper
Row(
children: <Widget>[
Flexible(
child: RadioListTileSuper<String>(
title: Text('無'),
groupValue: this.value,
contentPadding: EdgeInsets.symmetric(horizontal: 10.0),//這里根據(jù)文本的內(nèi)容靈活處理文本距離左邊的距離
//,因為有的字多有的字短顯示長短不一致 不美觀
value: AnswerType.none.toString(),
onChanged: onChange),
),
Flexible(
child: RadioListTileSuper<String>(
title: Text('有一點',),
groupValue: this.value,
contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
value: AnswerType.little.toString(),
onChanged: onChange),
),
Flexible(
child: RadioListTileSuper<String>(
title: Text('多'),
groupValue: this.value,
contentPadding: EdgeInsets.symmetric(horizontal: 10.0),
value: AnswerType.more.toString(),
onChanged: onChange),
),
Flexible(
child: RadioListTileSuper<String>(
title: Text('很多'),
groupValue: this.value,
contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
value: AnswerType.lot.toString(),
onChanged: onChange),
),
],
)
3.3 最終效果圖
