Skip to content

Instantly share code, notes, and snippets.

@koboolean
Last active June 18, 2022 07:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save koboolean/0eb66bad1f4c3043a256ff44d7b6c976 to your computer and use it in GitHub Desktop.
Save koboolean/0eb66bad1f4c3043a256ff44d7b6c976 to your computer and use it in GitHub Desktop.
flutter by book_store
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"] ?? "",
);
}
}
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();
}
}
import 'package:book_store/book_service.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'book.dart';
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);
}
}
@override
Widget build(BuildContext context) {
return Consumer<BookService>(
builder: (context, bookService, child) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: false,
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,
),
),
),
],
/// AppBar의 Bottom은 항상 PreferredSize 위젯으로 시작해야합니다.
bottom: PreferredSize(
preferredSize: Size(double.infinity, 72),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: searchController,
decoration: InputDecoration(
hintText: "원하시는 책을 검색해주세요.",
// 테두리
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
/// 돋보기 아이콘
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: () {
// 돋보기 아이콘 클릭
search(bookService);
},
),
),
onSubmitted: (v) {
// 엔터를 누르는 경우
search(bookService);
},
),
),
),
),
body: bookService.bookList.isEmpty
// bookList가 비어있는 경우
? Center(
child: Text(
"검색어를 입력해 주세요",
style: TextStyle(
fontSize: 21,
color: Colors.grey,
),
),
)
// bookList 보여주기
: 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 띄우기
launchUrl(Uri.parse(book.previewLink));
},
);
},
),
);
},
);
}
}
import 'package:book_store/book_service.dart';
import 'package:book_store/home_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => BookService()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment