Foggy day

[Flutter] Navigator(화면이동) - 사용법(2부) 본문

Flutter/Flutter widget

[Flutter] Navigator(화면이동) - 사용법(2부)

jinhan38 2023. 4. 1. 00:37

 

 

이번 포스팅은 Navigator 사용법 1부에 이은 2부입니다. 

 

1부를 안 보신 분은 확인해 주시기 바랍니다. 

https://jinhan38.tistory.com/147

 

[Flutter] Navigator- 사용법(1부)

이번 포스팅은 Flutter Navigator의 사용법에 대해 알아보겠습니다. Flutter에서 화면을 이동하는 방법은 개발자마다 다른 것 같습니다. Flutter에서 제공하는 기본적인 Navigator로만 사용하는 경우가 있

jinhan38.com

 

 

1부에서는 Navigator와 MaterialPageRoute를 사용하면서 페이지를 이동했습니다. 이번에는 사전에 경로를 지정해 놓고, 해당 경로를 입력해서 화면을 이동하는 함수를 알아보겠습니다. 

 

 

 

1. MaterialApp 설정

2. pushNamed

3. pushReplacementNamed, pushNamedAndRemoveUntil

4. arguement 전달, 데이터 돌려받기 

 

 

 

 

1. MaterialApp 설정

Named route의 핵심은 route(스크린, 페이지, 하나의 위젯)들을 특정 이름(name)으로 지정해 놓는 것입니다. 이름을 지정하는 곳이 바로 MaterialApp입니다.

initialRoute는 최초에 보여줄 route이고, 반드시 설정해줘야 합니다. 

routes에는 사용할 route들을 전달해줘야 합니다. Map<String, WidgetBuilder>의 타입을 받고 있습니다. 여기서 String은 routeName, WidgetBuilder는 이동할 스크린 위젯입니다. 

WidgetBuilder는 flutter framework에 사전에 정의된  타입으로, Context를 전달하고, Widget을 리턴하는 형태입니다.

typedef WidgetBuilder = Widget Function(BuildContext context);

 

예제에서는 2개의 route를 추가했습니다. routeName "/"를 호출하면 RouteNamedScreen으로, "/first"를 호출하면 RouteFirst 화면으로 이동하게 됩니다. 

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Named route',
      theme: ThemeData(primarySwatch: Colors.blue),
      debugShowCheckedModeBanner: false,
      initialRoute: "/",
      routes: {
        "/": (context) => const RouteNamedScreen(),
        "/first": (context) => const RouteFirst(),
      },
    );
  }

 

 

 

2. pushNamed

그럼 페이지를 이동해보겠습니다. 먼저 MaterialApp에서 선언한 페이지들을 만들어보겠습니다. 

 

 

RouteNamedScreen

RouteNameScreen은 앱을 실행하면 최초에 진입하는 화면입니다. 버튼이 하나 있고, 버튼을 클릭하면 Navigator.pushNamed 함수를 호출하고 있습니다. pushNamed에는 context와 이동할 routeName을 입력해 줍니다.

import 'package:flutter/material.dart';

class RouteNamedScreen extends StatefulWidget {
  const RouteNamedScreen({Key? key}) : super(key: key);

  @override
  State<RouteNamedScreen> createState() => _RouteNamedScreenState();
}

class _RouteNamedScreenState extends State<RouteNamedScreen> {
  String getData = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("RouteNamedScreen"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, "/first");
              },
              child: const Text("Go first, pushNamed"),
            ),
          ],
        ),
      ),
    );
  }
}

 

RouteFirst

RouteFirst는 MaterialApp에서 routeName을 "/first"로 지정해 줬습니다. 

여기서는 뒤로 되돌아가는 기능인 Navigator.pop을 호출하는 버튼이 하나 있습니다. 

import 'package:flutter/material.dart';

class RouteFirst extends StatelessWidget {
  const RouteFirst({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("RouteFirst"),
      ),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: Text("Pop")),
          ],
        ),
      ),
    );
  }
}

 

 

 

 

 

