-
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를 만들었던걸 깨달아버림..