꿈꾸는 시스템 디자이너

Flutter 강좌 - 페이지 전환 전에 작업 마무리 하는 법 | WillPopSocop 사용법 | 백버튼 재정의 본문

Development/Flutter

Flutter 강좌 - 페이지 전환 전에 작업 마무리 하는 법 | WillPopSocop 사용법 | 백버튼 재정의

독행소년 2019. 8. 20. 11:05

Flutter 강좌 시즌2 목록 : https://here4you.tistory.com/149

 

앱을 개발할 때 앱의 설정을 하는 페이지를 개발하여 이용하는 경우가 있다. 아래 예와 같이 메인 화면에서 앱 설정을 위해 설정 페이지로 이동한 후 설정작업을 마무리하고 메인화면으로 돌아오는 패턴이 이용된다.

 

설정 페이지(Second Page)에서 설정작업을 마치고 메인화면(First Page)로 돌아오는 방법은 AppBar의 화살표를 선택하거나 화면 하단의 백버튼을 선택하는 것이다. 그러면 위의 그림과 같이 메인화면으로 되돌아 오게 된다.

기본적은 소스코드는 다음과 같다.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: "WillPopScope Demo",
    home: FirstPage(),
  ));
}

class FirstPage extends StatefulWidget {
  @override
  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  void updateState() {
    print("Update State of FirstPage");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("First Page")),
      body: Center(
        child: RaisedButton(
          child: Text("Move to Second Page"),
          onPressed: () async {
            // SecondPage를 화면에 푸시
            await Navigator.push(
                context, MaterialPageRoute(builder: (context) => SecondPage()));
            updateState();
          },
        ),
      ),
    );
  }
}

class SecondPage extends StatefulWidget {
  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
  @override
  void initState() {
    print("initState() of _SecondPage");
    super.initState();
  }

  @override
  void dispose() {
    print("dispose() of _SecondPage");
    storeSettingValues();
    super.dispose();
  }

  void storeSettingValues() {
    print("store setting values");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Second Page")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text("Second Page"),
            Text("Set something on this page.")
          ],
        ),
      ),
    );
  }
}

메인화면에 해당하는 FirstPage와, 설정화면에 해당하는 SecondPage로 구성되는 간단한 구조다. 설정화면으로 이동하기 위해 버튼의 onPress 함수에서 Navigator.push()를 이용하고 있다. 이 때 설정작업이 완료됨을 알기 위해 await를 통해 대기하다가 설정화면에 제거된 후 설정내용을 반영하기 위해 updateState()를 호출하는 구조다.

설정화면은 더 간단하다. 다만 설정화면이 제거되기 전에 설정한 값을 저장하기 위한 함수 storeSettingValues()를 호출해야 할 필요가 있으므로 화면 삭제시 호출되는 dispose()에서 storeSettingValues()를 호출하도록 했다.

그럼 과연 예상대로 동작하는지 확인해 보자. 앱을 실행한 후 메인화면에서 설정화면을 진입했다가 되돌아온 후에 출력된 값을 확인하면 다음과 같다.

I/flutter ( 4296): initState() of _SecondPage
I/flutter ( 4296): Update State of FirstPage
I/flutter ( 4296): dispose() of _SecondPage
I/flutter ( 4296): store setting values

설정화면이 출력되면서 정상적으로 initState()가 호출되었다. 하지만 설정화면을 종료하고 메인화면으로 돌아올 때의 함수 호출 순서는 예상과 다른다. 정작 메인화면의 updateState()가 호출된 후에야 설정화면의 dispose()와 storeSettingValues()가 호출되고 있다. 만약 설정화면에서 설정된 값들을 SharedPreferences로 저장하고, 메인화며에서 다시 불러와서 페이지를 업데이트하는 구조라면 최신 SharePreferences 값이 저장되기 전 값들로 페이지를 업데이트하는 문제가 발생한다. 다시말해 설정값 저장 전에 메인화면이 업데이트되는 문제가 발생하게 된다.

결론적으로 위와 같은 페이지 구성에서는 storeSettingValues()를 dispose()에서 호출해서는 안되며, 화면이  제거되기 전에 확실하게 storeSettingValues()가 호출되도록 해야한다. 가장 좋은 방법은 AppBar의 아이콘이나 백버튼이 선택될 때 명시적으로 storeSettingValues()를 호출하는 것이다. 이러한 경우에 WillPopScope 위젯을 사용한다.

소스코드를 다음과 같이 수정한다.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: "WillPopScope Demo",
    home: FirstPage(),
  ));
}

class FirstPage extends StatefulWidget {
  @override
  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  void updateState() {
    print("Update State of FirstPage");
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("First Page")),
      body: Center(
        child: RaisedButton(
          child: Text("Move to Second Page"),
          onPressed: () async {
            // SecondPage를 화면에 푸시
            await Navigator.push(
                context, MaterialPageRoute(builder: (context) => SecondPage()));
            updateState();
          },
        ),
      ),
    );
  }
}

class SecondPage extends StatefulWidget {
  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
  @override
  void initState() {
    print("initState() of _SecondPage");
    super.initState();
  }

  @override
  void dispose() {
    print("dispose() of _SecondPage");
    //storeSettingValues();
    super.dispose();
  }

  void storeSettingValues() {
    print("store setting values");
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        storeSettingValues();
        Navigator.pop(context);
        return false;
      },
      child: Scaffold(
        appBar: AppBar(title: Text("Second Page")),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text("Second Page"),
              Text("Set something on this page.")
            ],
          ),
        ),
      ),
    );
  }
}

설정화며의 dispose()에서 storeSettingValues()를 호출하지 않도록 수정하고, Scaffold를 WillPopScope 위젯을 랩핑했다. WillPopScope 위젯은 onWillPop 속성을 제공하는데 이 속성을 이용해서 화면이 제거되는 로직을 재정의할 수 있다. 이 때 storeSettingValues()를 호출하는 코드를 추가한 것이다.

실행결과는 다음과 같다.

I/flutter ( 4945): initState() of _SecondPage
I/flutter ( 4945): store setting values
I/flutter ( 4945): Update State of FirstPage
I/flutter ( 4945): dispose() of _SecondPage

설정화면이 실행되면서 initState()가 호출되고 되돌아 가는 버튼이나 아이콘이 선택되면 onWillPop에 설정한 함수가 호출되면서 storeSettingValues()가 호출된 후 화면이 제거되고 await로 대기중이던 메인화면의 updateState()가 호출되게 된다. 그리고 그 뒤로 설정화면의 dispose()가 호출되는 것을 확인할 수 있다.

 


Flutter Code Examples 강좌를 추천합니다.

  • 제 블로그에서 Flutter Code Examples 프로젝트를 시작합니다.
  • Flutter의 다양한 예제를 소스코드와 실행화면으로 제공합니다.
  • 또한 모든 예제는 Flutter Code Examples 앱을 통해 테스트 가능합니다.

Flutter Code Examples 강좌로 메뉴로 이동

Flutter Code Examples 강좌 목록 페이지로 이동

Flutter Code Examples 앱 설치 | Google Play Store로 이동

 

Flutter Code Examples - Google Play 앱

Are you a beginner at Flutter? Check out the various features of Flutter through the demo. Source code for all demos is also provided.

play.google.com

Comments