출첵 app 실습 리뷰

pharmDev·2024년 12월 5일

1. 작동 순서 설명

  1. 라이브러리 임포트: 필요한 라이브러리 (dart:io, flutter/material.dart, geolocator, google_maps_flutter 등)을 임포트합니다.
  2. HomeScreen 위젯 정의: HomeScreen은 상태를 가질 수 있는 StatefulWidget입니다. 상태를 관리하기 위해 _HomeScreenState를 사용합니다.
  3. 초기 값 설정:
    • 카메라 초기 위치를 initialPosition 변수로 설정합니다.
    • 출근 여부와 출근 가능 여부를 나타내는 불리언 변수를 정의합니다.
    • 출근 가능 거리(okDistance)를 설정합니다.
  4. initState() 메소드 호출:
    • 스트림을 통해 사용자의 위치 변화를 추적하기 위해 initState() 메소드에서 Geolocator.getPositionStream().listen()을 사용합니다.
  5. 위치 권한 확인: checkPermission() 함수는 사용자의 위치 권한을 확인하고, 권한이 없다면 요청합니다.
  6. build() 메소드로 UI 구성:
    • FutureBuilder를 사용하여 위치 권한이 확인된 후에만 Google Map을 표시합니다.
    • 지도와 출근 버튼을 포함한 전체 UI를 구성합니다.
  7. 출근 버튼 클릭 처리 (choolCheckPressed()):
    • 출근 여부를 확인하는 대화 상자를 띄우고, 출근을 확정하면 상태를 업데이트합니다.
  8. 위치 버튼 클릭 처리 (myLocationPressed()):
    • 현재 위치로 지도의 카메라를 이동시킵니다.

2. /// 표시된 부분에 대한 답변

  1. GoogleMapController 사용 이유 및 **late**** 사용**

    /// GoogleMapController controller; 이걸 왜 써야하는지 궁금해
    /// 나중에 controller 의 값이 선언 될것이기에 late 사용함
    late final GoogleMapController controller;
    • 설명: GoogleMapController는 Google Maps를 제어하기 위해 사용됩니다. 예를 들어 카메라 이동, 특정 위치로 줌 설정 등이 필요할 때 사용합니다.
    • late 키워드를 사용하는 이유는 GoogleMapController가 지도 생성 후에 초기화되기 때문입니다. late를 사용하면 초기화되지 않은 상태로 변수를 선언한 뒤 나중에 초기화할 수 있습니다.
  2. **initState()**에서 스트림 사용 이유

    /// 코딩 강의 내용 : "initState에 해 놓는 이유는 스트림을 
    /// 우리가 받을거고 그 스트림에서 이 앱이 실행되고 종료될때까지 
    /// 계속 값을 받을 것이기 때문에 initstate에서 
    /// 한번만 리슨을 해주는 것이다."
    /// 위 코딩 강의 내용이 이해가 잘 되지 않아 궁금함
    • 설명: initState()는 위젯이 처음 생성될 때 한 번만 호출됩니다. 사용자의 위치를 계속 추적하기 위해 스트림을 한 번 설정하면 앱이 실행되는 동안 계속해서 위치 데이터를 받아올 수 있습니다. 이 때문에 스트림 설정은 initState()에서 한 번만 설정하는 것이 적절합니다.

