-
Notifications
You must be signed in to change notification settings - Fork 346
/
main.dart
149 lines (130 loc) · 4.25 KB
/
main.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:http_image_provider/http_image_provider.dart';
import 'package:provider/provider.dart';
import 'book.dart';
import 'http_client_factory.dart'
if (dart.library.js_interop) 'http_client_factory_web.dart' as http_factory;
void main() {
runApp(Provider<Client>(
// `Provider` calls its `create` argument once when a `Client` is
// first requested (through `BuildContext.read<Client>()`) and uses that
// same instance for all future requests.
//
// Reusing the same `Client` may:
// - reduce memory usage
// - allow caching of fetched URLs
// - allow connections to be persisted
create: (_) => http_factory.httpClient(),
child: const BookSearchApp(),
dispose: (_, client) => client.close()));
}
class BookSearchApp extends StatelessWidget {
const BookSearchApp({super.key});
@override
Widget build(BuildContext context) => const MaterialApp(
// Remove the debug banner.
debugShowCheckedModeBanner: false,
title: 'Book Search',
home: HomePage(),
);
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Book>? _books;
String? _lastQuery;
late Client _client;
@override
void initState() {
super.initState();
_client = context.read<Client>();
}
// Get the list of books matching `query`.
// The `get` call will automatically use the `client` configured in `main`.
Future<List<Book>> _findMatchingBooks(String query) async {
final response = await _client.get(
Uri.https(
'www.googleapis.com',
'/books/v1/volumes',
{'q': query, 'maxResults': '20', 'printType': 'books'},
),
);
final json = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
return Book.listFromJson(json);
}
void _runSearch(String query) async {
_lastQuery = query;
if (query.isEmpty) {
setState(() {
_books = null;
});
return;
}
final books = await _findMatchingBooks(query);
// Avoid the situation where a slow-running query finishes late and
// replaces newer search results.
if (query != _lastQuery) return;
setState(() {
_books = books;
});
}
@override
Widget build(BuildContext context) {
final searchResult = _books == null
? const Text('Please enter a query', style: TextStyle(fontSize: 24))
: _books!.isNotEmpty
? BookList(_books!)
: const Text('No results found', style: TextStyle(fontSize: 24));
return Scaffold(
appBar: AppBar(title: const Text('Book Search')),
body: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
const SizedBox(height: 20),
TextField(
onChanged: _runSearch,
decoration: const InputDecoration(
labelText: 'Search',
suffixIcon: Icon(Icons.search),
),
),
const SizedBox(height: 20),
Expanded(child: searchResult),
],
),
),
);
}
}
class BookList extends StatefulWidget {
final List<Book> books;
const BookList(this.books, {super.key});
@override
State<BookList> createState() => _BookListState();
}
class _BookListState extends State<BookList> {
@override
Widget build(BuildContext context) => ListView.builder(
itemCount: widget.books.length,
itemBuilder: (context, index) => Card(
key: ValueKey(widget.books[index].title),
child: ListTile(
leading: Image(
image: HttpImage(
widget.books[index].imageUrl.replace(scheme: 'https'),
client: context.read<Client>())),
title: Text(widget.books[index].title),
subtitle: Text(widget.books[index].description),
),
),
);
}