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?

More than 1 year has passed since last update.

Flutter でアプリを作ってみよう #3

Posted at

RSSリーダーの実装

今回はメイン機能のRSSのリストを実装していく。
引っ張ってくるのはこちらのサイト↓
https://iflyer.tv/
RSSfeedの提供があるのでありがたく使わせていただく。 
サイトによって使用時の注意事項や利用規約などがある場合があるので要注意。 
流れとしては、

  1. httpパッケージを使ってxmlファイルを取得
  2. webfeedパッケージを使ってFeedのitemに変換
  3. よしなに使う
rss_item.dart
class Rss {
  const Rss({
    required this.linkUrl,
    required this.title,
    required this.description,
  });
  final String linkUrl;
  final String title;
  final String description;
}
main.dart
import 'package:eve_search/rss_item.dart';
import 'dart:convert';
import 'package:webfeed_plus/webfeed_plus.dart';
import 'package:http/http.dart' as http;

class _MyHomePageState extends State<MyHomePage> {
  String _selectArea = '';
  List<Rss> onlyHiphopList = [];

  Future<List<Rss>> fetchFeed() async {
    final response = await http
      .get(Uri.parse('https://iflyer.tv/rss/events/'));

    if (response.statusCode != 200) {
      throw Exception('Failed to fetch');
    }
    final rssFeed = RssFeed.parse(utf8.decode(response.bodyBytes));
    final rssItemlist = rssFeed.items ?? <RssItem>[];
    final allCategoryList = rssItemlist
      .map(
        (item) => Rss(
          linkUrl: item.link ?? '',
          title: item.title ?? '',
          description: item.description ?? '',
        ),
      ).toList();
      //hot reloadの度にリストに重複して追加されてしまうのを防ぐため。
      onlyHiphopList = [];
      //descriptionに’Hip Hop’を含んでいるインスタンスのみonlyHiphopListに追加する
      for(int i = 0; i <= allCategoryList.length; i++){
        if(allCategoryList[i].description.contains('Hip Hop')){
          onlyHiphopList.add(allCategoryList[i]);
        }
      }
      return onlyHiphopList;
  }

準備完了。

サムネイル(OGP画像)のURLを遷移先から取得する

こちらもogp_data_extractという便利なパッケージがある。
引数に遷移先のURLを入れると、OGP画像のURLが返ってくるというお手軽設計。

import 'package:ogp_data_extract/ogp_data_extract.dart';

Future<String?> getOGPImageUrl(String url) async {
  final data = await OgpDataExtract.execute(url);
  return data?.image;
}

用意したこれらをFutureBuilderListView.builderを使い、あとレイアウトも少し整えて実装していく。

main.dart
Scaffold(
  appBar: AppBar(
    centerTitle: true,
    title: Text( widget.title,),
  ),
  body: SingleChildScrollView(
    child: Column(
      children: [
        Container(
          padding: const EdgeInsets.only(left: 24, top: 8, bottom: 4),
          alignment: Alignment.centerLeft,
          child: Row(
            children: [
              const Text(
                '全国',
                style: TextStyle(fontSize: 16),
              ),
              Text(_selectArea,
                style: const TextStyle(fontSize: 16),
              ),
            ],
          ),
        ),
        ElevatedButton(
          onPressed: () async{ 
            String selectArea = await showModalBottomSheet(
              context: context,
              isScrollControlled: true, 
              shape: const RoundedRectangleBorder(
                borderRadius: BorderRadius.vertical(top: Radius.circular(10)),
              ),
              builder: (BuildContext context) {
                return const PrefecturesWindow();
              },
            );
            setState(() {
              _selectArea = selectArea;
            });
          },
          child: const Text('エリアを選択する',),
        ),
        const SizedBox(height: 16,),
        SizedBox(
          height: 550,
          width: MediaQuery.of(context).size.width,
          child: FutureBuilder(
            future: fetchFeed(),
            builder: (context, snapshot) {
              return ListView.builder(
                itemCount: onlyHiphopList.length,
                itemBuilder: (context, index) {
                  return Card(
                    child: ListTile(
                      trailing: FutureBuilder<String?>(
                        future: getOGPImageUrl(onlyHiphopList[index].linkUrl),
                        builder: (context, snapshot) {
                          if(snapshot.hasError){
                            final error  = snapshot.error;
                            return Text('$error', style: const TextStyle(fontSize: 12,),);
                          }else if (snapshot.hasData) {
                            String result = snapshot.data!;
                            return Container(
                              padding: const EdgeInsets.symmetric(vertical: 4),
                              child: Image.network(
                                width: 80,
                                height: 80,
                                fit: BoxFit.cover,
                                result
                              ),
                            );
                          } else {
                              return  const SizedBox(
                                height: 20,
                                width: 20,
                                child: CircularProgressIndicator()
                              );
                          }
                        }
                      ),
                      title: Text(onlyHiphopList[index].title.toString()),
                      onTap:(){

                      },
                    ),
                  );
                },
              );
            }
          ),
        ),
      ],
    ),
  ),
);

するとこんな感じ
Screenshot_20230621_223237.jpg

画像の表示高速化

画像なので伝わらないが、サムネイルの表示スピードが遅いのがかなり気になる。
https://zenn.dev/flutteruniv_dev/articles/20220615-160504-flutter-cached-network-image-test
↑こちらにかなりわかりやすく改善方法が書いてあったのでパク...参考にさせて頂く。
cached_network_imageパッケージと、
flutter_cache_managerパッケージを使えば改善可能とのこと。

cached_image.dart
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';

class CachedImage extends StatefulWidget {
  const CachedImage({
    super.key,
    required this.url,
    this.cacheManager,
    this.useCache = true,
    this.showLoading = false,
  });

  final String url;
  final CacheManager? cacheManager;
  final bool useCache;
  final bool showLoading;

  @override
  CachedImageState createState() => CachedImageState();
}

@visibleForTesting
class CachedImageState extends State<CachedImage> {
  final imageKey = GlobalKey(debugLabel: 'CachedImage');

  ImageProvider<Object>? get imageProvider => _imageProvider;
  ImageProvider<Object>? _imageProvider;

  dynamic get error => _error;
  dynamic _error;

  CacheManager get _defaultCacheManager => CacheManager(
        Config(
          'CachedImageKey',
          stalePeriod: const Duration(days: 1),
          maxNrOfCacheObjects: 20,
        ),
      );

  @override
  Widget build(BuildContext context) {
    if (!widget.useCache) {
      return Image(
        key: imageKey,
        image: NetworkImage(widget.url),
        loadingBuilder: widget.showLoading
            ? (context, child, progress) {
                if (progress == null) {
                  return child;
                }
                return const Center(
                  child: CircularProgressIndicator(),
                );
              }
            : null,
      );
    }
    return CachedNetworkImage(
      cacheManager: widget.cacheManager ?? _defaultCacheManager,
      imageUrl: widget.url,
      imageBuilder: (context, imageProvider) {
        _imageProvider = imageProvider;
        return Image(
          key: imageKey,
          image: imageProvider,
        );
      },
      placeholder: widget.showLoading
          ? (context, url) => const Center(
                child: CircularProgressIndicator(),
              )
          : null,
      errorWidget: (context, url, dynamic error) {
        _error = error;
        return Icon(
          Icons.error,
        );
      },
    );
  }
}
main.dart
- child: Image.network(
-   width: 80,
-   height: 80,
-   fit: BoxFit.cover,
-   result
- ),
+ child: CachedImage(
+   url: result,
+ ),

結果:かなり早くなった。
今回はここまで。

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?