개발노트

8. [Flutter] Navigator.push(), pop() 화면 전환하기 본문

앱 개발/Flutter

8. [Flutter] Navigator.push(), pop() 화면 전환하기

mroh1226 2023. 3. 31. 15:06
반응형
Flutter에서 Navigator 클래스는 앱 내에서 화면을 전환하는 데 사용됩니다.

새 화면 띄울때 사용하는 push()
현재 화면을 종료할때 사용하는 pop()

메서드가 있으며, 이를 활용한 페이지 전환이 가능합니다.

 


Navigator.push()

  • push() 메서드: push() 메서드는 새로운 화면을 현재 화면 위에 푸시(push)하여 쌓는 작업을 수행합니다. 
  • 이 메서드를 사용하면 새로운 화면이 스택에 추가되고 사용자는 이전 화면으로 되돌아갈 수 있습니다. 
  • 일반적으로 사용자가 이전화면을 종료시키지 않은 채로 새로운 화면으로 이동할 때 사용됩니다.
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondScreen()),
);

 

Navigator.pop()

  • pop() 메서드: pop() 메서드는 현재 화면을 스택에서 팝(pop)하여 제거하는 작업을 수행합니다. 
  • 이 메서드를 호출하면 현재 화면이 스택에서 제거되고 이전 화면으로 되돌아갑니다. 
  • 주로 뒤로 가기 버튼이나 특정 조건을 충족했을 때 이전 화면으로 돌아가는 데 사용됩니다.
Navigator.pop(context);

 


응용하기.

Navigator.push() 를 사용하여 이전에 만든 ListView Item을 클릭했을 때 새로운 화면(Widget)으로 전환되는 기능을 만들어 보겠습니다.

 

- 이전 포스팅: https://mroh1226.tistory.com/69

 

6. [Flutter] 앱 하단에 화면전환 버튼 만들기(with BottomNavigationBar)

BottomNavigationBar 을 사용하여 화면 하단에 Tap 버튼을 두고 위젯(화면)을 전환시키는 기능을 만들어 보겠습니다. 전환되는 화면으로 사용할 Widget을 views라는 폴더를 두고 각각 생성해 줬습니다. 전

mroh1226.tistory.com

 

 

위젯이 클릭되었을 때 이벤트를 발생하도록 도와주는 위젯 GestureDetector와 Widget을 애니메이션 효과로 감싸서 마치 화면처럼 보여주는 Route를 Push 할 수 있는 위젯 Navigator를 조합하여 만들어 보겠습니다.

 

파라미터 값을 이용하여 이전 화면(위젯)의 값을 다음화면으로 가져올 수 있는지도 이번 시간을 통해 확인해 보겠습니다.

 


첫 번째 index 화면인 page_dump.dart로 가서 아래와 같이 작성합니다.

 

 이전 main.dart 에서는 Scaffold Widget의 body:에 화면(위젯)을 List<Widget>에 넣어 Item Index를 이용하여 BottomNavigationBar를 통해 갈아 끼워주는 방식으로 화면전환이 이뤄졌다면,

 

 이번 Navigator.push() 방식은 Widet을 화면처럼 만들어주는 Route라는 위젯을 이용하여 화면을 호출하는 방식입니다.

 

Navigator.push()가 추가된 전체적인 소스는 아래와 같습니다.


page_dump.dart
import 'package:flutter/material.dart';
import 'package:flutter_tonetrainer/models/entity_dump.dart';
import 'package:flutter_tonetrainer/views/page_dump_chord.dart';

class PageDump extends StatelessWidget {
  PageDump({super.key});
  final DumpList dumpList = DumpList.creatDumpList();

