App/Flutter

[ FLUTTER ] 커스텀위젯 value값 주고받기 예시

거북 2021. 12. 27. 22:03

 

* animated_condition_bar.dart

// ignore_for_file: must_be_immutable, unnecessary_null_comparison

import 'package:flutter/material.dart';
import 'package:secondtrack/config/config.dart';

class AnimatedConditionBar extends StatefulWidget {
  // int value;
  final int value;
  final int length;
  double? maxWidth;
  final String title;
  final ValueChanged<int?>? onChanged;
  final bool tristate;
  AnimatedConditionBar({
    Key? key,
    this.value = 1,
    this.length = 5,
    this.maxWidth,
    this.title = '',
    @required this.onChanged,
    this.tristate = false,
  })  : assert(tristate != null),
        assert(tristate || value != null),
        super(key: key);

  @override
  State<AnimatedConditionBar> createState() => _AnimatedConditionBarState();
}

class _AnimatedConditionBarState extends State<AnimatedConditionBar> {
  int delay = 300;
  double conditionStatusWidth = 0;

  Future resizedConditionStatus(int value) async {
    if (value < (widget.value - 1)) {
      delay = 300 * (widget.value - 1);
    } else {
      delay = 300 * (value);
    }
    await Future.delayed(const Duration(milliseconds: 100), () {
      setState(() {
        // conditionStatusWidth = SizeConfig.screenWidth - (InnerConfig.large*2);
        conditionStatusWidth = (SizeConfig.screenWidth - (InnerConfig.large * 2)) / (widget.length - 1) * (widget.value - 1);
        if (conditionStatusWidth == 0) conditionStatusWidth = 1;
      });
    });
  }

  void _handleValueChange(int index) {
    assert(widget.onChanged != null);
    widget.onChanged!(index + 1);
    resizedConditionStatus(index);
  }

  @override
  void initState() {
    widget.maxWidth ??= SizeConfig.screenWidth - (InnerConfig.large * 2);
    resizedConditionStatus(widget.value);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    double btnSize = 40;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Padding(
          padding: const EdgeInsets.only(
            top: 24,
            bottom: 5,
            left: InnerConfig.normal,
            right: InnerConfig.normal,
          ),
          child: Text(
            widget.title,
            style: const TextStyle(
              fontSize: FontConfig.basic,
              fontWeight: Weight.Medium,
              color: ColorConfig.darkGrey,
            ),
          ),
        ),
        Stack(
          children: [
            Container(
              padding: const EdgeInsets.symmetric(
                vertical: InnerConfig.normal,
                horizontal: InnerConfig.large,
              ),
              color: ColorConfig.lightGrey,
              child: Column(
                children: [
                  Stack(
                    clipBehavior: Clip.none,
                    children: [
                      Container(
                        width: SizeConfig.screenWidth - (InnerConfig.large * 2),
                        height: 2,
                        color: ColorConfig.grey,
                      ),
                      AnimatedContainer(
                        duration: Duration(milliseconds: delay),
                        curve: Curves.easeOut,
                        width: conditionStatusWidth,
                        height: 2,
                        color: ColorConfig.main,
                      ),
                      Positioned(
                        top: -5,
                        bottom: -5,
                        left: 0,
                        right: 0,
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            for (int i = 0; i < widget.length; i++)
                              Row(
                                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                children: [
                                  if (i != 0)
                                    Container(
                                      width: 5,
                                      color: ColorConfig.lightGrey,
                                    ),
                                  AnimatedOpacity(
                                    opacity: conditionStatusWidth == 0 ? 0 : 1,
                                    duration: const Duration(milliseconds: 1000),
                                    curve: Curves.easeOut,
                                    child: ClipOval(
                                      child: Container(
                                        width: 10,
                                        height: 10,
                                        color: widget.value > i ? ColorConfig.main : ColorConfig.grey,
                                      ),
                                    ),
                                  ),
                                  if (i != widget.length - 1)
                                    Container(
                                      width: 5,
                                      color: ColorConfig.lightGrey,
                                    ),
                                ],
                              ),
                          ],
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 10),
                  AnimatedOpacity(
                    opacity: conditionStatusWidth == 0 ? 0 : 1,
                    duration: const Duration(milliseconds: 1000),
                    curve: Curves.easeOut,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        for (int i = 0; i < widget.length; i++) Text('${i + 1}', style: TextStyle(fontSize: FontConfig.info, fontWeight: Weight.Medium, color: widget.value > i ? ColorConfig.main : ColorConfig.grey)),
                      ],
                    ),
                  ),
                ],
              ),
            ),
            Padding(
              padding: EdgeInsets.symmetric(
                vertical: InnerConfig.normal - 18,
                horizontal: InnerConfig.large - (btnSize / 2) + 5,
              ),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  for (int i = 0; i < widget.length; i++)
                    ClipRRect(
                      borderRadius: RadiusConfig.normal,
                      child: InkWell(
                        onTap: widget.onChanged != null
                            ? () {
                                _handleValueChange(i);
                              }
                            : null,
                        borderRadius: BorderRadius.circular(btnSize),
                        child: Container(
                          width: btnSize,
                          height: 60,
                          color: Colors.transparent,
                        ),
                      ),
                    ),
                ],
              ),
            ),
          ],
        ),
      ],
    );
  }
}

 

* 사용

int _value = 0;

.
.
.

AnimatedConditionBar(
  value: _value,
  length: 5,
  title: '제목',
  onChanged: (value) {
    setState(() {
	  _value = value!;
    });
  },
),