본문 바로가기
Language_/Flutter

[Flutter] Navigator & Named Route"화면 전환의 시작"

by 낭람_ 2020. 9. 30.
반응형

[Flutter] 

 

Navigator는 앱 화면 간 이동을 구현할 때 사용한다.

 

Navigator는 스택 개념으로 작동을 한다. 즉 Last In First Out 특징을 갖고 있는데,

 

만약 first screen, second screen, third screen 3개의 화면이 존재할 때,

 

first screen - second screen - third screen으로 이동을 했다면

 

third screen - second screen - first screen 화면으로 나와야 한다는 의미이다.

0

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

Navigator.of(context).push(
    MaterialPageRoute(
        builder: (context) => routeClass(),
    ),
);
Navigator.pop(context);

Navigator.of(context).pop();

 

위 두개의 코드로 Screen을 push, pop 할 수 있다.

 

아래의 간단한 예제를 살펴보자.

 

0

import 'package:flutter/material.dart';

void main() => runApp(
      MaterialApp(
        title: 'Navigator',
        home: FirstScreen(),
      ),
    );

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FirstScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go Second Screen'),
          onPressed: () {
            Navigator.push(context,
                MaterialPageRoute(builder: (context) => SecondScreen()));
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SecondScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go First Screen'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

 

위는 전체적인 코드다.. 아래를 보면서 코드를 이해해보자.

 

스크린은 두 개로 만들었다.

 

메인 함수는 아래와 같이 되어있다.

void main() => runApp(
      MaterialApp(
        title: 'Navigator',
        home: FirstScreen(),
      ),
    );

 

home을 통하여 시작 스크린을 변경할 수 있다. 

    - home Screen에서 pop을 하면 화면이 검은색으로 변한다.

 

첫 번째 스크린 내용이다.

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FirstScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go Second Screen'),
          onPressed: () {
            Navigator.push(context,
                MaterialPageRoute(builder: (context) => SecondScreen()));
          },
        ),
      ),
    );
  }
}

 

push를 통해서 두 번째 스크린으로 이동을 한다.

 

두 번째 스크린 내용이다.

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SecondScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go First Screen'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

 

pop을 통해서 다시 첫 번째 스크린으로 이동을 한다.

 

 

관련 오류

══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
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.
When the exception was thrown, this was the stack:
#0      Navigator.of.<anonymous closure> 
package:flutter/…/widgets/navigator.dart:2190
#1      Navigator.of 
package:flutter/…/widgets/navigator.dart:2197
#2      Navigator.push 
package:flutter/…/widgets/navigator.dart:1801
#3      FirstScreen.build.<anonymous closure> 
package:hello_flutter/main.dart:18
#4      _InkResponseState._handleTap 
package:flutter/…/material/ink_well.dart:992
#5      _InkResponseState.build.<anonymous closure> 
package:flutter/…/material/ink_well.dart:1098
#6      GestureRecognizer.invokeCallback 
package:flutter/…/gestures/recognizer.dart:184
#7      TapGestureRecognizer.handleTapUp 
package:flutter/…/gestures/tap.dart:524
#8      BaseTapGestureRecognizer._checkUp 
package:flutter/…/gestures/tap.dart:284
#9      BaseTapGestureRecognizer.handlePrimaryPointer 
package:flutter/…/gestures/tap.dart:219
#10     PrimaryPointerGestureRecognizer.handleEvent 
package:flutter/…/gestures/recognizer.dart:477
#11     PointerRouter._dispatch 
package:flutter/…/gestures/pointer_router.dart:78
#12     PointerRouter._dispatchEventToRoutes.<anonymous closure> 
package:flutter/…/gestures/pointer_router.dart:124
#13     _LinkedHashMapMixin.forEach  (dart:collection-patch/compact_hash.dart:377:8)
#14     PointerRouter._dispatchEventToRoutes 
package:flutter/…/gestures/pointer_router.dart:122
#15     PointerRouter.route 
package:flutter/…/gestures/pointer_router.dart:108
#16     GestureBinding.handleEvent 
package:flutter/…/gestures/binding.dart:220
#17     GestureBinding.dispatchEvent 
package:flutter/…/gestures/binding.dart:200
#18     GestureBinding._handlePointerEvent 
package:flutter/…/gestures/binding.dart:158
#19     GestureBinding._flushPointerEventQueue 
package:flutter/…/gestures/binding.dart:104
#20     GestureBinding._handlePointerDataPacket 
package:flutter/…/gestures/binding.dart:88
#24     _invoke1  (dart:ui/hooks.dart:267:10)
#25     _dispatchPointerDataPacket  (dart:ui/hooks.dart:176:5)
(elided 3 frames from dart:async)
Handler: "onTap"
Recognizer:
  TapGestureRecognizer#24119