  void listItemTap(BuildContext context, Dump dumpl) {
    Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => PageChord(
                  title: dumpl.title,
                  content: dumpl.content,
                  level: dumpl.level,
                  iconData: dumpl.icons,
                ),
            fullscreenDialog: true));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 200),
          child: ListView.separated(
            scrollDirection: Axis.horizontal,
            itemCount: dumpList.dumplist.length,
            separatorBuilder: (context, index) {
              return const SizedBox(
                height: 40,
                width: 10,
              );
            },
            itemBuilder: (context, index) {
              var dumpl = dumpList.dumplist[index];
              return GestureDetector(
                onTap: () {
                  listItemTap(context, dumpl);
                },
                child: Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 80, vertical: 80),
                  decoration: BoxDecoration(
                      color: Colors.amber.shade800,
                      borderRadius:
                          const BorderRadius.all(Radius.circular(20))),
                  child: Column(
                    children: [
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          Icon(
                            dumpl.icons,
                            size: 30,
                          ),
                          Text(
                            dumpl.title,
                            style: const TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 30),
                          )
                        ],
                      ),
                      Row(
                        crossAxisAlignment: CrossAxisAlignment.center,
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Text(
                            dumpl.level.name,
                            style: const TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 30),
                          ),
                          Text(
                            dumpl.content,
                            style: const TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 25),
                          )
                        ],
                      )
                    ],
                  ),
                ),
              );
            },
          )),
    );
  }
}

