ValueNotifier

샤워실의 바보·2024년 1월 11일
0

Flutter Animation

목록 보기
6/31
post-thumbnail

ValueNotifier는 Flutter에서 상태 관리를 위해 사용되는 클래스 중 하나입니다. ValueNotifier를 사용하는 것은 특히 작은 또는 로컬 상태를 관리할 때 유용합니다. 이 클래스의 목적은 값의 변화를 감지하고, 그 변화에 반응하여 애플리케이션의 UI를 업데이트하는 것입니다.

ValueNotifier의 기본 개념

  1. 데이터 저장 및 변경 감지: ValueNotifier는 어떠한 데이터 타입도 저장할 수 있으며, 이 데이터 값이 변경될 때마다 리스너들에게 알립니다. 예를 들어, 사용자 인터페이스에 슬라이더가 있고 그 값의 변화를 추적하고 싶을 때 ValueNotifier<double>을 사용할 수 있습니다.

  2. 리스너 등록: ValueNotifier에 리스너를 추가하여, 값이 변경될 때마다 특정 동작을 수행하게 할 수 있습니다. 예를 들어, 값이 변경될 때마다 UI를 다시 그리도록 할 수 있습니다.

  3. 성능: ValueNotifier는 비교적 단순한 상태 변경에 적합합니다. 복잡한 상태 로직이나 전역 상태 관리가 필요한 경우, Provider, Bloc, Riverpod 등의 더 강력한 상태 관리 솔루션을 고려해야 합니다.

사용 예시

class MyWidget extends StatelessWidget {
  final ValueNotifier<int> _counter = ValueNotifier(0);

  
  Widget build(BuildContext context) {
    return ValueListenableBuilder<int>(
      valueListenable: _counter,
      builder: (context, value, child) {
        return Center(
          child: Text('Value: $value'),
        );
      },
    );
  }

  void incrementCounter() {
    _counter.value++;
  }
}

위 예시에서, _counterValueNotifier<int> 타입으로, 정수 값을 저장합니다. ValueListenableBuilder를 사용하여 _counter의 값이 변경될 때마다 Text 위젯이 업데이트되어 현재 카운터 값을 표시합니다.

주의 사항

  • ValueNotifier를 사용할 때는 메모리 누수를 방지하기 위해 사용이 끝난 후에 반드시 리소스를 해제해야 합니다. 클래스가 파기될 때 dispose 메서드를 호출하여 리스너를 해제하는 것이 좋습니다.

  • ValueNotifier는 UI를 업데이트할 때 전체 위젯을 재빌드하지 않습니다. 따라서 성능상 이점을 제공합니다. 그러나, 대규모 애플리케이션에서는 더 복잡한 상태 관리 접근 방법을 고려해야 할 수도 있습니다.

Flutter에서 ValueNotifier의 인스턴스를 0.0의 초기 값으로 선언한 코드 스니펫에 대해 몇 가지 모범 사례와 개선 사항을 살펴보겠습니다. 이것은 특히 주니어 개발자들에게 가독성과 이해를 높이기 위함입니다.

  1. 변수 명명: 변수 _range는 밑줄로 시작하여 클래스 내부에서만 사용됨을 나타냅니다. 이러한 명명 규칙이 프로젝트 전반에 일관되게 사용되는지 확인하세요. _range라는 이름은 다소 일반적입니다. 이 변수가 앱의 컨텍스트에서 무엇을 나타내는지를 더 잘 설명할 수 있는 이름을 고려해 보세요.

  2. 타입 주석: <double>ValueNotifier의 타입을 명확히 한 것은 좋은 습관입니다. 이는 코드의 가독성을 향상시키고 컴파일 시간에 관련된 타입 오류를 잡는 데 도움이 됩니다.

  3. 초기화 값: ValueNotifier0.0으로 초기화됩니다. 이 기본값이 변수의 목적에 적합한지 확인하세요. 범위가 특정 항목(예: 슬라이더의 값)을 나타낸다면 0.0이 초기 값인 이유를 설명하는 주석을 추가하는 것이 좋습니다.

  4. 문서화 및 주석: 선언 위에 ValueNotifier의 목적을 설명하는 주석을 추가하는 것이 매우 도움이 될 수 있습니다. 특히 주니어 개발자나 코드베이스에 새로운 사람들에게 유용합니다.

  5. 범위 및 접근성: 변수가 private임(밑줄로 표시됨)을 나타내므로 선언된 클래스 내에서만 접근할 수 있습니다. 이것이 의도한 사용과 일치하는지 확인하세요.

다음은 주석을 포함한 개선된 버전입니다:

// [어떤 기능, 예: 슬라이더]의 현재 값을 보유하기 위한 ValueNotifier입니다.
// [초기 상태를 설명하는] 0.0으로 초기화됩니다.
final ValueNotifier<double> _currentValue = ValueNotifier(0.0);

이 버전은 더 설명적인 변수 이름과 그 목적을 설명하는 주석을 포함하고 있어, 코드의 의도를 이해하는 데 크게 도움이 됩니다. 팀 환경에서, 특히 아직 배우는 중인 사람들에게 잘 주석 처리된 코드는 매우 중요합니다.

import 'package:flutter/material.dart';

class ExplicitAnimationsScreen extends StatefulWidget {
  const ExplicitAnimationsScreen({super.key});

  
  State<ExplicitAnimationsScreen> createState() =>
      _ExplicitAnimationsScreenState();
}

