카테고리 없음

28일차 딕셔너리 앱 만들기

신승호. 2023. 3. 5. 20:25
  1. 다음의 공개된 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를 만들었던걸 깨달아버림..