[TIL] 책검색 앱 만들기 실습

청학동버블티·2024년 12월 3일

Flutter 공부

목록 보기
6/18

프로젝트 생성시에 ios, android 플랫폼 대상으로 생성하려면 terminal 에 아래 명령어를 입력한다.

flutter create --platforms=android,ios flutter_book_search_app

1. riverpod 추가 후 ProviderScope 로 앱을 감싸기


2. HomePage UI 구현


3. 검색입력위젯(TextField)

TextField(
  // TextField의 값을 변경하거나, 값을 다른 위젯에서 사용하고 싶을 때!
  controller: TextEditingController(),
  // TextField의 스타일 설정
  decoration: InputDecoration(
    hintText: "검색어를 입력해 주세요",
    // MaterialStateOutlineInputBorder.resolveWith : 
    // TextField 상태에 따라 다른 테두리 적용하고 싶을 때!
      border: MaterialStateOutlineInputBorder.resolveWith(
      (states) {
        return OutlineInputBorder(
          borderRadius: BorderRadius.circular(10),
          borderSide: BorderSide(color: Colors.grey),
        );
      },
    ),
  ),
  // 모바일에서 키보드 유형 설정
  keyboardType: TextInputType.text,
  // 키보드의 작업 버튼을 '완료'로 설정
  textInputAction: TextInputAction.done,
  // 텍스트 변경 시 호출되는 함수
  onChanged: (text) {
    print('Text changed: $text');
  },
  // 키보드에서 완료(엔터) 눌렀을 때 호출되는 함수
  onSubmitted: (text) {
    print('Submitted text: $text');
  },
  // true로 설정하면 입력한 텍스트가 가려짐 (비밀번호 입력 시 사용)
  obscureText: false, 
  // 최대 글자 수 제한
  maxLength: 50,
  // 최대 입력 줄 수 제한 
  maxLines: 1, 
  // TextField 글자 스타일
  style: TextStyle(fontSize: 16, color: Colors.black), // 입력 텍스트 스타일
)

여기서 구현할 순서는 아래와 같다.

1. TextField 구현

2. TextStyle 꾸미기
-> MaterialStateOutlineInputBorder.resolveWith를 사용하면
TextField의 값이 바뀔때마다 WidgetState enum 값을 전달해서 이 함수 실행

3. UX 고려하기
-> 현재 위젯(HomePage)에서 포커스를 가지고 있는 위젯이 있으면 포커스 해제해줌

4. TextEditingController 는 반드시 사용 다하면 dispose를 호출해줘야 메모리에서 제거됨!
-> 소거시 StatefulWidget 사용


4. 검색아이콘


5. GridView

기본 사용법

GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
    childAspectRatio: 3 / 4,
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
  ),
  children: [~],
)

builder 생성자 이용

  • itemCount 속성 갯수 만큼 itemBuilder 속성에 들어가는 함수 호출
  • itemBuilder에서는 return되는 위젯이 GridView children에 하나씩 들어간다고 생각!
    ⇒ 동일한 위젯인데 안에 내용만 바뀔때 주로 사용
GridView.builder(
  itemCount: 10,
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
    childAspectRatio: 3 / 4,
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
  ),
  itemBuilder: (context, index) {
      return Image.network(
        'https://picsum.photos/300/400',
        fit: BoxFit.cover,
      );
  },
)

gridDelegate

  • 그리드 레이아웃을 제어하는 클래스
  • SliverGridDelegateWithFixedCrossAxisCount
    - 한 행에 들어갈 위젯 갯수를 고정하고 싶을때 사용
SliverGridDelegateWithFixedCrossAxisCount(
	crossAxisCount: 3, // 한 행에 들어갈 위젯 갯수
	childAspectRatio: 3 / 4, // 각 위젯 비율
	crossAxisSpacing: 10, // 위젯간 가로 간격
	mainAxisSpacing: 10, // 위젯간 세로 간격
)
  • SliverGridDelegateWithMaxCrossAxisExtent
    - 위젯의 가로 크기를 정해서 사용하고 싶을때
SliverGridDelegateWithMaxCrossAxisExtent(
	maxCrossAxisExtent: 50, // 각 위젯의 너비
	childAspectRatio: 3 / 4,
	crossAxisSpacing: 10,
	mainAxisSpacing: 10,
)

6. BottomSheet UI

