ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter: Navigator, BuildContext
    공부 기록/Flutter 2021. 11. 21. 16:04
    728x90

     

    routes:  스마트폰 앱에서 보여지는 화면,페이지 (Android로 따지면 activity), Navigator 위젯으로 관리. 스텍이라는 자료구조 형식으로 라우터 개체들을 관리. push(data를 스택에 쌓는), pop method (스텍의 데이터 삭제) 제공

    https://www.youtube.com/watch?v=BWG9XS5ecig&list=PLQt_pzi-LLfpcRFhWMywTePfZ2aPapvyl&index=22 

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

     

    Navigator class - widgets library - Dart API

    A widget that manages a set of child widgets with a stack discipline. Many apps have a navigator near the top of their widget hierarchy in order to display their logical history using an Overlay with the most recently visited pages visually on top of the o

    api.flutter.dev

    route는 반드시 Material app 아래에 child로 생성되어야함.  모든 위젯은 Material app이 감싸고 있어야함.

    여기서 Firstpage가 route임. 연결ㅇ

    스택 구조로 화면에 첫번째 화면 위로 두번째 화면을 쌓는(스텍) 구조로 되어있음. 라우트를 담당하고 있는 Navigator 위젯의 push 메소드를 호출하면 context를 전달해야함.  context를 통해 context가 가지고 있는 위젯 주변의 위치정보에 근거해 현재 화면에 보여지는 페이지가 어떤 페이지인지 확인하고 푸시(새로운 루트를 쌓아올리는)함. 첫번째 페이지위의 위치를 정학히 알아서 그 위에 두번째 페이지를 쌓기 위함이다. 아래 하이라이트된 context의 이름이 같아야함 

    - route에는 플러터가 기본으로 제공하는 MaterialPageRoute 위젯을 불러옴. ()을 붙여 생성자 불러와 인스턴스를 생성하게 됨.  생성자는 아규먼트를 전달해야하는데 이 위젯은 빌드를 아규먼트로 가지고 있음. 

    - builder는 MaterialPageRoute 생성자를 사용할때마다 반드시 불러와야 하는 필수요소. MaterialPageRoute 생성자를 통해 어떤 위젯이 생성되어야할지 정의하는 것. 왜 사용해야할까? 곧바로 라우트 호출해서 일어날 수 있는 에러를 미리 방지하기 위한 안전장치. 빌더에서 제공되는 컨텍스트가 플러터에서 자동으로 할당되지만,  context를 통해 필요에 따라 사용할수도 사용하지 않을수 있음. 사용하지 않고 싶으면 매게변수로 언더스코어 전달. 

    builder: (_) => function()

    - builder: 내에는 메소드처럼 BuildContext type에 context를 인자값으로 전달  

    - return 에는 두번째 페이지를 반환하도록 두번째 페이지의 함수명 적기

    - 함수 뒤에는 ;으로 마무리

    더 간단히, MaerialPageRoune()호출하면 builder (required argument)자동완성됨 -> flutter가 원하는 context를 선택 -> => 호출할 페이지 선택 

    두번째 화면->첫번쨰화면으로 돌아올떄는 두번째화면 라우트를 없애는 pop 메소드만 이용하면됨. 자신의 정보를 가지고 있는 context명만 인자로 전달하면 끝. pop method는BuildContext의 컨텍스트(아래 그림의 3번째 줄 ctx)를 전달받아서 실행되기 때문에 builder를 별도로 전달하지 않아도됨. 사실 두번쨰 페이지를 생성하면 뒤로가기 버튼이 자동으로 생성되어, 굳이 첫번째로 돌아가는 버튼을 만들진 않아도됨. 

     

     

    *매개변수와 인자의 차이.
     - 어떤 값을 전달받는 변수를 매개변수(parameter)
      ㄴ addNum(int a, int b)
     - 함수에 직접적을 전달되는 변수값은 인자(argument)
      ㄴ addNum(3,4)

     

     

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: FirstPage()
        );
      }
    }
    
    class FirstPage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Fist page'),
          ),
          body: Center(
            child: ElevatedButton(
              child: Text('Go to 2nd page'),
              onPressed: (){
                Navigator.push(context, MaterialPageRoute(
                    builder: (context) => SecondPage()
                )
                );
              },
            ),
          ),
        );
      }
    }
    
    
    
    class SecondPage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('second page'),
          ),
          body: Center(
            child: ElevatedButton(
                child: Text('Go to 1nd page'),
                onPressed: (){
                  Navigator.pop(context);
                }),
          ),
        );
      }
    }

     

    -

    멀티페이지 이동 기능 구현 시 반드시 필요한 기능. 
    - this.initialRoute: 멀티페이지 이동 시 화면에 제일 처음 출력되는 라우터를 불러오는 역할. 따라서 멀티페이지 구현시에는 home 대신 라우터 아규먼트를 사용함. (두 개 동시에 사용할 수 없음) 
    - this.routes: 이동할 페이지들의 이름지정하고 생성하는 역할. String과 WidgetBuidler가 한쌍으로 전달되는 맵이라는 자료형을 가져야하마. (String이 키 값, WidgetBuidler가 값(value) -> key를 호출하면 값이 

    * Map 자료 구조
     key: value 쌍을 이루어준 구조. 


    initlaRoute의 경우  처음 출력될 라우트의 이름은  '/'로 지정해줌.

    라우트의 이름은 슬래시 뒤에 원하는 이름 지정

    // main.dart
    
    import 'package:flutter/material.dart';
    import 'screenA.dart';
    import 'screenB.dart';
    import 'screenC.dart';
    
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          // home: ScreenA(), // route의 경우에는 home 사용하지않음
          initialRoute: '/',
          routes: {
            '/' : (contexet) => ScreenA(), // 키값인 /가 불려지면 /의 value인 screenA가 빌드됨
            '/b': (contexet) => ScreenB(),
            '/c': (contexet) => ScreenC()
          },
        );
      }
    }

    강좌에서 사용된 RaisedButton은 derecated되어서 나는 OutlineButton 사용함.

    // screenA.dart
    
    import 'package:flutter/material.dart';
    
    class ScreenA extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('ScreenA'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                OutlinedButton(
                   child: Text('Go to ScreenB'),
                    onPressed: (){
                      Navigator.pushNamed(context, '/b');
                       },
                  ),
                OutlinedButton(
                  child: Text('Go to ScreenC'),
                  onPressed: (){
                    Navigator.pushNamed(context, '/c');
                  },
                )
              ]
            ),
          ),
        );
      }
    }

    #응용.

    - 버튼과 버튼 사이에 SizedBox 추가하여 버튼 간격 넓힘. 두번째 버튼은 ElevatedButton

     

    ElevatedButton constructor - ElevatedButton class - material library - Dart API

    const ElevatedButton({Key? key, required VoidCallback? onPressed, VoidCallback? onLongPress, ButtonStyle? style, FocusNode? focusNode, bool autofocus = false, Clip clipBehavior = Clip.none, required Widget? child} ) Create an ElevatedButton. The autofocus

    api.flutter.dev

    - ElevatedButton을 추가하고 속성 지정. ElevatedButton의 스타일 지정은 styleFrom()이용하기 + 배경색상은 primary 속성 이용하기

     body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                OutlinedButton(
                   child: Text(
                     'Go to ScreenB',
                     style: TextStyle(fontSize: 15.0), // 버튼 내 텍스트 스타일
                    ),
                  style: OutlinedButton.styleFrom(
                    primary: Colors.orangeAccent
                  ),
                  onPressed: (){
                    Navigator.pushNamed(context, '/b');
                  }
                ),
                SizedBox(width: 30.0 ,height: 30.0,),
                ElevatedButton(
                  child: Text('Go to ScreenC'),
                  style: ElevatedButton.styleFrom(
                    primary: Colors.amberAccent
                  ),
                  onPressed: (){
                    Navigator.pushNamed(context, '/c');
                  },
                )
              ]
            ),
          ),

    BuildContext에 대해 더 자세히 알아보기 (참고 강좌)

    #1.

    build method를 통해 위젯 구조를 만들어감. 

    모든 위젯은 타입을 가지고 있음.  예) int addNumber(int a, int b)
    build method는 Widget 타입인 것임. 그리고 build method는  (아래 밑줄친) BuildContext 타입의 context 인자를 가짐. 즉 BuildContext 타입의 context 인자를 대입한 Widget 타입의 Scaffold를 리턴한다는 의미!!

    build method는 Scaffold Widget을 리턴한다. 이 때, 위젯트리에서 어디에 위치하느냐에 따른 정보를 가진 context를 인자로 받는다. 

    #2.

    아래 커스텀 위젯에서 자신만의 BuildContext type의 context를 가지고, Scaffold widget을 반환한다. 이 때 Scaffold 위젯은 부모인 MyPage 위젯의 context를 그대로 물려받는다.

     

    Scaffold 위젯의 context는 물려받은 Mypage widget의 context와 동일.

    댓글

Designed by Tistory.