════════════════════════════════════════════════════════════════════════════════════════════════════

 

MaterialApp을 빌드한 클래스에서 navigator를 사용한다면 위의 오류가 뜰 것이다.

 

MainScreen    <------ context
  --> MaterialApp
   (--> Navigator built within MaterialApp)
      --> Scaffold
        --> App Bar
          --> ...
        --> Center
          --> FlatButton

stackoverflow.com/questions/50124355/flutter-navigator-not-working/50125296

 

Flutter Navigator not working

I have app with two screens, and I want to make push from 1st to second screen by pressing button. Screen 1 import 'package:flutter/material.dart'; import './view/second_page.dart'; void main() =&...

stackoverflow.com

 

위의 오류가 뜬다면 main에 MaterialApp을 넣으면 간단히 해결이 된다.

 

void main() => runApp(
      MaterialApp(
        title: 'Navigator',
        home: FirstScreen(),
      ),
    );

 

이번에는 Named Route에 대해 알아보자.

 

MaterialApp에 routes 속성이 있어 각 위젯 별로 이름을 지정하여 그 이름으로 화면을 이동할 수 있는 기능이다.

 

0

import 'package:flutter/material.dart';

void main() => runApp(
      MaterialApp(
        title: 'Navigator',
        initialRoute: '/',
        routes: {
          '/': (context) => FirstScreen(),
          '/second': (context) => SecondScreen(),
          '/third': (context) => ThirdScreen(),
        },
      ),
    );

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FirstScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go Second Screen'),
          onPressed: () {
            Navigator.pushNamed(context, '/second');
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SecondScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go Third Screen'),
          onPressed: () {
            Navigator.pushNamed(context, '/third');
          },
        ),
      ),
    );
  }
}

class ThirdScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ThirdScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Pop!'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

 

위는 전체적인 코드다.

 

 

push, pop을 하며 이동하는 기능은 Navigator와 똑같다.

 

이번에는 Navigator때와 달리 스크린 3개로 만들었다. 

 

void main() => runApp(
      MaterialApp(
        title: 'Navigator',
        initialRoute: '/',
        routes: {
          '/': (context) => FirstScreen(),
          '/second': (context) => SecondScreen(),
          '/third': (context) => ThirdScreen(),
        },
      ),
    );

 

메인 함수에는 initialRoute와 routes 속성이 추가되었다.

 

initialRoute속성은 처음 위치를 무엇으로 할지 정하는 속성이다.

 

Named routed에서는 '/'가 home을 대체하며 home과 '/'를 같이 사용할 수 없다.

    - 같이 사용하면 오류가 나타난다.

 

또한, routes를 통하여 이름을 지정할 수 있기 때문에 Named Routes라고 부른다.

 

void main() => runApp(
      MaterialApp(
        title: 'Navigator',
        initialRoute: '/second',
        routes: {
          '/': (context) => FirstScreen(),
          '/second': (context) => SecondScreen(),
          '/third': (context) => ThirdScreen(),
        },
      ),
    );

 

initialRoute를 /second로 했을 때의 화면이다. 시작이 second지만 home은 first로 되어있다.

 

0

 

첫 번째 스크린 내용이다.

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FirstScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go Second Screen'),
          onPressed: () {
            Navigator.pushNamed(context, '/second');
          },
        ),
      ),
    );
  }
}

 

Navigator와는 달리 pushNamed를 통하여 이동하는 것을 볼 수 있다.

 

세 번째 스크린 내용이다.

class ThirdScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ThirdScreen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Pop!'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

 

pop 할 때는  Navigator와 같이 pop을 사용하여 이전으로 돌아간다.

 

위의 Navigator와 NamedRoute기능을 통하여 화면 전환을 자유롭게 할 수 있다.

반응형

댓글