본문으로 이동

플러터:화면 전환(화면 쌓기, 하단 네비게이션 바): 두 판 사이의 차이

학교의 모든 지식. SMwiki
 
(같은 사용자의 중간 판 6개는 보이지 않습니다)
4번째 줄: 4번째 줄:
네이게이션을 통한 화면전환.
네이게이션을 통한 화면전환.


기본적으로 네비게이션바로 구조를 잡고, 네비게이터로 상세 화면을 다루는 형식을 많이 쓴다.
= 기본 화면 전환(Navigator) =
* Flutter 앱에서 화면을 이동하는 가장 기본적인 방식은 Navigator를 사용하는 것이다.
* Flutter 앱에서 화면을 이동하는 가장 기본적인 방식은 Navigator를 사용하는 것이다.
* Navigator는 “화면(stack)”을 관리하며, 새로운 페이지를 push하여 화면을 열고 pop하여 닫는다.
* Navigator는 “화면(stack)”을 관리하며, 새로운 페이지를 push하여 화면을 열고 pop하여 닫는다.
= 기본 화면 전환 =
<syntaxhighlight lang="dart">import 'package:flutter/material.dart';
<syntaxhighlight lang="dart">import 'package:flutter/material.dart';


39번째 줄: 40번째 줄:
             // 화면 이동 (push)
             // 화면 이동 (push)
             Navigator.push(
             Navigator.push(
               context,
               context, // 현재 위젯의 위치가 어디인지.(어디 위에 화면을 올릴 것인지.)
               MaterialPageRoute(builder: (_) => SecondPage()),
               MaterialPageRoute(builder: (_) => SecondPage()),
             );
             );
65번째 줄: 66번째 줄:
     );
     );
   }
   }
}
}</syntaxhighlight>
</syntaxhighlight>


== 이해를 위한 추가 설명 ==
== 이해를 위한 추가 설명 ==
77번째 줄: 77번째 줄:
|화면을 위에 올리기.
|화면을 위에 올리기.
Navigator.push
Navigator.push
|<syntaxhighlight lang="dart">
|<syntaxhighlight lang="dart">Navigator.push(
Navigator.push(
context,
context,
MaterialPageRoute(builder: (_) => SecondPage()),
MaterialPageRoute(builder: (_) => SecondPage()),
);
);</syntaxhighlight>
</syntaxhighlight>


새로운 페이지를 스택 위에 ‘올려서’ 이동한다.
새로운 페이지를 스택 위에 ‘올려서’ 이동한다.
88번째 줄: 86번째 줄:
MaterialPageRoute는 기본적인 화면 전환 애니메이션을 제공한다.
MaterialPageRoute는 기본적인 화면 전환 애니메이션을 제공한다.
|마테리얼 디자인 규칙에 따라 뒤로가기가 가능하면 자동으로 뒤로가기 버튼이 생긴다.
|마테리얼 디자인 규칙에 따라 뒤로가기가 가능하면 자동으로 뒤로가기 버튼이 생긴다.
<code>_</code>는 '인자를 받긴 해야 하지만, 나는 쓰지 않겠다'라는 의미의 Dart 관례적인 이름.(원랜 BuildContext context가 들어감.)
|-
|-
|뒤로 가기
|뒤로 가기
94번째 줄: 93번째 줄:


현재 페이지를 스택에서 제거(pop)하여 이전 화면으로 돌아간다.
현재 페이지를 스택에서 제거(pop)하여 이전 화면으로 돌아간다.
|
|위 코드엔 없지만, 특정 함수에서 진행하면 창이 닫힌다.
|}
|}
=== 결론 ===
스택을 쌓아 화면이 바뀐다.


== 의문 ==
== 의문 ==
110번째 줄: 112번째 줄:
* PageRouteBuilder (전환 애니메이션 직접 커스텀)
* PageRouteBuilder (전환 애니메이션 직접 커스텀)
* go_router, auto_route 같은 라우팅 라이브러리
* go_router, auto_route 같은 라우팅 라이브러리
 
= 기본 하단 네비게이션 바 =
기본 예제는 MaterialPageRoute로 충분하다.
<syntaxhighlight lang="dart">import 'package:flutter/material.dart';
= 기본 하단 네비게이션바 =
<syntaxhighlight lang="dart">
import 'package:flutter/material.dart';


void main() => runApp(MyApp());
void main() => runApp(MyApp());