class _ExplicitAnimationsScreenState extends State<ExplicitAnimationsScreen>
    with SingleTickerProviderStateMixin {
  late final AnimationController _animationController = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 2),
    reverseDuration: const Duration(seconds: 1),
  )..addListener(() {
      _range.value = _animationController.value;
    });

  late final Animation<Decoration> _decoration = DecorationTween(
    begin: BoxDecoration(
      color: Colors.amber,
      borderRadius: BorderRadius.circular(20),
    ),
    end: BoxDecoration(
      color: Colors.red,
      borderRadius: BorderRadius.circular(120),
    ),
  ).animate(_curve);

  late final Animation<double> _rotation = Tween(
    begin: 0.0,
    end: 0.5,
  ).animate(_curve);

  late final Animation<double> _scale = Tween(
    begin: 1.0,
    end: 1.1,
  ).animate(_curve);

  late final Animation<Offset> _position = Tween(
    begin: Offset.zero,
    end: const Offset(0, -0.2),
  ).animate(_curve);

  late final CurvedAnimation _curve = CurvedAnimation(
    parent: _animationController,
    curve: Curves.elasticOut,
    reverseCurve: Curves.bounceIn,
  );

  void _play() {
    _animationController.forward();
  }

  void _pause() {
    _animationController.stop();
  }

  void _rewind() {
    _animationController.reverse();
  }

  
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  final ValueNotifier<double> _range = ValueNotifier(0.0);

  void _onChanged(double value) {
    _range.value = value;
    _animationController.value = value;
  }

  
  Widget build(BuildContext context) {
    print("build");
    return Scaffold(
      appBar: AppBar(
        title: const Text('Explicit Animations'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SlideTransition(
              position: _position,
              child: ScaleTransition(
                scale: _scale,
                child: RotationTransition(
                  turns: _rotation,
                  child: DecoratedBoxTransition(
                    decoration: _decoration,
                    child: const SizedBox(
                      height: 400,
                      width: 400,
                    ),
                  ),
                ),
              ),
            ),
            const SizedBox(
              height: 25,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: _play,
                  child: const Text("Play"),
                ),
                ElevatedButton(
                  onPressed: _pause,
                  child: const Text("Pause"),
                ),
                ElevatedButton(
                  onPressed: _rewind,
                  child: const Text("Rewind"),
                )
              ],
            ),
            const SizedBox(
              height: 25,
            ),
            ValueListenableBuilder(
              valueListenable: _range,
              builder: (context, value, child) {
                return Slider(
                  value: value,
                  onChanged: _onChanged,
                );
              },
            )
          ],
        ),
      ),
    );
  }
}

이 Flutter 코드에서는 ValueNotifier를 사용하여 애니메이션 컨트롤러의 값을 추적하고 UI에 반영하는 방식을 구현하고 있습니다. ValueNotifier는 값의 변화를 감지하고 관련된 리스너에게 알림을 보내는 Flutter의 상태 관리 도구입니다. 이 예제에서는 ValueNotifier를 사용하여 애니메이션의 진행 상태를 Slider 위젯과 동기화하는 방법을 보여줍니다.

ValueNotifier의 역할:

  • _range:

    • _rangeValueNotifier<double> 객체로, 애니메이션 컨트롤러의 진행 상태를 추적합니다.
    • _range.valueAnimationControllervalue와 연동되어 애니메이션의 현재 진행 상태를 나타냅니다.
  • 애니메이션 컨트롤러와의 연동:

    • _animationControlleraddListener를 추가하여 컨트롤러의 값이 변경될 때마다 _range.value를 업데이트합니다.
    • 이를 통해 애니메이션 컨트롤러의 상태 변화가 실시간으로 _range에 반영됩니다.
  • _onChanged 메서드:

    • _onChanged 메서드는 Slider 위젯에서 사용자가 슬라이더를 조작할 때 호출됩니다.
    • 이 메서드는 _animationController.value를 업데이트하여 사용자의 입력에 따라 애니메이션 컨트롤러의 상태를 변경합니다.

ValueListenableBuilder의 사용:

  • ValueListenableBuilder 위젯:

    • ValueListenableBuilder_range 객체를 감시합니다.
    • _range의 값이 변경될 때마다, ValueListenableBuilder는 자식 위젯을 다시 빌드하여 UI를 최신 상태로 유지합니다.
  • Slider 위젯과의 연동:

    • Slider 위젯은 _range.value를 사용하여 현재 값을 표시합니다.
    • 사용자가 슬라이더를 조작하면, _onChanged 메서드를 통해 애니메이션 컨트롤러의 상태를 변경하고, 이는 다시 _range.value를 업데이트하여 UI에 반영됩니다.

종합:

이 코드는 ValueNotifierValueListenableBuilder를 사용하여 애니메이션의 상태를 UI 컴포넌트(여기서는 Slider)와 동기화하는 방법을 보여줍니다. 이러한 접근 방식은 Flutter에서 사용자 인터페이스와 애니메이션 상태를 효율적으로 연동하고, 사용자의 상호작용에 반응하는 동적인 UI를 만드는 데 유용합니다. 이 예제는 Flutter 개발자들이 애니메이션과 상태 관리를 더 깊이 이해하고 통합하는 데 도움이 될 것입니다.

profile
공부하는 개발자

0개의 댓글