준코딩

플러터 웹뷰 뒤로가기(앱 종료까지) 본문

프로그래밍/플러터(Flutter)

플러터 웹뷰 뒤로가기(앱 종료까지)

Ljunhyeob - App Dev 2024. 4. 5. 14:38

웹뷰에서 뒤로 가기를 눌렀을 때 이전 페이지로 이동하거나, 이동할 페이지가 없는 경우 앱 종료하는 기능을 구현해 보도록 하겠습니다~

 

 

일단 아래 글에 이어서 코드를 작성하였습니다.

https://leejhjava.tistory.com/147

 

플러터 웹뷰 Javascript <-> 앱 통신

플러터 하이브리드 앱을 제작하고 있습니다. 하이브리드 앱에서 거의 필수로 해야 하는 게 웹페이지와 앱 간의 통신입니다. 어떻게 하는지 정리 해보겠습니다. pubspec.yaml 파일에 webview_flutter: ^4.7

leejhjava.tistory.com

 

 

수정 해야하는 부분

  @override
  Widget build(BuildContext context) { 
    return Scaffold( 
      body: SafeArea( 
        child: WebViewWidget(controller: _controller!), 
      ),
    );
  }

 

 

 

수정 이후 코드

코드 설명은 주석으로 달아두었습니다!

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if (await _controller!.canGoBack()) {  //_controller 에서 뒤로 갈 곳이 있는지 확인합니다. bool형태로 나옵니다
          _controller!.goBack(); //갈곳이 있는경우 true이기 때문에 이 코드가 실행됩니다. _controller 의 이전 페이지로 이동합니다.
          return false;
        } else {  //만약 _controller 에서 뒤로 갈 곳이 없는 경우~ 
          if (_backButtonPressedOnce) { //_backButtonPressedOnce 가 true 인 경우
            SystemNavigator.pop(); //앱 종료
          } else {
            _backButtonPressedOnce = true; //_backButtonPressedOnce 를 true로 바꾸고
            ScaffoldMessenger.of(context).showSnackBar( //하단에 스낵바를 생성합니다.
              SnackBar(
                content: Text('한 번 더 누르시면 앱이 종료됩니다.'),
                duration: Duration(seconds: 2),
              ),
            );
            Timer(Duration(seconds: 2), () { //그리고 2초 뒤 _backButtonPressedOnce 값을 다시 false로 변환합니다.
              _backButtonPressedOnce = false;
            });
            return false;
          }
        }
        return true;
      },
      child: SafeArea(
        child: Scaffold(
          body: WebViewWidget(
            controller: _controller!,
          ),
        ),
      ),
    );
  }

 

그리고~ 이것만 수정하면 _backButtonPressedOnce 가 없어서 에러가 발생하는데,

이전 코드에서 WebViewController? _controller;  선언한 곳 아래에 

bool _backButtonPressedOnce = false;  를 선언해주어야합니다.

 

 

 

수정 이후 webview_page.dart의 코드

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebviewPage extends StatefulWidget {
  const WebviewPage({Key? key}) : super(key: key);

  @override
  State<WebviewPage> createState() => _WebviewPageState();
}

class _WebviewPageState extends State<WebviewPage> {
  WebViewController? _controller;
  bool _backButtonPressedOnce = false;
  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(const Color(0x00000000))
      ..setNavigationDelegate(
        NavigationDelegate(
          onProgress: (int progress) {},
          onPageStarted: (String url) {},
          onPageFinished: (String url) {},
          onWebResourceError: (WebResourceError error) {},
          onNavigationRequest: (NavigationRequest request) {
            return NavigationDecision.navigate;
          },
        ),

      )
      ..addJavaScriptChannel('Test', onMessageReceived: (JavaScriptMessage message) {
        Map<String, dynamic> data = json.decode(message.message);
        print("javascript func message: " + data['action'].toString());
        if(data['action'] == "download"){
          print("javascript func download 실행");
          print("value: " + data['value'].toString()); // 값 출력
        }
      })
      ..loadRequest(Uri.parse('https://leejhjava.tistory.com'));
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if (await _controller!.canGoBack()) {
          _controller!.goBack();
          return false;
        } else {
          if (_backButtonPressedOnce) {
            SystemNavigator.pop();
          } else {
            _backButtonPressedOnce = true;
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text('한 번 더 누르시면 앱이 종료됩니다.'),
                duration: Duration(seconds: 2),
              ),
            );
            Timer(Duration(seconds: 2), () {
              _backButtonPressedOnce = false;
            });
            return false;
          }
        }
        return true;
      },
      child: SafeArea(
        child: Scaffold(
          body: WebViewWidget(
            controller: _controller!,
          ),
        ),
      ),
    );
  }
}
Comments