class MyApp extends StatelessWidget {
class MyApp extends StatelessWidget {
@override
  @override
Widget build(BuildContext context) {
  Widget build(BuildContext context) {
return MaterialApp(
    return MaterialApp(
title: 'BottomNavigation Demo',
      title: 'BottomNavigation Demo',
debugShowCheckedModeBanner: false,
      debugShowCheckedModeBanner: false,
home: HomePage(),
      home: HomePage(),
);
    );
}
  }
}
}


class HomePage extends StatefulWidget {
class HomePage extends StatefulWidget {
@override
  @override
State<HomePage> createState() => _HomePageState();
  State<HomePage> createState() => _HomePageState();
}
}


class _HomePageState extends State<HomePage> {
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0; // 현재 선택된 탭
  int _selectedIndex = 0; // 현재 선택된 탭


// 탭마다 보여줄 화면
  // 탭마다 보여줄 화면
static const List<Widget> _pages = <Widget>[
  static const List<Widget> _pages = <Widget>[ // static은 클래스 변수임을 의미미(인스턴스가 생길 때마다 새로 만들지는 않겠다.)
Center(child: Text('첫 번째 화면', style: TextStyle(fontSize: 24))),
    Center(
Center(child: Text('두 번째 화면', style: TextStyle(fontSize: 24))),
      child: Text(
Center(child: Text('세 번째 화면', style: TextStyle(fontSize: 24))),
        '첫 번째 화면',
];
        style: TextStyle(fontSize: 24),
      ),
    ),
    Center(
      child: Text(
        '두 번째 화면',
        style: TextStyle(fontSize: 24),
      ),
    ),
    Center(
      child: Text(
        '세 번째 화면',
        style: TextStyle(fontSize: 24),
      ),
    ),
  ];


void _onItemTapped(int index) {
  void _onItemTapped(int index) {
setState(() {
    setState(() {
_selectedIndex = index; // 탭 선택 시 상태 변경
      _selectedIndex = index; // 탭 선택 시 상태 변경
});
    });
}
  }


@override
  @override
Widget build(BuildContext context) {
  Widget build(BuildContext context) {
return Scaffold(
    return Scaffold(
appBar: AppBar(title: Text('BottomNavigationBar Demo')),
      appBar: AppBar(
body: _pages[_selectedIndex], // 현재 선택된 화면 표시
        title: Text('BottomNavigationBar Demo'),
bottomNavigationBar: BottomNavigationBar(
      ),
currentIndex: _selectedIndex,
      body: _pages[_selectedIndex], // 현재 선택된 화면 표시
onTap: _onItemTapped, // 탭 클릭 시 호출
      bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
        currentIndex: _selectedIndex,
BottomNavigationBarItem(
        onTap: _onItemTapped, // 탭 클릭 시 호출
icon: Icon(Icons.home),
        items: const <BottomNavigationBarItem>[
label: '홈',
          BottomNavigationBarItem(
),
            icon: Icon(Icons.home),
BottomNavigationBarItem(
            label: '홈',
icon: Icon(Icons.business),
          ),
label: '비즈니스',
          BottomNavigationBarItem(
),
            icon: Icon(Icons.business),
BottomNavigationBarItem(
            label: '비즈니스',
icon: Icon(Icons.school),
          ),
label: '학교',
          BottomNavigationBarItem(
),
            icon: Icon(Icons.school),
],
            label: '학교',
),
          ),
);
        ],
}
      ),
}
    );
</syntaxhighlight>
  }
}</syntaxhighlight>


= 이해를 위한 추가 설명 =
= 이해를 위한 추가 설명 =
184번째 줄: 199번째 줄:


!비고
!비고
StatefulWidget
|-
하단 탭 선택 상태를 관리해야 하므로 HomePage를 StatefulWidget으로 정의
|_onItemTapped
StatelessWidget이면 선택된 탭 정보를 저장할 수 없음
|탭 클릭 시 호출되는 함수.
-
자동으로 Index가 입력된다.
_selectedIndex
|
현재 선택된 탭의 인덱스를 저장하는 상태 변수
|-
탭을 클릭하면 setState()로 값 변경
|BottomNavigationBarType
-
|옵션은?
_pages
 
각 탭에 대응하는 화면 위젯 리스트
* fixed: 모든 탭이 항상 같은 너비로 표시
_selectedIndex를 이용해 화면 전환
* shifting: 선택된 강조, 배경색 변화 가능
-
 
