LoginSignup
0
0

More than 1 year has passed since last update.

NCMBのFlutter SDKを使ってニュースアプリを作る(その2:ニュースの取得と表示)

Last updated at Posted at 2021-12-10

NCMBでは公式SDKとしてSwift/Objective-C/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回はコミュニティSDKの一つ、Flutter SDKを使ってニュースアプリを作ってみます。前回は画面の仕様とSDKの初期化について解説しました。今回はニュースの取得と、一覧表示までを実装します。

完成版のコード

作成したデモアプリのコードはNCMBMania/flutter_news: Flutter SDKを使ったニュースアプリデモです。にアップロードしてあります。

ニュース記事をNCMBに登録する

まずニュース記事をNCMBに登録します。この時、RSSフィードを直接読み込んでも良いですが、キャッシュであったりデータへのアクセスのしやすさを考えると一旦NCMBに保存する方がお勧めです。

今回はNCMBのスクリプト機能とRSS2JSONを使ってフィードをJSON化しつつ、データストアに取り込んでいます。YOUR_APPLICATION_KEYとYOUR_CLIENT_KEYを、それぞれ皆さんのものと置き換えてください。

// ライブラリの読み込み
const request = require('superagent');
const NCMB = require('ncmb');
// 定数を定義
const applicationKey = 'YOUR_APPLICATION_KEY';
const clientKey = 'YOUR_CLIENT_KEY';

// NCMBの準備
const ncmb = new NCMB(applicationKey, clientKey);
const Feed = ncmb.DataStore('Feed');
const Entry = ncmb.DataStore('Entry');

// メイン処理
module.exports = async (req, res) => {
  if (!req.query.url) {
    return res.json({});
  }
  // キャッシュの検索
  const date = new Date;
  date.setHours(date.getHours() - 1);
  let feed = await Feed.equalTo('url', req.query.url).fetch();
  if (Object.keys(feed).length > 0) {
    if (new Date(feed.fetchDate.iso) > date) {
      res.json({
        objectId: feed.objectId,
        nextFetchDate: new Date(feed.fetchDate.iso)
      });
      return;
    }
  } else {
    feed = new Feed;
    feed.set('url', req.query.url);
  }
  // フィードを取得
  const url = `https://api.rss2json.com/v1/api.json?rss_url=${encodeURI(req.query.url)}`;
  response = await request
    .get(url)
    .send();
  const json = await response.body;
  for (const key in json.feed) {
    if (key !== 'items' && key !== 'url') {
      feed.set(key, json[key]);
    }
  }

  // フィールの中の記事を検索&登録
  const entries = [];
  const relation = new ncmb.Relation();
  for (const item of json.items) {
    let entry = await Entry.equalTo('guid', item.guid).fetch();
    if (Object.keys(entry).length > 0) {
      relation.add(entry);
    }
    entry = new Entry;
    for (const key in item) {
      if (['created', 'updated'].indexOf(key) > -1) {
        entry.set(key, new Date(item[key]));
      } else {
        entry.set(key, item[key]);
      }
    }
    relation.add(entry);
  }

  // フィードを更新
  feed.set('entries', relation);
  feed.set('fetchDate', new Date);
  const method = feed.objectId ? 'update' : 'save';
  try {
    await feed[method]();
    res.json({
      objectId: feed.objectId,
      nextFetchDate: feed.fetchDate
    });
  } catch (e) {
    console.log(e);
    res.json(e);
  }
}

実際に呼び出す際にはGETリクエストで、urlパラメータに対してフィードのURLを指定して実行します。このスクリプトを実行すると、ニュース記事がFeedクラスとEntryクラスに登録されます。

ニュース記事を一覧表示する

登録したニュース記事を一覧表示するのが _MyHomePageState クラスになります。まず利用する変数を用意します。

class _MyHomePageState extends State<MyHomePage> {
  var listItem = [];           // 記事一覧が入る
  var _selectedIndexValue = 0; // 記事一覧か、お気に入り登録記事表示の切り替え用
}

そしてクラスの初期化時に、ニュース記事を取得し、その結果をlistItemに入れます。ニュース記事は日付の降順で並べて、前記事を取得しています。データストアを検索する際にはNCMBQueryクラスを使います。

@override
void initState() {
    super.initState();
    getNews();
}

// ニュースを取得する処理
getNews() async {
    // 匿名認証実行
    await login();
    // ニュース記事が入っているクラスを指定
    var entry = NCMBQuery('Entry');
    // 登録日時の降順
    entry.order('createDate');
    // データ取得
    var ary = await entry.fetchAll();
    // 結果をlistItemに適用
    setState(() {
        listItem = ary;
    });
}

login関数は、匿名認証を使って認証を行っています。セッションはローカルデータに保存されて、次回からそのデータを復元する形になります。そのため、セッションの有効性は確認していません。そこで、復元した際にデータストアにアクセスして、セッションの有効性を確認しています。もし有効でない場合には、元々のUUIDを使って再度匿名認証を実行しています。

login() async {
    // 現在ログインしているユーザデータを取得
    var user = await NCMBUser.currentUser();
    // nullの場合はログインしていない
    if (user == null) {
        // 匿名認証実行
        await NCMBUser.loginAsAnonymous();
    // ログインしている場合はセッションの有効性をチェック
    } else if (!(await user.enableSession())) {
        // セッションが無効だった場合
        // 保存されている認証データを取得
        var authData = user.get('authData') as Map;
        // ログアウトを実行
        await NCMBUser.logout();
        // 再度同じUUIDを使って匿名認証を実行
        await NCMBUser.loginAsAnonymous(id: authData['anonymous']['id']);
    }
}

表示処理

localhost_53010_.png

表示処理を行う画面の内容です。画面上部にはCupertinoSegmentedControlを使って、記事一覧とお気に入り表示を切り替えできるようにしています。記事一覧はListViewを使って表示しています。記事一覧をタップした際にはMaterialPageRouteを使ってMyDetailPageへ遷移します。

@override
Widget build(BuildContext context) {
    return Scaffold(
            appBar: AppBar(title: Text(widget.title)),
            body: Column(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: <Widget>[
                        CupertinoSegmentedControl(
                            children: const {
                                0: Text('All entries'),
                                1: Text('Favorited'),
                            },
                            groupValue: _selectedIndexValue,
                            onValueChanged: (value) async {
                                await changeList(value);
                            },
                        ),
                        Expanded(
                                child: ListView.builder(
                            itemBuilder: (BuildContext context, int index) {
                                var item = listItem[index];
                                return Container(
                                        decoration: const BoxDecoration(
                                            border: Border(
                                                bottom: BorderSide(color: Colors.black38),
                                            ),
                                        ),
                                        child: ListTile(
                                            title: Text(item.get('title')),
                                            subtitle: Text(stripTags(item.get('description')),
                                                    overflow: TextOverflow.ellipsis, maxLines: 2),
                                            onTap: () {
                                                Navigator.push(
                                                        context,
                                                        MaterialPageRoute<void>(
                                                                settings:
                                                                        const RouteSettings(name: '/detail'),
                                                                builder: (BuildContext context) =>
                                                                        MyDetailPage(object: item)));
                                            },
                                        ));
                            },
                            itemCount: listItem.length,
                        ))
                    ]));
}

ここまでで一覧表示機能ができあがります。

まとめ

今回はニュース記事の取得にスクリプトを利用し、取り込んだ記事の表示を行いました。Flutter SDKとしては、記事の表示のみなのでNCMBQueryクラスを使えば簡単にできます。次回は記事の詳細表示(ここはNCMBは使っていません)に加えて、お気に入り登録について解説します。

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