결론적으로, initState() 안에 Geolocator.getPositionStream().listen()이 있는 것과 다른 위치에 같은 스트림을 설정하는 것의 차이는 호출 시점입니다. initState()에서는 위젯이 처음 생성될 때 한 번 설정하고 이후에는 변경하지 않도록 보장합니다. 만약 다른 곳에서 setState()를 사용해 스트림을 시작해도 결과적으로 스트림은 동일하게 동작하지만, initState()는 생명주기상 한 번만 호출된다는 이점이 있습니다. 즉, 매번 위젯이 빌드될 때마다 불필요하게 스트림을 재설정하는 것을 방지합니다.

  1. Dart에서 스트림(Stream)이란?

    /// 다트언어에서 steam 스트림이 궁금해
    • 설명: 스트림(Stream)은 데이터의 흐름을 비동기적으로 전달하는 방식입니다. 여러 개의 데이터를 순차적으로 전달할 수 있고, listen()을 통해 데이터를 받을 때마다 이벤트를 처리할 수 있습니다. 이 앱에서는 사용자의 위치가 바뀔 때마다 새 위치를 스트림으로 받습니다.
  2. **event**가 가리키는 위치

    Geolocator.getPositionStream().listen(
          (event) {
            final start = LatLng(
              37.5214,
              126.9246,
            );
    
    /// 아래의 event가 가르키는데 현재 위치인지 궁금해. 이유도 궁금해
    final end = LatLng(event.latitude, event.longitude);
    • 설명: event는 스트림에서 전달된 현재 사용자의 위치 데이터를 의미합니다. Geolocator.getPositionStream()은 사용자의 위치가 변경될 때마다 위치 정보를 담은 이벤트를 event 변수에 담아 전달하므로 event.latitudeevent.longitude는 사용자의 현재 위치를 나타냅니다.
  1. if** 문 사용법과 **!** 연산자**

    /// if 사용법들 모두 궁금함. 아래에 ! 를 if 문을 포함해서
    if (!isLocationEnabled) {
      throw Exception('위치 기능을 활성화 해주세요.');
    }
    • 설명: if (!isLocationEnabled)isLocationEnabledfalse일 때 조건을 만족합니다. !는 논리 부정 연산자로, truefalse로, falsetrue로 바꿉니다. 따라서 위치 기능이 활성화되지 않았을 때 예외를 던지는 코드입니다.
  2. **||****&&**** 연산자 사용법**

    /// if 문에서 ||, &&사용법 궁금함
    if (checkPermission != LocationPermission.always &&
        checkPermission != LocationPermission.whileInUse) {
      throw Exception('위치 권한을 허가해 주세요.');
    }
    • 설명: &&는 논리 AND 연산자로, 두 조건이 모두 참일 때만 true를 반환합니다. ||는 논리 OR 연산자로, 하나 이상의 조건이 참이면 true를 반환합니다. 이 경우, 위치 권한이 alwayswhileInUse가 아닐 때 예외를 던집니다.
  3. actions** 사용법**

    /// actions 사용법 궁금해
    actions: [
      IconButton(
        onPressed: myLocationPressed,
        icon: Icon(
          Icons.my_location,
        ),
        color: Colors.blue,
      )
    ],
    • 설명: actions는 앱바 오른쪽에 표시될 위젯들을 정의합니다. 여기서는 위치 버튼을 추가하여 사용자가 현재 위치로 지도를 이동할 수 있게 합니다.
  4. **FutureBuilder**의 역할

    ///future Builder의 역할 궁금해. 
    ///강의에서는 future Builder가 future를 실행하고 나서, 
    ///Builder 함수에서 반환된 UI를 화면에 보여주는데 
    ///그때 우리가 사용할 수 있는 옵션이 Context 뿐만 아니라. 
    ///Build 함수 같은 경우는 context만 사용할 수 있다. 
    ///두번째 파라미터로 snapshot도 사용할 수 있게 해준다.
    ///snapshot의 역할은 앞서 실행한 future의 결과를 
    ///받을 수 있는 곳이다.
     body: FutureBuilder(
            future: checkPermission(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (snapshot.hasError) {
                return Center(
                  child: Text(snapshot.error.toString()),
                );
              }
    • 설명: FutureBuilder는 비동기 작업(future)의 결과를 기반으로 화면을 빌드해주는 역할을 합니다. 위 코드에서 checkPermission() 함수가 future로 사용되며, 비동기 작업의 결과가 나올 때까지 기다립니다.

      builder는 FutureBuilder가 데이터를 받은 후 UI를 빌드하는데 사용하는 콜백 함수입니다. 여기서는 context와 snapshot 두 가지 파라미터를 받습니다.

      context는 위젯의 위치를 나타내는 정보이고, snapshot은 future의 상태나 결과를 담고 있습니다. 예를 들어, snapshot.hasError는 비동기 작업에 에러가 발생했는지를 확인하는데 사용됩니다.

      snapshot을 통해 future가 완료됐을 때 결과 값을 가져와 UI에 반영할 수 있습니다. 이 코드에서는 에러가 발생했을 때 에러 메시지를 화면에 출력합니다.

    • onMapCreated** 사용법**

    /// onMapCreated의 사용법이 궁금해
    onMapCreated: (GoogleMapController controller) {
      this.controller = controller;
    },
    • 설명: onMapCreated는 지도가 처음 생성될 때 호출됩니다. 여기서 전달된 controller를 통해 지도를 제어할 수 있습니다.
  5. **this.controller**와 뒤에 있는 **controller**** 차이점**

    ///this.controller의 controller와 =뒤에 있는 controller의 차이점이 궁금해
    this.controller = controller;
    • 설명: this.controller는 클래스에 선언된 변수입니다. 오른쪽의 controlleronMapCreated에서 전달된 파라미터입니다. 클래스 변수에 파라미터로 전달된 값을 할당하는 것입니다.
  6. 조건부 렌더링 (``)

    /// if(!choolCheckDone) 이 코드를 쓰면, 이 코드 뒤에 있는 코드들을 보여줄지 말지 결정할 수 있는지 궁금해
    if (!choolCheckDone && canChoolCheck)
      OutlinedButton(
        onPressed: choolCheckPressed,
        style: OutlinedButton.styleFrom(
          foregroundColor: Colors.blue,
        ),
        child: Text('출근하기'),
      ),
    • 설명: 조건부 렌더링을 통해 if 문 뒤의 코드가 조건이 참일 때만 렌더링됩니다. !choolCheckDone이면 아직 출근하지 않았음을 의미하고, canChoolCheck이 참이면 출근 버튼을 표시합니다.
  7. **result**에 저장되는 값

    /// result에는 어떤 값이 저장되는지 궁금해, 그 값이 저장된 이유도 궁금해
    final result = await showDialog(
      context: context,
      builder: (BuildContext context) {
        return CupertinoAlertDialog(
          title: Text('출근하기'),
          content: Text('출근을 하시겠습니까?'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(false);
              },
              child: Text('취소'),
            ),
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(true);
              },
              child: Text('출근하기'),
            ),
          ],
        );
      },
    );
    • 설명: result에는 대화 상자에서 Navigator.of(context).pop()으로 반환된 값이 저장됩니다. true 또는 false가 저장되며, 사용자가 출근하기를 선택하면 true, 취소를 선택하면 false가 됩니다.
  8. **context**builder 의미

    ///아래에 왜 context를 사용하는지, context의미, builder의 의미가 궁금해
    • 설명: context는 현재 위젯의 위치 및 부모-자식 관계 등 트리 구조 정보를 가지고 있습니다. showDialog에서 context를 사용해 현재 트리의 위치에서 대화 상자를 띄우는 데 사용됩니다.
    • builder는 대화 상자 위젯을 반환하는 함수입니다. 여기서 CupertinoAlertDialog를 반환하고 있습니다.
  9. **controller.animateCamera****CameraUpdate.newLatLng**** 역할**

    ///controller.animateCamera 이 부분이 하는 역할이 궁금해
    controller.animateCamera(
      ///CameraUpdate.newLatLng 이 부분이 하는 역할이 궁금해
      CameraUpdate.newLatLng(
        LatLng(location.latitude, location.longitude),
      ),
    );
    • 설명: controller.animateCamera()는 지도 카메라를 이동시키는 역할을 합니다. CameraUpdate.newLatLng()은 새로운 위치로 카메라를 이동하도록 지시하는 객체를 반환합니다. 즉, 사용자의 현재 위치로 지도를 이동시키는 것입니다.
  10. Geolocator.getPositionStream().listen** 안의 **setState** 실행 시**

    /// Geolocator.getPositionStream().listen 안에 있는 setState가 실행되면 무슨일이 생기지?
    • 설명: setState()가 실행되면 Flutter는 위젯의 상태가 변경되었음을 인식하고, 해당 위젯을 다시 빌드하여 화면에 변경된 내용을 반영합니다. 이 경우 사용자의 위치가 변경되면 canChoolCheck 변수가 업데이트되고, 이에 따라 UI가 변경되어 출근 가능 여부를 표시합니다.
profile
코딩을 배우는 초보

0개의 댓글