메뉴 닫기

Flutter 화면 전환 오류 – Navigator operation requested with a context that does not include a Navigator

Flutter에서 “Navigator operation requested with a context that does not include a Navigator. The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget…”라는 오류 메시지의 원인 및 해결 방법을 소개하도록 하겠습니다.

원인

Flutter에서 route는 프로그램의 화면 또는 페이지를 나타냅니다. 앱에서 화면 사이를 이동할 때 기본적으로 Navigator stack에서 route를 push 및 pop 동작을 통해 전환됩니다. 

Navigator는 스택 방식으로 하위 위젯 집합을 관리하는 위젯입니다. Navigator에서 route를 push하거나 pop하려면, 위젯 트리에서 Navigator 위젯의 하위 항목인 context가 있어야 합니다. 이는 Navigator가 스택을 올바르게 관리할 수 있도록 push 또는 pop되는 route의 상위 위젯이 무엇인지 알아야 하기 때문입니다.

이 오류 메시지는 일반적으로 Navigator 위젯의 하위 항목이 아닌, 다른 context를 사용하여 Navigator에서 route를 push 또는 pop하려고 할 때 발생합니다. 이 오류를 수정하려면 route를 push/pop하는 데 사용하는 context가 Navigator 위젯의 자손인지 확인해야 한다고 합니다.

저도 처음에 그랬지만, 위 설명 만으로는 오류 메세지를 이해하는 데 어려움이 있을 듯합니다. 그래서 예제 코드를 보여드리도록 하겠습니다.

예제 코드

두 개의 페이지(Page 1, Page 2)를 만들고, 버튼을 눌러 페이지 간에 이동하는 간단한 예제입니다.

문제 발생 예제

import 'package:flutter/material.dart';

void main() {
  runApp(const Page1());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Page 1'),
        ),
        body: Center(
          child: ElevatedButton(
            child: Text('Go to Page 2'),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => Page2()),
              );
            },
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 2'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go back'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

빨간색 메시지에 의하면 Navigator를 포함하지 않는(Navigator가 null인!) context에서 navigator 작업이 요청되었다고 하는데요, Page 2로 이동하는 데 사용되는 page 1의 buildContext 객체가 Navigation 위젯을 포함하지 않았기 때문입니다.

Navigator를 생성하는 가장 일반적인 방법은 MaterialApp 위젯을 생성하는 것입니다. MaterialApp의 home 항목은 Navigator stack에서 가장 아래에 위치한 route가 됩니다. 그런데 Page 1의 build() 메서드를 보면 MaterialApp을 분명 생성했는데 말이죠?!

Page 1이 MaterialApp을 인스턴스화 하는 위젯이라서 Page 1의 BuildContext에 MaterialApp을 포함하지 않는다고 합니다. 그래서 Navigation 위젯도 포함되지 않았습니다. 아래 이미지는 문제 발생 예제와 정상 동작 예제의 각각 context를 확인해 본 것인데, 문제 발생 예제의 parent는 null이고 MaterialApp은 child로 들어가 있습니다.

정상동작 예제에서는 MyHomePage 위젯을 만들고 여기에서 MaterialApp을 생성한 다음, home에서 Page 1을 호출합니다(정상 동작 예제 코드는 본문 제일 마지막에 있습니다). 정상 동작 예제의 context는 parent의 depth가 112에 달하고, parent를 한참 열어보면 WidgetsApp과 Navigator를 확인할 수 있습니다.

정상 동작 예제

MyHomePage라는 MaterialApp이 포함된 StatelessWidget을 생성하고, 여기에서 Page 1을 불러옵니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyHomePage());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Page1(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 1'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go to Page 2'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => Page2()),
            );
          },
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 2'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go back'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다