_onItemTapped
기본값은 탭이 4개 이하면 fixed, 4개 이상이면 shifting
탭 클릭 시 호출되는 함수
 
setState() 안에서 _selectedIndex 값을 바꾸어 화면 갱신
기본값은 아래 옵션으로 바꾸어 지정 가능.
-
BottomNavigationBar
하단 네비게이션바 위젯
currentIndex: 선택된 탭 표시, onTap: 탭 클릭 시 동작 지정, items: 각 탭의 아이콘과 라벨
-
currentIndex
현재 선택된 탭을 표시
_selectedIndex와 연결
-
onTap
탭 클릭 이벤트
_onItemTapped 함수 연결
}


  type: BottomNavigationBarType.fixed, // 고정형
|shifting이면 각 탭에 색을 넣어주어야 한다.
backgroundColor: Colors.blue, 형태로.
|}
|}


218번째 줄: 223번째 줄:


=== StatelessWidget으로도 만들 수 없나? ===
=== StatelessWidget으로도 만들 수 없나? ===
탭 선택 상태를 저장할 방법이 없으므로 일반 StatelessWidget으로는 불가능하다.
탭 선택 상태를 저장할 방법이 없으므로 일반 StatelessWidget으로는 불가능하다. 탭 상태를 유지하면서 화면 전환하려면 StatefulWidget을 사용해야 한다.
탭 상태를 유지하면서 화면 전환하려면 StatefulWidget을 사용해야 한다.
 
=== BottomNavigationBarType 옵션은? ===
 
fixed: 모든 탭이 항상 같은 너비로 표시
 
shifting: 선택된 탭 강조, 배경색 변화 가능
 
기본값은 탭이 3개 이상이면 fixed, 3개 이하이면 shifting
 
=== AppBar, Body, BottomNavigationBar 구조는 꼭 이렇게 해야 하나? ===
대부분의 경우 Scaffold 안에서 body와 bottomNavigationBar를 함께 사용한다.
AppBar는 선택 사항이며 필요 없으면 생략 가능.

2026년 1월 7일 (수) 01:50 기준 최신판

틀:플러터 Dart:개요 플러터에 대한 지식 분류

  1. 플러터:개요
    1. 플러터:VSCode
    2. 플러터:안드로이드 스튜디오
  2. 플러터:실행
  3. 플러터:개념 잡기
    1. 플러터:화면 하나 만들기
    2. 플러터:변하는 화면(StatefulWidget)
    3. 플러터:화면 전환(화면 쌓기, 하단 네비게이션 바)
    4. 플러터:화면 전환(Drawer)
    5. 플러터:입력 관련
      1. 플러터:버튼
      2. 플러터:키보드 입력
      3. 플러터:슬라이더
    6. 플러터:그래프 그리기(fl chart)
    7. 플러터:데이터 저장(간단한 데이터)
    8. 플러터:인증(Firebase 인증)(미완)
    9. 플러터:인증(OAuth2)(미완)
  4. 권한 사용
    1. 플러터:마이크 입력
  5. 위젯
    1. 플러터:아이콘
    2. 플러터:레이아웃 계열 위젯
    3. 플러터:네비게이션 계열 위젯
    4. 플러터:버튼
    5. 플러터:상태관리(미완)
  6. 플러터:DB연결
    1. 플러터:Firebase(미완)
    2. 플러터:MySQL(미완)
  7. 디자인
    1. 플러터:테마
    2. 플러터:앱바
  8. 플러터:배포
  9. 플러터:참고자료
  10. 플러터:위젯
    1. 플러터:공간배치용 위젯
  11. 플러터:라이브러리
    1. 플러터:logger

네이게이션을 통한 화면전환.

기본적으로 네비게이션바로 구조를 잡고, 네비게이터로 상세 화면을 다루는 형식을 많이 쓴다.

기본 화면 전환(Navigator)

[편집 | 원본 편집]
  • Flutter 앱에서 화면을 이동하는 가장 기본적인 방식은 Navigator를 사용하는 것이다.
  • Navigator는 “화면(stack)”을 관리하며, 새로운 페이지를 push하여 화면을 열고 pop하여 닫는다.
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Navigator Demo',
      debugShowCheckedModeBanner: false,
      home: FirstPage(),
    );
  }
}

