일반적인 라디오 버튼이 아닌 아래와 같이 텍스트로 이루어진 라디오 버튼을 커스텀하고 싶었다.
비슷하게 성별을 선택할 수 있고, 비공개로 처리할 수도 있는 위젯을 만들어보겠다.
Provider
를 이용하여 View
와 ViewModel
로 나누어 구현하였다.
enum Gender { initialized, male, female, hide }
// enum으로 관리하는 예시
// 처음에 아무것도 선택되지 않은 상태를 위해 initialized 포함
class RadioViewModel extends ChangeNotifier {
Gender gender = Gender.initialized; // 빈 초기값
// 선택 성별 반영 함수
void changeGender(Gender genderValue) {
gender = genderValue;
notifyListeners();
}
}
Viewmodel
은 선택된 데이터와 선택사항을 반영하는 함수로만 이루어져있기 때문에 간단하다.
class RadioView extends StatelessWidget {
const RadioView({super.key});
final MaterialColor maleColor = Colors.blue;
final MaterialColor femaleColor = Colors.pink;
Widget build(BuildContext context) {
final RadioViewModel viewmodel = Provider.of<RadioViewModel>(context);
return Scaffold(
appBar: AppBar(title: Text("Custom Radio Demo")),
body: Center(
child: SizedBox(
height: 200,
width: 160,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("성별"),
Row(
children: [
Checkbox(
// 비공개 상태에서만 활성화되므로, 성별을 선택했을 때에는 해제된다.
value: viewmodel.gender == Gender.hide,
onChanged: (value) {
if (value!) {
viewmodel.changeGender(Gender.hide);
}
},
),
Text("비공개"),
],
),
],
),
Row(
spacing: 10,
children: [
// 쉬운 구현을 위해 OutlinedButton을 사용함
// 선택된 성별에 따라서 스타일 적용
// 선택 상태를 적용하는 조건과 활성화 색상을 지정하는 Custom 함수 적용
Expanded(
child: OutlinedButton(
onPressed: () => viewmodel.changeGender(Gender.male),
style: customRadioButtonStyle(
isSelected: viewmodel.gender == Gender.male,
mainColor: maleColor,
),
child: Icon(
Icons.male,
size: 30,
color:
viewmodel.gender == Gender.male
? maleColor.shade400
: Colors.black45,
),
),
),
Expanded(
child: OutlinedButton(
onPressed: () => viewmodel.changeGender(Gender.female),
style: customRadioButtonStyle(
isSelected: viewmodel.gender == Gender.female,
mainColor: femaleColor,
),
child: Icon(
Icons.female,
size: 30,
color:
viewmodel.gender == Gender.female
? femaleColor.shade400
: Colors.black45,
),
),
),
],
),
],
),
),
),
);
}
}
아래는 위 View
에서 사용한 함수이며, 활성화 조건 isSelected
와 메인 색상 mainColor
를 받아서 그에 맞게 스타일을 반환해준다.
ButtonStyle customRadioButtonStyle({
required bool isSelected,
required MaterialColor mainColor,
}) {
return ButtonStyle(
side: WidgetStatePropertyAll(
BorderSide(
color: isSelected ? mainColor.shade300 : Colors.black45,
width: 2,
),
),
backgroundColor: WidgetStatePropertyAll(
isSelected ? mainColor.shade50 : Colors.transparent,
),
);
}
Provider
나MVVM
구조를 사용하지 않고StatefulWidget
으로만 구현한다면, 위ViewModel
에서 관리했던 상태 변수들을 위젯 내부에 선언하고,notifyListeners
대신setState
함수를 이용하면 된다.