창업 부트캠프 {창} 4주차 실습 - book_store

SungJun Park·2022년 6월 24일
0

Flutter

목록 보기
3/3

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';

import 'book.dart';
import 'book_service.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => BookService()),
      ],
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  HomePage({Key? key}) : super(key: key);

  /// 검색어를 가져올 수 있도록 TextField와 연결해 줍니다.
  final TextEditingController searchController = TextEditingController();

  /// 검색 함수
  /// 엔터를 누르거나 돋보기 아이콘을 누를 때 호출
  void search(BookService bookService) {
    String keyword = searchController.text;
    if (keyword.isNotEmpty) {
      bookService.getBookList(keyword);
    }
  }

  
  Widget build(BuildContext context) {
    return Consumer<BookService>(
      builder: (context, bookService, child) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.white, // 배경 색상
            iconTheme: IconThemeData(color: Colors.black), // app bar icon color
            title: Text(
              'Book Store',
              style: TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
                color: Colors.black,
              ),
            ),
            actions: [
              Container(
                alignment: Alignment.bottomCenter,
                padding: const EdgeInsets.only(right: 12),
                child: Text(
                  "total ${bookService.bookList.length}",
                  style: TextStyle(
                    color: Colors.black,
                    fontSize: 16,
                  ),
                ),
              ),
            ],

            bottom: PreferredSize(
              preferredSize: Size.fromHeight(70.0),
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextField(
                  controller: searchController,
                  decoration: InputDecoration(
                    labelText: '원하시는 책을 검색해주세요',
                    enabledBorder: OutlineInputBorder(
                      borderSide: const BorderSide(width: 0.5),
                      borderRadius: BorderRadius.circular(1),
                    ),
                    // 돋보기 아이콘
                    suffixIcon: IconButton(
                      icon: Icon(Icons.search),
                      onPressed: () {
                        search(bookService);
                      },
                    ),
                  ),
                  onSubmitted: (v) {
                    // 엔터를 누르는 경우
                    search(bookService);
                  },
                ),
              ),
            ),
          ),
          body: Column(
            children: [
              Expanded(
                child: ListView.builder(
                  itemCount: bookService.bookList.length,
                  itemBuilder: (context, index) {
                    Book book = bookService.bookList[index];

                    return ListTile(
                      leading: Image.network(
                        book.thumbnail,
                        width: 80,
                        height: 80,
                        fit: BoxFit.cover,
                      ),
                      title: Text(book.title),
                      subtitle: Text(book.subtitle),
                      onTap: () {
                        // 클릭시 previewLink 띄우기
                        launch(book.previewLink);
                      },
                    );
                  },
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

book_service.dart

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

import 'book.dart';

class BookService extends ChangeNotifier {
  // 책 목록
  List<Book> bookList = [];

  /// 검색어로 책 정보 불러오기
  void getBookList(String q) async {
    bookList.clear(); // 기존에 들어있는 데이터 초기화

    // API 호출
    Response res = await Dio().get(
      "https://www.googleapis.com/books/v1/volumes?q=$q&startIndex=0&maxResults=40",
    );
    List items = res.data["items"]; // items 접근
    for (Map<String, dynamic> item in items) {
      Map<String, dynamic> volumeInfo = item["volumeInfo"]; // volumeInfo 접근
      Book book = Book.fromJson(volumeInfo); // Map -> Book
      bookList.add(book); // Book 추가
    }

    // 화면 갱신
    notifyListeners();
  }
}

book.dart

class Book {
  String title;
  String subtitle;
  String thumbnail;
  String previewLink;

  Book({
    required this.title,
    required this.subtitle,
    required this.thumbnail,
    required this.previewLink,
  });

  // Map<String, dynamic>을 전달받아 Book 클래스 인스턴스를 반환하는 함수
  // factory 키워드를 붙여서 생성자로 사용
  factory Book.fromJson(Map<String, dynamic> volumeInfo) {
    return Book(
      // title이 없는 경우 빈 문자열 할당
      title: volumeInfo["title"] ?? "",
      // subtitle이 없는 경우 빈 문자열 할당
      subtitle: volumeInfo["subtitle"] ?? "",
      // imageLisks 또는 thumbnail이 없을 때 빈 이미지 추가
      thumbnail: volumeInfo["imageLinks"]?["thumbnail"] ??
          "https://i.ibb.co/2ypYwdr/no-photo.png",
      // previewLink가 없는 경우 빈 문자열 할당
      previewLink: volumeInfo["previewLink"] ?? "",
    );
  }
}

0개의 댓글