showModalBottomSheet(
	// 내부적으로 네비게이터 스택에 페이지를 쌓는 형식이라서 BuildContext 넘겨줌.
	// 페이지 이동 시 Navigator.push(context... 썼던거 기억 되살립시다!
  context: context, 
  // 배경 터치했을 때 바텀시트 닫을지 여부
  isDismissible: true,
  builder: (context) {
	  // 바텀시트 내 배치할 위젯
	  // Column 은 기본적으로 children 중 가장 큰 위젯을 기준으로 사이즈를 정하므로
	  // SizedBox로 바텀시트의 사이즈를 정해줍니다
    return HomeBottomSheet();
  },
)

7. DetailPage UI

  • Back button : Navigator Stack에서 이전 페이지가 존재하면 나타남.
  • flutter_webview vs flutter_inappwebview
    - flutter_webview : flutter 공식 팀에서 만들고 관리하지만 기능이 많이 없음
    - flutter_inappwebview : flutter 공식팀이 만든건 아니지만 쿠키 관리, 자바스크립트 상호작용 등의 기능을 간편하게 할 수 있음
    -> 패키지 추가 : flutter pub add flutter_inappwebview
InAppWebView(
    // InAppWebView 최초 요청할 URL
    initialUrlRequest: URLRequest(
      url: WebUri("https://www.naver.com/"),
    ),
    // WebView 설정 : 외우지 마시오! 버전바뀌면 사용법 바뀌니까 이런설정을 할 수 있다 정도로만 학습바랍니다!
    initialSettings: InAppWebViewSettings(
      // 사용자 제스쳐 없이도 비디오, 오디오 자동재생 가능여부
      mediaPlaybackRequiresUserGesture: true,
      // 페이지 javascript 활성화여부. 웹 브라우저에서는 js 필수불가결한 요소라 true!
      javaScriptEnabled: true,
      // 요청하는 클라이언트의 브라우저 종류, 운영체제, 장치 정보 등을 서버에서 알 수 있게 보낼때 같이 보냄
      // 디폴트로 웹뷰로 되어 있는데 일부 웹페이지에서는 웹뷰로 접속시 차단하는 페이지도 있으니 꼭 설정!
      userAgent:
          'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
    ),
    // WebView 네이티브 컴포넌트가 만들어지면 호출됨
    onWebViewCreated: (controller) {
      print('onWebViewCreated');
    },
    // 페이지 로딩이 시작될 때 호출됨
    onLoadStart: (controller, url) {
      print('onLoadStart');
    },
    // 페이지 로딩이 완료되면 호출됨
    onLoadStop: (controller, url) {
      print('onLoadStop');
    },
    // 웹뷰 내 웹 페이지에서 GPS, 카메라 등의 권한을 요청했을때 호출됨
    onPermissionRequest: (controller, request) async {
      print('onPermissionRequest');
      return null;
    },
  )

📚 Open API 란?

  • Open Application Programming Interface
  • 특정 서비스나 소프트웨어 기능을 외부 개발자가 사용할 수 있도록 정의한 인터페이스
  • 우리가 사용할 네이버 책 검색 API 의 경우 우리가 검색어를 네이버에 요청하면 네이버에서 그에 대한 결과를 JSON 형태로 반환해주는 서비스



  • 파라미터
    • 함수에서 파라미터는 괄호를 통해 넘겼지면 웹요청 중 GET 요청은 서버로 파라미터를 url 뒤에 ? 붙인 후 key=value 형태로 넘김. 여러개는 & 기호로 구분 key1=value1&key2=value2
    • 필수 파라미터인 검색어 query를 넘겨야함
    • `https://openapi.naver.com/v1/search/book.json?query=해리포터`

  • Thunder Client 설치
    • Thunder Client : HTTP 요청 VSCode 확장 프로그램. API 요청을 해볼 수 있는 도구.Header 수정, GET메서드 이외의 요청 등. POSTMAN 이라는 프로그램도 많이 쓰는데 Thurnder Client 가 VSCode안에 있어서 쓰기 용이



<역직렬화 클래스 만들기>

  1. 인스턴스 및 생성자 설정
  2. fromJson 생성자로 Map타입 지정하고 인스턴스가 들어갈 각각의 자리(클래스) 만들어 넣어주기
  3. toJson 메서드로 인스턴스를 Mapping 하여 Map 타입으로 뽑아냄

test 함수 사용 : 앱을 켜지 않고 test 작성 및 테스팅 가능.
또 테스트 케이스 작성 시 나중에 코드가 수정되었을 때 오류 찾기 용이
(기존 테스트가 실패할수도 있으니)

0개의 댓글