Provider

샤워실의 바보·2024년 2월 16일
0
post-thumbnail
post-custom-banner

Provider 패키지는 Flutter에서 데이터의 흐름을 관리하고 UI의 재구성을 효율적으로 처리할 수 있게 하는 인기 있는 상태 관리 솔루션 중 하나입니다. 이 패키지를 사용하면 데이터 모델을 위젯 트리의 다양한 위치에서 쉽게 접근하고, 소비할 수 있습니다. Provider는 리스너를 통해 데이터의 변경 사항을 감지하고, 데이터가 변경될 때마다 해당 데이터에 의존하는 위젯들을 자동으로 재구성하여 UI를 업데이트합니다.

Provider의 기본 개념

  • Provider: 데이터를 제공하는 객체입니다. 앱의 최상위에 위치하여 필요한 데이터를 앱의 다른 부분에 전달합니다.
  • Consumer: Provider로부터 데이터를 소비하는 위젯입니다. Consumer 위젯은 Provider가 제공하는 데이터에 접근하여 UI를 구성합니다.
  • ChangeNotifier: 데이터의 변경을 알리는 데 사용되는 클래스입니다. ChangeNotifier를 상속받은 모델은 notifyListeners() 메서드를 호출하여 리스너(소비자)들에게 데이터 변경을 알릴 수 있습니다.
  • ChangeNotifierProvider: ChangeNotifier를 기반으로 하는 데이터 모델을 제공하는 Provider입니다. 이 Provider를 사용하면, 모델이 변경될 때마다 의존하는 위젯들이 자동으로 업데이트됩니다.

예제 코드 설명 및 주석 처리

// VideoConfig 클래스는 ChangeNotifier를 상속받아 isMuted 및 isAutoplay 상태를 관리합니다.
class VideoConfig extends ChangeNotifier {
  bool isMuted = false; // 비디오 음소거 상태
  bool isAutoplay = false; // 자동 재생 상태

  // isMuted 상태를 토글하고 리스너들에게 상태 변경을 알립니다.
  void toggleIsMuted() {
    isMuted = !isMuted;
    notifyListeners();
  }

  // isAutoplay 상태를 토글하고 리스너들에게 상태 변경을 알립니다.
  void toggleAutoplay() {
    isAutoplay = !isAutoplay;
    notifyListeners();
  }
}

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

  
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool _notifications = false;

  void _onNotificationsChanged(bool? newValue) {
    if (newValue == null) return;
    setState(() {
      _notifications = newValue;
    });
  }

  
  Widget build(BuildContext context) {
    // ChangeNotifierProvider를 사용하여 VideoConfig 인스턴스를 제공합니다.
    return ChangeNotifierProvider(
      create: (context) => VideoConfig(),
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Settings'),
        ),
        body: ListView(
          children: [
            // Provider.of<T>를 사용하여 VideoConfig 인스턴스에 접근하고, isMuted 상태에 따라 UI를 구성합니다.
            SwitchListTile.adaptive(
              value: Provider.of<VideoConfig>(context).isMuted,
              onChanged: (value) => Provider.of<VideoConfig>(context, listen: false).toggleIsMuted(),
              title: const Text("Auto Mute"),
              subtitle: const Text("Videos muted by default."),
            ),
            // 기타 설정 UI...
          ],
        ),
      ),
    );
  }
}

이 예제에서 ChangeNotifierProviderVideoConfig 인스턴스를 앱의 상위에서 제공합니다. 이를 통해 SettingsScreen 위젯은 Provider.of<VideoConfig>(context)를 사용하여 VideoConfig 인스턴스에 접근하고, isMutedisAutoplay 상태에 따라 UI를 동적으로 구성할 수 있습니다. 상태가 변경될 때 notifyListeners() 메서드가 호출되면, Provider는 이를 자동으로 감지하여 의존하는 UI를 재구성합니다.

Provider 패키지는 Flutter 앱에서 상태 관리를 간결하고 효과적으로 수행할 수 있도록 돕습니다. 데이터의 흐름을 명확하게 관리하고, 필요한 곳에서 쉽게 데이터를 사용할 수 있게 해 줍니다.

제공된 코드 예시에서 context.read()context.watch()를 직접적으로 사용하지 않았습니다만, 이들의 사용 방법과 Consumer, Provider와의 관계에 대해 설명드리겠습니다. Provider 패키지는 Flutter에서 상태 관리를 용이하게 하는 여러 유틸리티와 패턴을 제공합니다. context.read<T>(), context.watch<T>(), Consumer<T>, 그리고 Provider<T>는 그 중 일부입니다.

context.watch<T>()

context.watch<T>() 메서드는 위젯이 데이터 모델 T의 변화를 구독하게 만듭니다. T가 변할 때마다 Flutter는 watch를 호출한 위젯을 재빌드합니다. 이 방법은 위젯이 모델의 변화에 의존할 때 사용됩니다.

예시 코드에 context.watch<T>()를 사용하는 부분이 있었다면, 그것은 SwitchListTile 위젯이 VideoConfigisMuted 상태를 실시간으로 반영하도록 하기 위함입니다.

value: context.watch<VideoConfig>().isMuted,

context.read<T>()

