본문 바로가기

카테고리 없음

Nested Navigation

MaterialApp에서 routes를 설정하여 Navigation을 잘 이용하고 있었다. Provider와 같이 이용하고 있었는데 회원가입할 때 정보를 여러 페이지로 나누어서 입력을 받아 회원가입을 진행하는 것으로 되었다. sigup_first_page와 signup_second_page가 있을 때 sigup_first_page에서 signup_provider를 생성하여 사용하니 signup_first_page에서 Navigaion으로 second페이지로 이동 후 해당 provider를 사용하려고 하니 아래와 같은 에러가 발생하였다.

해당 페이지의 BuildContext에서 Provider를 찾을 수 없다는 내용이다. Flutter inspector를 통해서 에러의 원인을 찾아보자

First 페이지에서 pushNamed로 Second 페이지로 이동을 했다고해서 Widget tree 상에서 부모-자식 관계가 아니라 같은 레벨의 Widget이라는 것을 확인할 수 있다. 그렇기 때문에 First페이지에서 생성한 Provider를 Second 페이지에서 사용할 수 없었다.

MyApp 이나 MaterialApp 기준으로 Provider를 생성을 하면 first 페이지에서도 second 페이지에서도 사용할 수 있지만 signup과 관련없는 페이지에서도 접근할 수 있게 된다. Nested Navigator을 알게 되어 이것을 적용하여 해결해보려고 한다. Nested Navigator은 Multi Navigator 라고 이해하면 된다.

 

디렉토리 구조는 아래와 같이 했다.

app.dart (top level route)

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/' : (BuildContext context) => HomePage(),
        '/signup' : (BuildContext context) => SignupPage(),
      },
    );
  }
}

home_page

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('home'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pushNamed('/signup');
          },
          child: const Text(
            'sign up',
            style: TextStyle(fontSize: 20),
          ),
        ),
      ),
    );
  }
}

signup_provider

class SignupProvider extends ChangeNotifier {

  final navigatorKey = GlobalKey<NavigatorState>();

  int _count = 0;

  int get count => _count;

  set count(int value) {
    _count = value;
    notifyListeners();
  }
}

 

signup_page

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => SignupProvider(),
      child: Builder(
        builder: (context) {
          return Navigator(
            key: context.read<SignupProvider>().navigatorKey,
            initialRoute: 'first',
            onGenerateRoute: (RouteSettings settings){
              late Widget page;
              switch(settings.name){
                case 'first' :
                  page = SignupFirstDepthPage();
                  break;
                case 'second' :
                  page = SignupSecondDepthPage();
                  break;
                default:
                  throw Exception('Invalid route: ${settings.name}');
              }
              return MaterialPageRoute(
                builder: (context) {
                  return page;
                },
                settings: settings,
              );
            },
          );
        }
      ),
    );
  }
}

signup_first_depth_page

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(onPressed: (){
          Navigator.of(context).pushNamed('second');
        }, child: const Text('다음', style: TextStyle(fontSize: 20),),),
      ),
    );
  }
}

signup_second_depth_page

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('sign up second page ${context.watch<SignupProvider>().count}'),
      ),
    );
  }
}

 

하드웨어 back button으로 뒤로 가면 second 페이지에서 first페이지로 가는게 아니라 HomePage로 이동한다. 그래서 WillPopScope 위젯을 이용해서 제어해야 할 것 같다.