스터디/Flutter+Dart

Flutter Screen(Route,Activity) 컨트롤 (화면 이동, Data전달)

Dalmangyi 2019. 7. 26.

Android 에선 Activity, iOS에선 UIViewController 로 창을 만들고 이동하는데 Flutter에선 어떻게 하는지 알아보겠습니다

 

화면을 만드는 방법,

다른 화면 이동시 애니메이션 방법

화면끼리 데이터를 주고 받는 방법

 

1. 화면 준비

우선 화면을 준비해보겠습니다 

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Open route'),
          onPressed: () {
            // Navigate to second route when tapped.
          },
        ),
      ),
    );
  }
}

class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // Navigate back to first route when tapped.
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

첫번째 화면(왼), 두번째 화면(오)

 

 

2. 다음 화면으로 이동하기

Navigator의 push 기능을 이용해서 다음페이지로 이동할 수 있습니다.

자동으로 다음페이지에는 AppBar부분에 뒤로가기가 생기고, 동작도 잘 되는걸 볼 수 있습니다.

 

첫화면에서 버튼의 onPressed() 함수 내부를 아래 코드를 추가합니다

onPressed: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );
}

다음 화면으로 이동 gif.

 

 

3. 이전 화면으로 이동하기

화면을 Navigator에 push 해두었으니, 반대로 pop하면 됩니다

 

두번째 화면에서 버튼의 onPressed() 함수 내부를 아래 코드를 추가합니다

Navigator.pop(context);

뒤로가기 까지 구현된 화면. gif

 

 

 

4. 이동시 애니메이션 변경

위 예제 실행 결과를 보면, 다음 화면으로 이동할땐 화면이 위로 올라오고, 뒤로가기를 누르면 아래로 내려가는것을 알 수 있습니다

 

애니메이션 종류

  • FadeTransition
  • SlideTransition
  • ScaleTransition
  • RotationTransition
  • SizeTransition

여러가지 Transition을 이용해서 예제를 보여드리겠습니다.

 

4.1 FadeTransition

서서히 나타났다가 서서히 사라지는 FadeTransition을 이용해 보겠습니다 

 

기존에 사용하던 MaterialPageRoute를 상속받아서 buildTransitions ()함수를 재정의 해야 합니다

(개발을 어느정도 하다보니 flutter에서는 화면 단위를 route 라고 하는거 같네요)

class CustomRoute<T> extends MaterialPageRoute<T> {

  CustomRoute({ WidgetBuilder builder, RouteSettings settings })
      : super(builder: builder, settings: settings);

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
    if (settings.isInitialRoute)
      return child;
    
    return /* 이부분에 Transition 코드를 넣어주세요 */
  }
}

 

FadeTransition을 사용.

FadeTransition(opacity: animation, child: child);
class CustomRoute<T> extends MaterialPageRoute<T> {

  CustomRoute({ WidgetBuilder builder, RouteSettings settings })
      : super(builder: builder, settings: settings);

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
    if (settings.isInitialRoute)
      return child;
    
    return FadeTransition(opacity: animation, child: child);
  }
}

 

 

재정의 한 클래스를 기존에 이동할때 사용하던 코드에 적용해보겠습니다

/*전*/
Navigator.push(
 	context,
	MaterialPageRoute(builder: (context) => SecondRoute()),
 ); 

/*후*/
Navigator.push(
	context,
	CustomRoute(builder: (context) => SecondRoute()),
); 

 

fade 애니메이션이 적용된 화면이동. gif

 

4.2 SlideTransition

SlideTransition(
    position: Tween<Offset>(
        begin: Offset(-1,0),
        end: Offset.zero
    ).animate(animation), 
    child: child
);

 

slide 애니메이션이 적용된 화면 이동. gif

 

4.3 ScaleTransition

ScaleTransition(
    scale: Tween<double>(
        begin: 0.0,
        end: 1.0,
    ).animate(
        CurvedAnimation(
            parent: animation,
            curve: Curves.fastOutSlowIn,
        ),
    ), 
    child: child
);

 

ScaleTransition Gif.

4.4 RotationTransition

RotationTransition(
    turns: Tween<double>(
        begin: 0.0,
        end: 1.0,
    ).animate(
        CurvedAnimation(
            parent: animation,
            curve: Curves.linear,
        ),
    ), 
    child: child
);

RotationTransition Gif.

 

4.5 SizeTransition

Size의 경우 기준이 있어야 해서? Align을 추가 합니다

두번째 페이지의 비교가 힘들어서 이번 예제에선 두번째 페이지 배경을 오랜지 색으로 칠해두었습니다.

Align(
    child: SizeTransition(
        sizeFactor: animation,
        child: child,
    ),
);

SizeTransition. Gif

 

 

 

5.다른 화면으로 데이터 이동 (직접적 전달)

직접적으로 전달하는데 아직까지 안드로이드의 Intent 혹은 아이폰의 segue 처럼 간접적인 전달 방식은 모르겠네요.

혹시 알게 된다면 댓글 부탁드립니다

 

두번째 화면에서 Widget을 만들때, 파라매터를 받을 수 있게 생성자를 만듭니다

class SecondRoute extends StatelessWidget {

  final String str;
 
  SecondRoute({Key key, @required this.str}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(str),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            
            Navigator.pop(context);

          },
          child: Text('Go back!'),
        ),
      ),
      backgroundColor: Colors.orange,
    );
  }
}

 

그리고 첫번째 화면에서는 Navigator로 두번째 화면을 만들어서 푸쉬할때, 데이터('Hello')를 넣어줍니다

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Open route'),
          onPressed: () {
             
            Navigator.push(
              context,
              CustomRoute(builder: (context) => SecondRoute(str:'Hello')),
            ); 
          },
        ),
      ),
    );
  }
}

 

그러면 두번째 화면이 생성되면서 데이터가 할당되고, AppBar에는 데이터가 반영됩니다

 

 

 

6. 기존 화면으로 돌아왔을때, 데이터 전달 받기

이번 기능을 이해하기 위해서는 async, await라는 비동기 개념을 익혀야 합니다. 

flutter에서 어떤 구조로 작동되는지 정확히 모르겠지만... 

연결된 코드가 아닌 반응이 올때까지 기다리는 코드를 말합니다 

 

먼저 계속해서 statelessWidget으로 만들었던 FirstRoute를 statefulWidget으로 변경합니다

왜냐하면 FirstRoute창으로 돌아왔을때, 보이고 있는 버튼의 데이터를 바꿀 생각이거든요

class FirstRoute extends StatefulWidget {

  @override
  State createState() => _FirstRouteState();
} 

class _FirstRouteState extends State<FirstRoute> {

  String text = 'first';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Open route ' + text),
          onPressed: () async {
             
            final result = await Navigator.push(
              context,
              CustomRoute(builder: (context) => SecondRoute(str: 'Hello')),
            ); 

            setState(() {
              text = result;
            });
          },
        ),
      ),
    );
  }
 
}

onPressed: 에 들어가는 함수의 구현 부분앞에 async가 추가 되었습니다

그리고 Navigator.push 함수의 결과값을 await를 붙여서 받고, setState를 호출해서 위젯을 갱신합니다 

 

 

Navigator.pop(context,'second');

두번째 화면에서 코드는 아주 살짝 바꼇습니다

Navigator.pop 함수에 context만 있었지만, 'second' 라는 데이터를 하나 더 추가했습니다 

 

 

처음엔 Open route first 였던 버튼이, 다음 화면을 갔다가 되돌아오면 Open route second로 변해있습니다. Gif.

 

 

 

댓글