App/Flutter

[ FLUTTER ] 직접 만든 라디오버튼

거북 2022. 7. 22. 01:44
import 'package:flutter/material.dart';
import 'package:iamhere/config/config.dart';

/// 라디오버튼에 사용할 테마
class RadioButtonTheme {
  final BorderSide activeBorderSide;
  final BorderSide inactiveBorderSide;
  final Color activeColor;
  final Color inactiveColor;
  const RadioButtonTheme({
    this.activeBorderSide = const BorderSide(width: 1.0, color: Color(0xFF292929)),
    this.inactiveBorderSide = const BorderSide(width: 1.0, color: Color(0xFF292929)),
    this.activeColor = const Color(0xFF292929),
    this.inactiveColor = const Color(0xFFFFFFFF),
  });
}

class RadioButton extends StatefulWidget {
  /// Padding 간격
  final EdgeInsets padding;

  /// 라디오버튼의 방향
  final Axis direction;

  /// 라디오버튼의 제목
  final List<String> items;

  /// 상태 값
  final int value;

  /// 변경 감지
  final ValueChanged<int> onChanged;

  /// 클릭 영역 (false: 라디오버튼만 클릭 영역으로 지정 | true: 라디오버튼 + 제목 모두 클릭 영역으로 지정)
  final bool listTap;

  /// 라디오버튼의 테마
  final RadioButtonTheme theme;

  const RadioButton({
    Key? key,
    this.padding = const EdgeInsets.symmetric(horizontal: 20, vertical: 11),
    this.direction = Axis.vertical,
    required this.items,
    required this.value,
    required this.onChanged,
    this.listTap = true,
    this.theme = const RadioButtonTheme(),
  }) : super(key: key);

  @override
  State<RadioButton> createState() => _RadioButtonState();
}

class _RadioButtonState<T> extends State<RadioButton> {
  int _value = 0;

  late Widget _activeWidget;
  late Widget _inactiveWidget;

  ValueChanged<int> get onChanged => widget.onChanged;

  void _handleValueChange(int value) {
    setState(() {
      _value = value;
      onChanged(value);
      // print('_handleValue: ${value} | ${widget.value} | $_value');
    });
  }

  @override
  void initState() {
    _value = widget.value;

    _activeWidget = Container(
      decoration: BoxDecoration(
        border: Border.all(width: widget.theme.activeBorderSide.width, color: widget.theme.activeBorderSide.color),
      ),
      width: 20,
      height: 20,
      padding: const EdgeInsets.all(4),
      child: Container(
        color: widget.theme.activeColor,
      ),
    );

    _inactiveWidget = Container(
      decoration: BoxDecoration(
        border: Border.all(width: widget.theme.inactiveBorderSide.width, color: widget.theme.inactiveBorderSide.color),
      ),
      width: 20,
      height: 20,
      padding: const EdgeInsets.all(4),
      child: Container(
        color: widget.theme.inactiveColor,
      ),
    );

    super.initState();
  }

  Widget _buttonWrap(int index) {
    return InkWell(
      onTap: widget.listTap
          ? () {
              // print('index: $index');
              _handleValueChange(index);
            }
          : null,
      child: Padding(
        padding: widget.padding,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            if (widget.listTap)
              _value == index ? _activeWidget : _inactiveWidget
            else
              InkWell(
                onTap: () {
                  _handleValueChange(index);
                },
                child: _value == index ? _activeWidget : _inactiveWidget,
              ),
            const SizedBox(width: 10, height: 10),
            Text(widget.items[index]),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    switch (widget.direction) {
      case Axis.horizontal:
        return Row(children: [for (int i = 0; i < widget.items.length; i++) Expanded(child: _buttonWrap(i))]);
      case Axis.vertical:
        return Column(children: [for (int i = 0; i < widget.items.length; i++) _buttonWrap(i)]);
    }
  }
}