class FirstPage extends StatelessWidget {
  // StatelessWidget: 상태(state)를 가지지 않는 화면
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("첫 번째 화면"),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text("두 번째 화면으로 이동"),
          onPressed: () {
            // 화면 이동 (push)
            Navigator.push(
              context,  // 현재 위젯의 위치가 어디인지.(어디 위에 화면을 올릴 것인지.)
              MaterialPageRoute(builder: (_) => SecondPage()),
            );
          },
        ),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  // 두 번째 화면도 상태를 가지지 않는 StatelessWidget
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("두 번째 화면"),
      ),
      body: Center(
        child: Text(
          "여기는 두 번째 화면!",
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

이해를 위한 추가 설명

[편집 | 원본 편집]
요소 설명 비고
화면을 위에 올리기.

Navigator.push

Navigator.push(
context,
MaterialPageRoute(builder: (_) => SecondPage()),
);

새로운 페이지를 스택 위에 ‘올려서’ 이동한다.

MaterialPageRoute는 기본적인 화면 전환 애니메이션을 제공한다.

마테리얼 디자인 규칙에 따라 뒤로가기가 가능하면 자동으로 뒤로가기 버튼이 생긴다.

_는 '인자를 받긴 해야 하지만, 나는 쓰지 않겠다'라는 의미의 Dart 관례적인 이름.(원랜 BuildContext context가 들어감.)

뒤로 가기

Navigator.pop

Navigator.pop(context)

현재 페이지를 스택에서 제거(pop)하여 이전 화면으로 돌아간다.

위 코드엔 없지만, 특정 함수에서 진행하면 창이 닫힌다.

스택을 쌓아 화면이 바뀐다.

왜 push()는 context가 필요한가?

[편집 | 원본 편집]

Navigator는 위젯 트리 안에 존재하는 “Navigator State”를 찾아야 하는데, 이를 위해 현재 위젯의 BuildContext가 필요하다.

BuildContext는 “현재 위젯이 트리에서 어디 위치하는지”를 나타내는 정보다.

MaterialPageRoute 말고 다른 것도 있나?

[편집 | 원본 편집]

있다.

  • CupertinoPageRoute (iOS 스타일 전환)
  • PageRouteBuilder (전환 애니메이션 직접 커스텀)
  • go_router, auto_route 같은 라우팅 라이브러리

기본 하단 네비게이션 바

[편집 | 원본 편집]
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BottomNavigation Demo',
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedIndex = 0; // 현재 선택된 탭

  // 탭마다 보여줄 화면
  static const List<Widget> _pages = <Widget>[  // static은 클래스 변수임을 의미미(인스턴스가 생길 때마다 새로 만들지는 않겠다.)
    Center(
      child: Text(
        '첫 번째 화면',
        style: TextStyle(fontSize: 24),
      ),
    ),
    Center(
      child: Text(
        '두 번째 화면',
        style: TextStyle(fontSize: 24),
      ),
    ),
    Center(
      child: Text(
        '세 번째 화면',
        style: TextStyle(fontSize: 24),
      ),
    ),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index; // 탭 선택 시 상태 변경
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('BottomNavigationBar Demo'),
      ),
      body: _pages[_selectedIndex], // 현재 선택된 화면 표시
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _selectedIndex,
        onTap: _onItemTapped, // 탭 클릭 시 호출
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: '홈',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: '비즈니스',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: '학교',
          ),
        ],
      ),
    );
  }
}

이해를 위한 추가 설명

[편집 | 원본 편집]
요소 설명 비고
_onItemTapped 탭 클릭 시 호출되는 함수.

자동으로 Index가 입력된다.

BottomNavigationBarType 옵션은?
  • fixed: 모든 탭이 항상 같은 너비로 표시
  • shifting: 선택된 탭 강조, 배경색 변화 가능

기본값은 탭이 4개 이하면 fixed, 4개 이상이면 shifting

기본값은 아래 옵션으로 바꾸어 지정 가능.

  type: BottomNavigationBarType.fixed, // 고정형

shifting이면 각 탭에 색을 넣어주어야 한다.

backgroundColor: Colors.blue, 형태로.

StatelessWidget으로도 만들 수 없나?

[편집 | 원본 편집]

탭 선택 상태를 저장할 방법이 없으므로 일반 StatelessWidget으로는 불가능하다. 탭 상태를 유지하면서 화면 전환하려면 StatefulWidget을 사용해야 한다.