context.read<T>() 메서드는 데이터 모델 T를 읽기 위해 사용되지만, T의 변화에 대해서는 구독하지 않습니다. 즉, T가 변해도 read를 사용하는 위젯은 재빌드되지 않습니다. read는 주로 이벤트 핸들러나 Future를 실행하는 데 사용됩니다.

예시 코드에서 onChanged 콜백 내에서 context.read<T>()를 사용하여, 사용자가 스위치를 토글할 때 toggleIsMuted() 함수를 호출하는 데 사용될 수 있습니다.

onChanged: (value) => context.read<VideoConfig>().toggleIsMuted(),
ChangeNotifierProvider(
  create: (context) => VideoConfig(),
  child: MaterialApp(
    // MaterialApp 구성
  ),
),

이 코드는 Flutter에서 비디오 포스트를 표시하고 관리하는 방법을 보여줍니다. 주요 기능으로는 비디오의 재생 및 일시 정지, 음소거 토글, 그리고 비디오와 관련된 상호작용을 위한 버튼들이 포함됩니다. Provider 패키지를 사용하여 비디오 설정(예: 음소거)의 상태를 관리하고, 해당 상태에 따라 UI를 동적으로 업데이트합니다.

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:tiktok_clone/common/widgets/video_config/video_config.dart';
// ... 다른 import 구문들

class VideoPost extends StatefulWidget {
  final Function onVideoFinished; // 비디오 재생 완료 시 호출될 콜백 함수
  final int index; // 비디오 포스트의 인덱스

  const VideoPost({super.key, required this.onVideoFinished, required this.index});

  
  State<VideoPost> createState() => _VideoPostState();
}

class _VideoPostState extends State<VideoPost> with SingleTickerProviderStateMixin {
  late final VideoPlayerController _videoPlayerController; // 비디오 재생을 위한 컨트롤러
  final Duration _animationDuration = const Duration(milliseconds: 200); // 애니메이션 지속 시간
  late final AnimationController _animationController; // 재생 아이콘 애니메이션을 위한 컨트롤러
  bool _isPaused = false; // 비디오가 일시 정지 상태인지 여부

  
  void initState() {
    super.initState();
    _initVideoPlayer(); // 비디오 플레이어 초기화

    _animationController = AnimationController(
      vsync: this, // 애니메이션 프레임 동기화를 위한 vsync
      lowerBound: 1.0,
      upperBound: 1.5,
      value: 1.5,
      duration: _animationDuration,
    );
  }

  void _initVideoPlayer() async {
    _videoPlayerController = VideoPlayerController.asset("assets/videos/video.mp4");
    await _videoPlayerController.initialize(); // 비디오 초기화
    await _videoPlayerController.setLooping(true); // 비디오를 반복 재생 설정
    if (kIsWeb) {
      await _videoPlayerController.setVolume(0); // 웹에서는 볼륨을 0으로 설정
    }
    _videoPlayerController.addListener(_onVideoChange); // 비디오 상태 변경 리스너 추가
    setState(() {}); // UI 업데이트를 위해 상태 변경
  }

  
  Widget build(BuildContext context) {
    return VisibilityDetector(
      key: Key("${widget.index}"), // 각 비디오 포스트를 구분하기 위한 키
      onVisibilityChanged: _onVisibilityChanged, // 화면에 보이는지 여부에 따라 동작을 변경
      child: Stack(
        children: [
          Positioned.fill(
            child: _videoPlayerController.value.isInitialized
                ? VideoPlayer(_videoPlayerController) // 비디오 플레이어 위젯
                : Container(color: Colors.black), // 비디오 초기화 전 검은색 배경 표시
          ),
          Positioned.fill(
            child: GestureDetector(
              onTap: _onTogglePause, // 탭 시 비디오 재생/일시 정지 토글
            ),
          ),
          Positioned(
            left: 20,
            top: 40,
            child: IconButton(
              icon: FaIcon(
                context.watch<VideoConfig>().isMuted
                    ? FontAwesomeIcons.volumeOff // 음소거 상태에 따라 아이콘 변경
                    : FontAwesomeIcons.volumeHigh,
                color: Colors.white,
              ),
              onPressed: () {
                context.read<VideoConfig>().toggleIsMuted(); // 음소거 토글 함수 호출
              },
            ),
          ),
          // ... 기타 UI 요소들
        ],
      ),
    );
  }
}

이 코드는 Provider 패키지를 사용하여 앱 전체에서 비디오 설정의 상태를 관리합니다.

비디오 포스트 위젯(VideoPost)은 비디오를 재생하고, 음소거 상태를 토글하며, 사용자가 비디오를 탭할 때 재생 또는 일시 정지 상태를 토글하는 등의 기능을 제공합니다. VideoConfig 클래스는 비디오의 음소거(isMuted) 상태와 자동 재생(isAutoplay) 상태를 관리합니다. 사용자 인터페이스는 비디오 설정의 현재 상태(isMuted, isAutoplay)에 따라 동적으로 업데이트되며, 이는 Provider를 통해 상태 관리가 이루어지기 때문입니다.

VisibilityDetector를 사용하여 비디오가 화면에 완전히 보일 때만 재생하도록 하고, 화면에서 사라지면 일시 정지하도록 함으로써 사용자 경험을 개선합니다. 이러한 방식으로, 앱의 성능을 최적화하고 사용자에게 더 나은 시청 경험을 제공할 수 있습니다.

profile
공부하는 개발자
post-custom-banner

0개의 댓글