설명.1

 
          ListView.separated(
            scrollDirection: Axis.horizontal,
            itemCount: dumpList.dumplist.length,
            separatorBuilder: (context, index) {
              return const SizedBox(
                height: 40,
                width: 10,
              );
            },
            itemBuilder: (context, index) {
              var dumpl = dumpList.dumplist[index];
              return GestureDetector(
                onTap: () {
                  listItemTap(context, dumpl);
                },
                child: Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 80, vertical: 80),
                  decoration: BoxDecoration(
                      color: Colors.amber.shade800,
                      borderRadius:
                          const BorderRadius.all(Radius.circular(20))),
                  child: Column(
 

먼저, ListView.separated() 위젯을 보도록하겠습니다.

 

기존 소스에서 추가된 사항으로는 ListView.separated()의  itemBuilder: 속성에 자식이자, dumpl의 값들을 노출시키는 위젯을 모두 포함하고 있었던 Container() 위젯 상위에 GestureDetector 라는 위젯을 씌워져 있습니다.

 

GestureDetector() 에는 여러 가지 이벤트를 발생시킬 수 있는 속성이 있으며, 이 중 onTap: 을 구현하도록 하겠습니다.

 

listItemTap() 에 Parameter 값으로 context와 dumpl을 넘겨줍니다.

 

*context 를 넘기는 이유는 listItemTap() 메서드에 나오게 될 위젯 Navigator.push()에 context가 요구되기 때문입니다.

*dumpl 을 넘기는 이유는 다음 화면이 될 위젯의 Parameter에 클릭된 Dump의 객체 속성 값들을 넘기기 위함입니다.

 

 

※Flutter에서 GestureDetector() 위젯에서 일반적으로 사용되는 이벤트는 다음과 같습니다.

1. onTap: 사용자가 위젯을 탭하는 경우 발생합니다.
2. onDoubleTap: 사용자가 위젯을 더블 탭하는 경우 발생합니다.
3. onLongPress: 사용자가 위젯을 길게 누르는 경우 발생합니다.
4. onVerticalDragDown: 사용자가 위젯에서 수직으로 드래그를 시작하는 경우 발생합니다.
5. onVerticalDragEnd: 사용자가 위젯에서 수직으로 드래그를 끝내는 경우 발생합니다.
6. onHorizontalDragDown: 사용자가 위젯에서 수평으로 드래그를 시작하는 경우 발생합니다.
7. onHorizontalDragEnd: 사용자가 위젯에서 수평으로 드래그를 끝내는 경우 발생합니다.
8. onPanDown: 사용자가 위젯에서 드래그를 시작하는 경우 발생합니다.
9. onPanEnd: 사용자가 위젯에서 드래그를 끝내는 경우 발생합니다.
10. onScaleStart: 사용자가 위젯에서 확대/축소 제스처를 시작하는 경우 발생합니다.
11. onScaleEnd: 사용자가 위젯에서 확대/축소 제스처를 끝내는 경우 발생합니다.

위의 이벤트 중 일부는 함께 사용될 수도 있습니다. 예를 들어, onPanDown과 onPanUpdate 이벤트를 함께 사용하면, 사용자가 위젯에서 드래그하는 경우 연속적인 이벤트를 처리할 수 있습니다.

  또한 GestureDetector() 위젯을 사용하여 사용자 지정 이벤트를 처리할 수도 있습니다. 이 경우, onTap, onDoubleTap, onLongPress 이벤트 등의 내장 이벤트 대신, 사용자가 정의한 콜백 함수를 호출합니다. 이를 통해, 사용자 지정 이벤트를 처리하고 필요한 작업을 수행할 수 있습니다.

설명.2

  void listItemTap(BuildContext context, Dump dumpl) {
    Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => PageChord(
                  title: dumpl.title,
                  content: dumpl.content,
                  level: dumpl.level,
                  iconData: dumpl.icons,
                ),
            fullscreenDialog: true));
  }

ListView의 item을 눌렀을 때 onTap: 으로 동작할 메서드를 위와 같이 생성해 줍니다.

 

파라미터 값으로 받아온 context를 Navigator.push 인자값에 넣어주고 PageChord에 받아온 dumpl의 속성들을 이용하여 Dump 객체 값들을 사용할 수 있도록 파라미터 값으로 넘겨줍니다.

 

fullscreenDialog: true 로 두면 Appbar가 활성화되어있을 때 뒤로가기 아이콘 '<' 대신  닫기 아이콘 'X'이 표시됩니다.


 

page_dump_chord.dart
import 'package:flutter/material.dart';
import 'package:flutter_tonetrainer/models/entity_dump.dart';

class PageChord extends StatelessWidget {
  final String title, content;
  final Level level;
  final IconData iconData;
  const PageChord(
      {super.key,
      required this.title,
      required this.content,
      required this.level,
      required this.iconData});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        heightFactor: 20,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 200),
          child: Container(
            decoration: const BoxDecoration(
                color: Colors.blueGrey,
                borderRadius: BorderRadius.all(Radius.elliptical(20, 20))),
            padding: const EdgeInsets.symmetric(horizontal: 80, vertical: 80),
            child: Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Icon(
                      iconData,
                      size: 30,
                    ),
                    Text(title,
                        style: const TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 30)),
                    const SizedBox(
                      width: 10,
                    ),
                  ],
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(level.name,
                        style: const TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 30)),
                    const SizedBox(
                      width: 10,
                    ),
                    Text(content,
                        style: const TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 30))
                  ],
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}
class PageChord extends StatelessWidget {
  final String title, content;
  final Level level;
  final IconData iconData;
  const PageChord(
      {super.key,
      required this.title,
      required this.content,
      required this.level,
      required this.iconData});

매개변수(Parameter)를 받기 위해 변수를 선언해 주고 생성자에 required를 통해 'Null'이 아닌 값이 들어온다는 의미와 동시에 매개변수 값을 필요로 한다는 것을 소스로 작성합니다.

 

     
        Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Icon(
                      iconData,
                      size: 30,
                    ),
                    Text(title,
                        style: const TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 30)),
                    const SizedBox(
                      width: 10,
                    ),
                  ],
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(level.name,
                        style: const TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 30)),
                    const SizedBox(
                      width: 10,
                    ),
                    Text(content,
                        style: const TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 30))
                  ],
 

위에서 보는 소스와 같이 PageChord() 생성자를 통해 받아온 값을 위젯(Widget)에 바로 사용가능합니다.


 

빌드해서 위젯끼리 값이 잘 넘어가는 지 확인합니다.

반응형
Comments