본문으로 바로가기

Navigation and routing

category 📱 Mobile/📱 (old) Flutter v 1.0 2020. 5. 15. 12:11
  • Navigator.push
  • Navigator.pop
  • Navigator.pushNamed
  • 이 외에도 pushReplacement 등 다양한 메서드가 있습니다. 히스토리 관리를 위해서 적절하게 사용합시다.

 

 

쿡북을 참고하는게 제일 좋다.

 

 

Navigate to a new screen and back

How to navigate between routes.

flutter.dev

 

사용한 위젯의 상세는 다음 링크에서 참조하자.

 

https://flutter.dev/docs/development/ui/navigation

https://api.flutter.dev/flutter/widgets/Navigator-class.html

 

 

 

모바일 앱은 일반적으로 페이지를 통해 콘텐츠를 보여줍니다. Flutter에서 이러한 요소를 Route라고하며 네비게이터 위젯으로 관리합니다 . 네비게이터는 Route 객체 스택을 관리하기 위해 스택을 관리하는 두 가지 방법을 사용합니다. declarative API인 Navigator.pages, imperative API인 Navigator.push  Navigator.pop이 그것입니다.

 

그러니까, Stack에 Route가 쌓일 것이니 Route 객체를 넣는 push 메서드가 있고, Route 객체를 빼내는 pop 메서드가 있는 것입니다. pop이란 메서드의 이름에서 알 수 있듯이, 스택의 마지막 Route를 빼내는 역할을 합니다.

 

Navigation을 통해 다른 Route로 이동하는 것처럼 보이지만 사실 push를 통해 그 위에 페이지가 덧씌워지는 것입니다. 이는, 다른 화면으로 이동해도 그 위에 화면이 메모리에 남아 있다는 말과 동일합니다. 자료구조적으로는 "스택(stack)" 입니다. 그래서 뒤로가기 버튼을 누르게 되면, 본래 페이지로 돌아가게 됩니다. 

 

push와 pop을 사용하여 간단한 Navigation을 만들어 보겠습니다.

 

 

push는 컨텍스트와 route를 인자로 받습니다. 반면 pop은 컨텍스트만 인자로 받습니다.

Navigator.push(context, route)
Navigator.pop(context)

 

// Within the `FirstRoute` widget
onPressed: () {
  Navigator.push(
    context,
    // 어디서 push할 것인가? context(현재 컨텍스트)
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );
}

// Within the SecondRoute widget
onPressed: () {
  // 무엇을 pop할 것인가? context(현재 컨텍스트)
  Navigator.pop(context);
}

 

 

 

새로운 화면에 값 전달하기

 

1. push일 때 다음 페이지에 값 전달하기

 

SecondPage에 person이라는 인자의 이름에 person이란 값을 전달했습니다.

final person = new Person(name: "사람", age: 20);

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

 

이를 받아주기 위해서 Second Page 위젯 클래스의 생성자에 해당 값을 받을 수 있도록 설정해줘야겠죠.

Dart의 클래스를 잘 떠올려보면, 쉬울겁니다.

class SecondPage extends StatelessWidget {

  final Person person;

  const SecondPage({Key key, this.person}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
	... 이하 생략

 

 

2. pop일 때 이전 화면으로 값 전달하기

 

pop의 두번째 인자로 전달하고 싶은 값을 넣으면 됩니다

Navigator.pop(context, "ok");

 

이를 받은 페이지에서는 pop을 통해 되돌아가는 페이지에서는 해당 페이지로 이동시킨 push의 값을 비동기적으로 받아오면 됩니다. Navigator.push는 Future 타입의 반환 타입을 가지는데 이를 받아주면 되는 겁니다. 

함수를 async/await 처리하고 값을 받으면 됩니다.

onPressed: () async{
  final result = await Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => SecondPage(person: person),
    ),
  );
  print(result);
},

 

 

Navigate with named routes

 

https://flutter.dev/docs/cookbook/navigation/named-routes

 

 

