카테고리 없음
28일차 딕셔너리 앱 만들기
신승호.
2023. 3. 5. 20:25
- 다음의 공개된 API를 분석하고, 클래스를 활용하여 적용 후 딕셔너리 앱을 다음과 같이 만드시오.
- 반드시 Dict 클래스를 만들고 Serialization을 진행할 수 있도록 하시오.
- 필요한 요소만을 클래스에 적용하는 것은 허용되지만, 최대한 많은 데이터를 가져올 수 있도록 한다.
- 이 때, 만약 검색어가 존재하지 않는 단어로 서버에서 정상적인 응답을 못받았을 경우는 아무 것도 출력되지 않도록 한다.
- 검색어를 입력하고 엔터를 누르면 (TextField의 onSubmitted) 주어진 API를 통해 검색하도록 한다.
- 이 때, 결과는 아래에 커스텀 위젯을 최대한 활용하여 보여줄 수 있도록한다.
- 커스텀 위젯은 최대한 분할되어 있을수록 좋다.
- 예) MeaningCard..
- 다음의 제공되는 코드를 사용할 수 있다.
- lib/page/main_page.dart
// 추가 코드를 작성할 것. 본 소스는 디자인만 작성되어 있으며
// 이 기본 틀을 통하여 과제에 필요한 소스코드를 추가적으로 구현할 것.
import 'package:flutter/material.dart';
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dictionary App'),
elevation: 0,
centerTitle: false,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(16),
child: TextField(
decoration: const InputDecoration(
hintText: "Search",
suffixIcon: Icon(Icons.search),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
onSubmitted: (value) {},
),
),
),
],
),
],
),
),
);
}
}
lib/model/dict.dart
import 'package:dictionary_app/model/license.dart';
import 'package:dictionary_app/model/meaning.dart';
import 'package:dictionary_app/model/phonetic.dart';
class Dict {
String word;
String? phonetic;
List<Phonetic> phonetics;
List<Meaning> meanings;
License license;
List<String> sourceUrls;
Dict(
{required this.word,
required this.phonetic,
required this.phonetics,
required this.meanings,
required this.license,
required this.sourceUrls});
factory Dict.fromMap(Map<String, dynamic> map) {
return Dict(
word: map['word'],
phonetic: map['phonetic'],
phonetics: List<Phonetic>.from(
map['phonetics'].map((e) => Phonetic.fromMap(e))),
meanings:
List<Meaning>.from(map['meanings'].map((e) => Meaning.fromMap(e))),
license: License.fromMap(map['license']),
sourceUrls: List<String>.from(map['sourceUrls']));
}
}
lib/model/definition.dart
class Definition {
String definition;
List<String> synonyms;
List<String> antonyms;
String? example;
Definition(
{required this.definition,
required this.synonyms,
required this.antonyms,
required this.example});
factory Definition.fromMap(Map<String, dynamic> map) {
return Definition(
definition: map['definition'],
synonyms: List<String>.from(map['synonyms']),
antonyms: List<String>.from(map['antonyms']),
example: map['example']);
}
}
lib/model/meaning.dart
import 'package:dictionary_app/model/definition.dart';
class Meaning {
String partOfSpeech;
List<Definition> definitions;
List<String> synonyms;
List<String> antonyms;
Meaning(
{required this.partOfSpeech,
required this.definitions,
required this.synonyms,
required this.antonyms});
factory Meaning.fromMap(Map<String, dynamic> map) {
return Meaning(
partOfSpeech: map['partOfSpeech'],
definitions: List<Definition>.from(
map['definitions'].map((e) => Definition.fromMap(e))),
synonyms: List<String>.from(map['synonyms']),
antonyms: List<String>.from(map['antonyms']));
}
}
lib/model/phonetic.dart
import 'package:dictionary_app/model/license.dart';
class Phonetic {
String text;
String? audio;
String? sourceUrl;
License? license;
Phonetic(
{required this.text,
required this.audio,
required this.sourceUrl,
required this.license});
factory Phonetic.fromMap(Map<String, dynamic> map) {
return Phonetic(
text: map['text'],
audio: map['audio'],
sourceUrl: map['sourceUrl'],
license: map['license'] != null ? License.fromMap(map['license']) : null,
);
}
}
lib/model/license.dart
class License {
String name;
String url;
License({required this.name, required this.url});
factory License.fromMap(Map<String, dynamic> map) {
return License(name: map['name'], url: map['url']);
}
}
main.dart
import 'package:dictionary_app/page/mainpage.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: MainPage(),
);
}
}
mainpage.dart
// ignore_for_file: prefer_const_constructors, unused_local_variable
import 'package:dictionary_app/model/dict.dart';
import 'package:dictionary_app/widget/dict_card.dart';
import 'package:dictionary_app/widget/meaningcard.dart';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Dict? dict;
Dio dio = Dio();
getData(String word) async {
var url = 'https://api.dictionaryapi.dev/api/v2/entries/en/';
try {
var res = await dio.get(url + word);
var data = res.data.first;
dict = Dict.fromMap(data);
setState(() {});
} catch (e) {
dict = null;
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white12,
elevation: 0,
title: Text('Dictionary App'),
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(16),
child: TextField(
decoration: const InputDecoration(
hintText: "Search",
suffixIcon: Icon(Icons.search),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
onSubmitted: (value) {
getData(value); //네트워크 단어 요청
},
),
),
),
],
),
Divider(
thickness: 1,
),
if (dict != null)
Expanded(
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: DictCard(dict: dict!))),
],
),
),
);
}
}
dict.card.dart
import 'package:dictionary_app/model/dict.dart';
import 'package:dictionary_app/widget/meaningcard.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
class DictCard extends StatelessWidget {
const DictCard({super.key, required this.dict});
final Dict dict;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
dict.word,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24),
),
Divider(),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: dict.meanings.length,
itemBuilder: ((context, index) {
return MeaningCard(meaning: dict.meanings[index]);
}))
],
),
),
);
}
}
meaningcard.dart
import 'package:dictionary_app/model/meaning.dart';
import 'package:flutter/material.dart';
class MeaningCard extends StatelessWidget {
const MeaningCard({super.key, required this.meaning});
final Meaning meaning;
@override
Widget build(BuildContext context) {
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
meaning.partOfSpeech,
style: TextStyle(fontWeight: FontWeight.bold),
),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: meaning.definitions.length,
itemBuilder: ((context, index) => ListTile(
title: Text(meaning.definitions[index].definition),
)),
),
],
),
);
}
}
클래스만드는거에 오류난 부분이 없는거 확인하고 UI를 만들었는데 결과가 안나와서 헤매다가 강의보고했는데 실수로 빌드안에getdata를 만들었던걸 깨달아버림..