3. pushReplacementNamed, pushNamedAndRemoveUntil

pushReplacementNamed의 사용방법은 pushName와 동일합니다. 기능은 현재의 페이지를  새로 입력한 routeName의 페이지로 교체하는 것입니다. 

ElevatedButton(
  onPressed: () {
    Navigator.pushReplacementNamed(context, "/first");
  },
  child: const Text("pushReplacementNamed"),
),

 

pushNamedAndRemoveUntil는 pushAndRemoveUntil과 동일한 기능을 합니다. 특정 페이지로 이동하고 기존의 모든 페이지를 종료합니다. 아래 예제의 경우에는 routeName "/first"로 이동하고, 기존의 페이지들은 모두 종료합니다.

ElevatedButton(
  onPressed: () {
    Navigator.pushNamedAndRemoveUntil(
      context,
      "/first",
      (route) => false,
    );
  },
  child: const Text("pushNamedAndRemoveUntil"),
),

 

 

 

4. arguement 전달, 데이터 돌려받기 

named route를 사용해서 데이터를 전달할 수도 있고, pop으로 돌아올 때 데이터를 돌려받을 수도 있습니다.

 

먼저 pushNamed함수를 호출하면서 argument를 전달해 보겠습니다. routeName을 입력한 후에 arguments 인자에 전달하고 싶은 데이터를 넣어줍니다. String, int custom model 타입 전부 가능합니다. 

pushNamed 함수의 return Type은 Future<T?> 입니다. 리턴 타입을 지정해주지 않아서 dynamic으로 받았지만 지정해줄 수도 있습니다. 물론 지정해준다면 해당 타입을 잘 지켜주는 것이 필요합니다. 

RoutePushArgument 화면에서 pop을 할 때 데이터를 보낸다면 result 변수에 값이 담기겠지만, 데이터를 보내지 않는다면 null이 들어옵니다. 

ElevatedButton(
  onPressed: () async {
    dynamic result = await Navigator.of(context)
        .pushNamed("/pushArgument", arguments: "pass argument");
    setState(() {
      getData = result.toString();
    });
  },
  child: const Text("pushArgument"),
)

 

그다음 RoutePushARgument 페이지를 만들겠습니다.

build함수의 첫째줄에서 보면 ModalRoute.settings.arguments를 이용해서 데이터를 받고 있습니다. 

그리고 버튼을 클릭하면 pop함수를 호출하고, "데이터 전달"이라는 String 값을 되돌려주고 있습니다. 

import 'package:flutter/material.dart';

class RoutePushArgument extends StatelessWidget {
  const RoutePushArgument({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments;
    return Scaffold(
      appBar: AppBar(
        title: const Text("RoutePushArgument"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("argument : $args"),
            ElevatedButton(
                onPressed: () {
                  Navigator.of(context).pop("데이터 전달");
                },
                child: const Text("페이지 종료 및 데이터 전달"))
          ],
        ),
      ),
    );
  }
}

 

마지막으로 MaterialApp에서 route를 추가해 줍니다.

"/pushArgument": (context) => const RoutePushArgument(),

 

 

 

 

추가적인 내용

named route를 사용했을 때의 편리함과 명확함이 있지만, 특정 route만 remove 하는 것이 불가능합니다(방법이 있다면 댓글로 알려주시면 감사하겠습니다). 때문에 새로운 화면으로 이동할 때 기존에 떠 있는 동일한 화면들 제거하고, 새로운 화면으로 이동시키는 것이 불가능합니다. 다시 말해서 동일한 화면이 여러 개 존재할 수 있다는 것입니다. 

그래서 특정 route들을 다양하게 다뤄야 하는 애플리케이션이라면 MaterialRoute를 사용하는 방법이 좋을 것 같습니다. MaterialRoute를 사용하면 화면 이동시 기존에 동일한 화면이 떠 있다면 제거한 후 이동할 수 있습니다.