그런데, 대개의 앱은 이동하는 경로가 복잡합니다. 적어도 위 예시처럼 2개의 페이지만을 왔다갔다 하지 않습니다. 여러 페이지를 이동하는 멀티 페이지를 사용하려면 MaterialApp 단위에서부터 이름이 있는 route를 사용하도록 손을 봐야 합니다.

 

route는 Map 자료구조를 가지고 있는 MaterialApp의 파라미터입니다.

 

initialRoute를 통해 처음 시작할 때 렌더할 페이지를 지정하고, routes를 통해 각 페이지의 경로를 설정했습니다. routes를 이용해 이동하기 위해서는 Navigator.pushNamed를 사용하면 됩니다.

 

* 컨텍스트에 주의합시다.

* /를 사용하면 반드시 initialRoute는 '/'여야만 합니다. 아니면 오류를 냅니다.

 

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => Screen0(),
        '/first': (context) => Screen1(),
        '/sec': (context) => Screen2(),
      },
    );
  }
}


/// Navigator.pushNamed

            RaisedButton(
              color: Colors.red,
              child: Text('Go To Screen 1'),
              onPressed: () {
                //Navigate to Screen 1
                Navigator.pushNamed(context, '/first');
              },
            ),

 

Routes with the Static Const

 

참고로, /가 아니라 단순한 문자열도 가능하다. 

위젯에 static 변수를 줘서 직접 문자열을 하드코딩하지 않음으로 실수를 줄일 수도 있다. (static이 아니면 인스턴스를 만들어야 하는데 이건 리소스 낭비다) 

 

아니면 route 변수를 하나 만들어서 주던가.

final route = MaterialPageRoute(builder: (context) => SignInPage());

 

그러나 무엇을 사용하든, 일관성있게 하자. '/'를 사용하면 그 방식을 계속 쓰고, 문자열을 쓰면 계속 그 방식을 써야 한다.

 

      initialRoute: WelcomeScreen.id,
      routes: {
        WelcomeScreen.id: (context) => WelcomeScreen(),
        'register': (context) => RegistrationScreen(),
        'login': (context) => LoginScreen(),
        'chat': (context) => ChatScreen(),
      },

// static
class WelcomeScreen extends StatefulWidget {
  static const id = 'welcome';

// 활용
Navigator.pushNamed(context, 'register');

 

 

화면이 스택으로 쌓인다는 것은 웹에서 히스토리가 작동하는 방식과 비슷합니다. 여기서 주의할 점은, 로그인/아웃을 했을 때 뒤로가기 버튼을 눌러 다시 화면으로 진입하는 것을 막는 것입니다.

 

로그인을 했다 => 메인 페이지에 왔다. => 뒤로가기 버튼을 누른다 => 다시 로그인 화면?

로그아웃을 했다. => 로그인 페이지에 왔다 => 뒤로가기 버튼을 누른다 => 다시 로그아웃 화면?

 

이상하다.

 

따라서 히스토리 스택을 아예 지윅 위해 push가 아니라 pushrerplacement를 해주었습니다.

quick doc을 읽어보면

 

Replace the current route of the navigator that most tightly encloses the given context by pushing the given route and then disposing the previous route once the new route has finished animating in.

 

그러니까 replace한다는 말이네요

이를 이용해서 로그아웃 버튼을 누르면 pusheplacement를 하도록 해보았습니다.

 FlatButton.icon(
   onPressed: () {
     final route = MaterialPageRoute(builder: (context) => SignInPage());
     Navigator.pushReplacement(context, route);
   },
   icon: Icon(Icons.exit_to_app),
   label: Text("Logout",
       style: TextStyle(
           color: Colors.black87, fontWeight: FontWeight.w500)),
 )

 

아래 예시를 보시면, pushReplace를 한 경우 이동한 다음 뒤로가기 버튼을 누르면 쌓인 히스토리가 없어서 그냥 앱이 내려가게 되는 반면 일반 push를 사용한 경우 다시 전 페이지로 돌아가는 것을 보실 수 있습니다.

 

넵. 이겁니다.

 

 

 

 

pushReplace의 경우

 

일반 push인 경우


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체