0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【スマホアプリ】Flutterで「英語ニュースを読みながら単語を学べるアプリ」を作った話

Last updated at Posted at 2025-10-07

はじめに

こんにちは。
個人でアプリ開発をしているjunです。
この記事では、私が個人で開発・リリースした英語学習アプリ 「WordTapNews」 の開発背景から技術構成、実装の工夫、そしてリリース後の振り返りまでをまとめます。

アプリ概要:「WordTapNews」とは?

WordTapNews は、
「英語ニュースを読みながら、わからない単語をタップするだけで意味を確認できる」
英語リーディング支援アプリです。

主な特徴

  • 英語ニュースサイト(BBC / NHK World / Reuters など)をアプリ内で読める
  • 英単語をタップすると即座に意味がポップアップ表示
  • 翻訳にはAI(OpenAI API / Google Translate API)を利用
  • 将来的には単語帳・学習履歴機能を追加予定

「ニュース × 辞書 × 習慣化」
をコンセプトに、毎日の英語学習を自然に続けられる体験を目指しています。


開発のきっかけ

私は以前から英語学習を習慣にしているのですが、
「英語ニュースを読みたい」と思っても、知らない単語で詰まることが多くありました。

ニュースアプリを開いても、

  • 辞書アプリをいちいち開くのが面倒
  • 単語の意味を調べても、また記事に戻るのが手間
  • 調べた単語を記録するのを忘れる

という問題があり、気づくと学習が続かない。

そこで、

“ニュースを読む流れの中で、そのまま英語を学べるアプリを作りたい”
と思い立ち、開発をスタートしました。


技術スタック

分類 使用技術
フレームワーク Flutter (Dart)
言語 Dart / JavaScript (WebView側)
データベース Firestore(予定) / SharedPreferences(ローカル)
翻訳API Google Translate API
AI補助 OpenAI API(辞書生成 / 自然な訳語生成)
UIデザイン Figma
配信 App Store(iOS向け)

アーキテクチャ概要

アプリはシンプルな2画面構成です。

  1. HomeScreen

    • ニュースソース(BBC, NHK, Reuters, etc)を選択
    • URLをWebViewに渡して開く
  2. WebViewScreen

    • WebView内で英語ニュースを表示
    • 各単語をクリックできるようにJSをインジェクト
    • クリックされた単語をFlutterに送信
    • Flutter側で辞書APIを叩いて意味を表示

WebView 内で単語をクリック可能にする仕組み

FlutterのInAppWebViewを利用し、
JavaScriptを挿入してすべての英単語を <span> で囲み、クリックイベントを設定しています。

function wrapWords() {
  const textNodes = document.evaluate(
    '//text()[not(ancestor::script) and not(ancestor::style)]',
    document.body,
    null,
    XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
    null
  );

  for (let i = 0; i < textNodes.snapshotLength; i++) {
    const node = textNodes.snapshotItem(i);
    const text = node.nodeValue;
    const words = text.split(/(\b[a-zA-Z']+\b)/g);
    const fragment = document.createDocumentFragment();

    words.forEach(word => {
      if (word.match(/\b[a-zA-Z']+\b/)) {
        const span = document.createElement('span');
        span.textContent = word;
        span.style.cursor = 'pointer';
        span.onclick = () => window.flutter_inappwebview.callHandler('WordTap', word);
        fragment.appendChild(span);
      } else {
        fragment.appendChild(document.createTextNode(word));
      }
    });

    node.parentNode.replaceChild(fragment, node);
  }
}

単語の意味取得と翻訳の流れ

クリックされた単語は Flutter 側に送信され、
_fetchWordDefinition()_translateToJapanese() の順に処理します。

Future<Map<String, dynamic>?> _fetchWordDefinition(String word) async {
  final response = await http.get(
    Uri.parse('https://wordsapiv1.p.rapidapi.com/words/$word'),
    headers: {
      'x-rapidapi-host': 'wordsapiv1.p.rapidapi.com',
      'x-rapidapi-key': dotenv.env['WORDS_API_KEY'] ?? '',
    },
  );
  return response.statusCode == 200 ? json.decode(response.body) : null;
}

Future<String> _translateToJapanese(String text) async {
  final translator = GoogleTranslator();
  final translation = await translator.translate(text, from: 'en', to: 'ja');
  return translation.text;
}

表示UI

取得した結果を英和辞書のように整形して表示します。
英語+日本語訳+例文を組み合わせてユーザーに提示します。

_showWordMeaning(String word) async {
  final definition = await _fetchWordDefinition(word);
  final meaningWidgets = <Widget>[];

  for (var result in definition?['results'] ?? []) {
    final pos = result['partOfSpeech'] ?? 'その他';
    final engDef = result['definition'] ?? '';
    final jaDef = await _translateToJapanese(engDef);

    meaningWidgets.add(Text('$pos: $jaDef'));
  }

  showDialog(
    context: context,
    builder: (_) => AlertDialog(
      title: Text(word),
      content: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: meaningWidgets,
        ),
      ),
      actions: [
        TextButton(onPressed: () => Navigator.pop(context), child: Text('閉じる'))
      ],
    ),
  );
}

今後の機能予定

機能 状況 目的
単語帳(ローカル保存) 実装中 よく調べた単語を復習
Firebaseログ収集 導入予定 UX分析・改善
アカウントログイン 検討中 データ同期・端末間共有
習慣化リマインダー 検討中 英語学習継続支援
プレミアムプラン(広告除去) 検討中 マネタイズ

リリース後の課題と気づき

リリース後すぐに感じたのは 「アプリを簡単には知ってもらえないこと」 ことでした。

改善のために取り組んだこと:

  • X(旧Twitter)で英語ニュース+単語投稿を開始
  • 英語学習系コミュニティで意見交換

結果として、開発者仲間や英語学習者から反応をもらえるようになりました。


振り返りと学び

  • Flutter は UI 構築が非常に早く、個人開発に最適
  • WebView + JavaScript 連携は癖があるが慣れると強力
  • 技術的完成度よりも 「ユーザーがすぐ価値を感じる体験」 が重要
  • SNS を通じた 「開発過程の発信」 がプロモーションになる

関連リンク


まとめ

英語学習は「続けること」が一番難しい。
でも、ニュースを読むだけで英語が身につくなら、続けられると思う。

WordTapNews は、
「英語を読む習慣を自然に続けられるアプリ」を目指してこれからも改善していきます。

個人開発でも、アイデアと粘り強さがあればリリースまで到達できる。
この記事が同じように挑戦する人の参考になれば嬉しいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?