개발노트

81. [Flutter] bottomNavigationBar 로 화면 전환하는 화면에서 미니 플레이어 구현하기 (AnimatedContainer,PopScope) 본문

앱 개발/Flutter

81. [Flutter] bottomNavigationBar 로 화면 전환하는 화면에서 미니 플레이어 구현하기 (AnimatedContainer,PopScope)

mroh1226 2024. 9. 25. 23:46
반응형

PopScope의 핵심은 뒤로가기 버튼의 순기능인 뒤로가기를 막고 다른 액션을 줄 수 있다는 것이다.

이 위젯을 이용하여 뒤로가기 버튼을  Player의 크기가 작아지는 버튼으로 사용해본다.

 

AnimatedContainer의 핵심은 특정 값을 분기를 두고 컨테이너의 크기나 다른 번동사항이 애니메이션으로 편하게 적용된다는 점이다. 이로 크기를 변동시켜본다.

작아졌다 커졌다 하는 Player 소스 (GetX의 GetBuilder 사용함)
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class MiniPlayerController extends GetxController {
  late bool isMinimized; // MiniPlayer의 최소화 상태

  void togglePlayerSize() {
    isMinimized = !isMinimized; // 상태 전환
    update(); // GetBuilder가 UI를 업데이트하도록 알림
  }

  @override
  void onInit() {
    isMinimized = true;
    super.onInit();
  }
}

class MiniPlayer extends StatelessWidget {
  MiniPlayer({super.key});

  final MiniPlayerController controller =
      Get.put(MiniPlayerController()); // 컨트롤러 인스턴스를 생성 및 등록

  @override
  Widget build(BuildContext context) {
    return GetBuilder<MiniPlayerController>(
      builder: (controller) => PopScope(
        canPop: controller.isMinimized ? true : false,
        //canPop: 뒤로가기 버튼 무시여부 false면 무시함
        onPopInvokedWithResult: (didPop, result) {
          //onPopInvokedWithResult: didPop 상관없이
          //뒤로가기 버튼 누르면 실행되는 Function
          controller.togglePlayerSize();
        },
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 300),
          height: controller.isMinimized ? 70 : 1000, // 상태에 따라 높이를 조정
          decoration: BoxDecoration(
            color: Colors.blueAccent,
            borderRadius: controller.isMinimized
                ? BorderRadius.circular(30)
                : BorderRadius.circular(0), // 작게 표시될 때 테두리를 둥글게
          ),
          child: GestureDetector(
            onVerticalDragUpdate: (details) {
              if (details.primaryDelta! > 10 && !controller.isMinimized) {
                controller.togglePlayerSize();
              }
            },
            child: Column(
              children: [
                !controller.isMinimized
                    ? const Expanded(
                        child: Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Text('Now Playing:'),
                              SizedBox(height: 10),
                              Text(
                                'Song Title',
                                style: TextStyle(
                                    fontSize: 18, fontWeight: FontWeight.bold),
                              ),
                              SizedBox(height: 10),
                              Icon(
                                Icons.music_note,
                                size: 50,
                                color: Colors.white,
                              ),
                            ],
                          ),
                        ),
                      )
                    :
                    // 작아진 상태의 UI
                    ListTile(
                        leading:
                            const Icon(Icons.music_note, color: Colors.white),
                        title: const Text('Song Title',
                            style: TextStyle(color: Colors.white)),
                        trailing: IconButton(
                          icon: const Icon(Icons.expand_less,
                              color: Colors.white),
                          onPressed: controller.togglePlayerSize, // 다시 확장
                        ),
                      ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

bottomNavigationBar로 화면 전환이 되는 메인 화면
void main() {
  runApp(const MainApp()); // MainApp을 실행하는 main 함수
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: Scaffold(
        body: Stack(
          alignment: Alignment.bottomCenter,
          children: [
            const IndexedStack(
              index: 0,
              children: [
                Center(child: Text('Page 1')),
                Center(child: Text('Page 2')),
                Center(child: Text('Page 3')),
              ],
            ),
            MiniPlayer(), // MiniPlayer를 여기서 호출
          ],
        ),
        bottomNavigationBar: const BottomNavigation(),
      ),
    );
  }
}

class BottomNavigation extends StatelessWidget {
  const BottomNavigation({super.key});

  @override
  Widget build(BuildContext context) {
    return GetBuilder<MiniPlayerController>(
      init: MiniPlayerController(), // 컨트롤러 초기화
      builder: (controller) {
        return AnimatedContainer(
          height: controller.isMinimized
              ? kBottomNavigationBarHeight
              : 0, // 상태에 따라 높이 조정
          duration: const Duration(milliseconds: 300),
          child: BottomAppBar(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                IconButton(
                  icon: const Icon(Icons.ac_unit),
                  onPressed: () {},
                ),
                IconButton(
                  icon: const Icon(Icons.access_alarm_outlined),
                  onPressed: () {},
                ),
                IconButton(
                  icon: const Icon(Icons.accessibility_sharp),
                  onPressed: () {},
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

 

 

완료된 사항

반응형
Comments