Search code examples
androidiosflutterdartrss

Is there a way to fetch multiple RSS feeds in Flutter?


I am trying to set up an RSS feed for my application, so it would be interesting to put several different RSS links into it. However, I've already tried several ways to execute this task, like putting various links directly in a List, and each time I failed and couldn't find a solution anywhere.

So the big question is: is it possible to do this?

import 'package:webfeed/webfeed.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import 'package:cached_network_image/cached_network_image.dart';

class Feed extends StatefulWidget {
  const Feed({Key? key}) : super(key: key);
  final String title = 'Últimas atualizações';

  @override
  State<Feed> createState() => _FeedState();
}

class _FeedState extends State<Feed> {
  static Uri feedUrl = Uri(
    /* 'https://nasa.gov/rss/dyn/lg_image_of_the_day.rss' */
    scheme: 'https',
    host: 'rss.tecmundo.com.br',
    path: 'feed'
  );
  RssFeed _feed = RssFeed(link: 'https://rss.tecmundo.com.br/feed');
  static const String placeholderImg = 'assets/images/noImage.jpg';
  
  late GlobalKey<RefreshIndicatorState> _refreshKey;

  @override
  void initState() {
    super.initState();
    _refreshKey = GlobalKey<RefreshIndicatorState>();
    load();
  }

  // GETTING THE FEED
  Future<RssFeed?> loadFeed() async {
    try {
      final client = http.Client();
      final response = await client.get(feedUrl);
      return RssFeed.parse(response.body);
    } catch (e) {
      //
    }
    return null;
  }

  load() async {
    loadFeed().then((result) {
      if (null == result || result.toString().isEmpty) {
        return showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: const Text('Erro ao carregar.'),
              content: const Text('Tente novamente mais tarde.'),
              actions: [
                TextButton(
                  child: const Text('OK'),
                  onPressed: () {
                    Navigator.pop(context);
                  },
                ),
              ],
            );
          }
        );
      }
      updateFeed(result);
    });
  }

  updateFeed(feed) {
    setState(() {
      _feed = feed;
    });
  }

  Future<void> openFeed (String url) async {
    final Uri uri = Uri.parse(url);
    if(!await launchUrl(uri, mode: LaunchMode.inAppWebView)) {
      print('Error');
    }
  }

  Text title (title) {
    return Text(
      title,
      style: const TextStyle(
        fontSize: 18.0,
        fontWeight: FontWeight.w500,
      ),
      maxLines: 2,
      overflow: TextOverflow.ellipsis,
    );
  }

  Text subtitle (String? subtitle) {
    return Text(
      subtitle ?? '',
      style: const TextStyle(
        fontSize: 14.0,
        fontWeight: FontWeight.w100,
      ),
      maxLines: 1,
      overflow: TextOverflow.ellipsis,
    );
  }

  Padding thumbnail (imageUrl) {
    return Padding(
      padding: const EdgeInsets.only(left: 15.0),
      child: imageUrl != null ? CachedNetworkImage(
        placeholder: (context, url) => Image.asset(placeholderImg),
        imageUrl: imageUrl,
        height: 50,
        width: 50,
        alignment: Alignment.center,
        fit: BoxFit.cover,
      ) :
      SizedBox(
        height: 50,
        width: 50,
        child: Image.asset(placeholderImg),
      ),
    );
  }

  ListView list () {
    return ListView.builder(
      itemCount: _feed.items!.length,
      itemBuilder: (BuildContext context, int index) {
        final item = _feed.items![index];
        return ListTile(
          title: title(item.title),
          subtitle: subtitle(item.dc?.creator),
          leading: thumbnail(item.enclosure!.url),
          trailing: Icon(Icons.bookmark_add_outlined),
          contentPadding: const EdgeInsets.all(5.0),
          onTap: () => openFeed(item.link ?? '')
        );
      },
    );
  }

  bool isFeedEmpty () {
    return null == _feed || null == _feed.items;
  }

  Widget body () => isFeedEmpty() ?
      const Center(
        child: CircularProgressIndicator(),
      ) :
      RefreshIndicator(
        child: list(),
        onRefresh: () => load(),
        key: _refreshKey,
      );
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Latest updates'),
      ),
      body: body(),
    );
  }
}```

Solution

  • You should be able to create multiple RssFeed instances:

    final response1 = await client.get(feed1Url);
    RssFeed feed1 = RssFeed.parse(response1.body);
    
    final response2 = await client.get(feed2Url);
    RssFeed feed2 = RssFeed.parse(response2.body);
    

    Then you could put them in a List

    List<RssFeed> feeds = [feed1,feed2]; // you can do this dynamically of course
    

    And then create a ListView or a Column or a PageView that shows the seperate feeds.

    If you want a more dynamic approach you could do something like this:

    List<Uri> feedURIs = [
      Uri feedUrl = Uri(
        scheme: 'https',
        host: 'rss.tecmundo.com.br',
        path: 'feed'
      ),
      Uri feedUrl = Uri(
        scheme: 'https',
        host: 'nasa.gov',
        path: 'rss/dyn/lg_image_of_the_day.rss'
      ),
    ]
    
    List<RssFeed> feeds = [];
    
    for(Uri uri in feedURIs) {
       final response = await client.get(uri);
       feeds.add(RssFeed.parse(response.body);
    }