Parsing RSS Feeds in Flutter

By | December 8, 2019
RSS Feed Parsing In Flutter

RSS Feed Parsing In Flutter


Add Dependencies

The first thing is to add the plugins in the pubspec.yaml file.

We need 4 plugins for this demo.

  1. http package – to get the Feed
  2. webfeed – to parse the RSS/Atom Feed
  3. cached_network_image – to cache the images
  4. url_launcher – to open the feed url in a browser

Your pubspec.yaml file should look like this

dependencies:
    flutter:
        sdk: flutter
    ...

    http: '0.11.3+17'
    webfeed: ^0.4.2
    cached_network_image: ^1.0.0
    url_launcher: ^5.2.5

Get the Feed

 static const String FEED_URL =
      'https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss';
  RssFeed _feed;

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

  load() async {
    loadFeed().then((result) {
      if (null == result || result.toString().isEmpty) {
        updateTitle(feedLoadErrorMsg);
        return;
      }
      updateFeed(result);
    });
  }

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

The above function will download the feed and parse it.

Now we have the parsed feeds, next is just create a listview and show the data in it.
The whole set of below funtions create the title, subtitle, left thumbnail icon and right arrow icon for each list row.
Then finally the list() function creates the list and show the feed.

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

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

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

  rightIcon() {
    return Icon(
      Icons.keyboard_arrow_right,
      color: Colors.grey,
      size: 30.0,
    );
  }

  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.pubDate),
          leading: thumbnail(item.enclosure.url),
          trailing: rightIcon(),
          contentPadding: EdgeInsets.all(5.0),
          onTap: () => openFeed(item.link), // code below
        );
      },
    );
  }

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

  body() {
    return isFeedEmpty()
        ? Center(
            child: CircularProgressIndicator(),
          )
        : RefreshIndicator(
            key: _refreshKey,
            child: list(),
            onRefresh: () => load(),
          );
  }

Open Feed in browser

The Url launcher package will help us here.
The openFeed function will open the url inside a browser. The forceSafariVC property set to true will open the browser inside the app.


  Future<void> openFeed(String url) async {
    if (await canLaunch(url)) {
      await launch(
        url,
        forceSafariVC: true,
        forceWebView: false,
      );
      return;
    }
  }

That’s it for this simple example.


RSS Feed Parsing in  Flutter

RSS Feed Parsing in Flutter


Source code

Here is the full source code.

import 'package:flutter/material.dart';
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 RSSDemo extends StatefulWidget {
  //
  RSSDemo() : super();

  final String title = 'RSS Feed Demo';

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

class RSSDemoState extends State<RSSDemo> {
  //
  static const String FEED_URL =
      'https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss';
  RssFeed _feed;
  String _title;
  static const String loadingFeedMsg = 'Loading Feed...';
  static const String feedLoadErrorMsg = 'Error Loading Feed.';
  static const String feedOpenErrorMsg = 'Error Opening Feed.';
  static const String placeholderImg = 'images/no_image.png';
  GlobalKey<RefreshIndicatorState> _refreshKey;

  updateTitle(title) {
    setState(() {
      _title = title;
    });
  }

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

  Future<void> openFeed(String url) async {
    if (await canLaunch(url)) {
      await launch(
        url,
        forceSafariVC: true,
        forceWebView: false,
      );
      return;
    }
    updateTitle(feedOpenErrorMsg);
  }

  load() async {
    updateTitle(loadingFeedMsg);
    loadFeed().then((result) {
      if (null == result || result.toString().isEmpty) {
        updateTitle(feedLoadErrorMsg);
        return;
      }
      updateFeed(result);
      updateTitle(_feed.title);
    });
  }

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

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

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

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

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

  rightIcon() {
    return Icon(
      Icons.keyboard_arrow_right,
      color: Colors.grey,
      size: 30.0,
    );
  }

  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.pubDate),
          leading: thumbnail(item.enclosure.url),
          trailing: rightIcon(),
          contentPadding: EdgeInsets.all(5.0),
          onTap: () => openFeed(item.link),
        );
      },
    );
  }

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

  body() {
    return isFeedEmpty()
        ? Center(
            child: CircularProgressIndicator(),
          )
        : RefreshIndicator(
            key: _refreshKey,
            child: list(),
            onRefresh: () => load(),
          );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_title),
      ),
      body: body(),
    );
  }
}

Watch the complete youtube tutorial to see parsing RSS Feed in Flutter in action.
Please like, Subsribe and Share if you find my video useful.

Thanks for reading the article.

Please leave your valuable comments below.

6 thoughts on “Parsing RSS Feeds in Flutter

  1. Murat Celik

    Hi. I’m exercises rss parse over your code. I make some changes on my code. i removed cached network image and thumbnail method part and some ui changes .. debug apk everything work. but then i take release apk .. on release apk load method return null. how can i fix that..

    Reply
  2. saadaoui skander

    Please Help Me i found this error after doing this tutorial

    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:1111:7: Error: Struct ‘ENUMLOGFONTEX’ is empty. Empty structs are undefined behavior.
    class ENUMLOGFONTEX extends Struct {
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2835:7: Error: Struct ‘BLUETOOTH_PIN_INFO’ is empty. Empty structs are undefined behavior.
    class BLUETOOTH_PIN_INFO extends Struct {
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2960:7: Error: Struct ‘EXCEPINFO’ is empty. Empty structs are undefined behavior.
    class EXCEPINFO extends Struct {}
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2966:7: Error: Struct ‘PROPERTYKEY’ is empty. Empty structs are undefined behavior.
    class PROPERTYKEY extends Struct {}
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2973:7: Error: Struct ‘PROPVARIANT’ is empty. Empty structs are undefined behavior.
    class PROPVARIANT extends Struct {}
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2978:7: Error: Struct ‘SAFEARRAY’ is empty. Empty structs are undefined behavior.
    class SAFEARRAY extends Struct {}
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2985:7: Error: Struct ‘CLSID’ is empty. Empty structs are undefined behavior.
    class CLSID extends Struct {}
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2992:7: Error: Struct ‘STATSTG’ is empty. Empty structs are undefined behavior.
    class STATSTG extends Struct {}
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/win32-1.7.4+1/lib/src/structs.dart:2999:7: Error: Struct ‘NLM_SIMULATED_PROFILE_INFO’ is empty. Empty structs are undefined behavior.
    class NLM_SIMULATED_PROFILE_INFO extends Struct {}
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-0.1.3/lib/src/utf8.dart:23:7: Error: Struct ‘Utf8’ is empty. Empty structs are undefined behavior.
    class Utf8 extends Struct {
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-0.1.3/lib/src/utf16.dart:16:7: Error: Struct ‘Utf16’ is empty. Empty structs are undefined behavior.
    class Utf16 extends Struct {
    ^
    /C:/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-0.1.3/lib/src/allocation.dart:47:33: Error: Expected type ‘T’ to be a valid and instantiated subtype of ‘NativeType’.
    final int totalSize = count * sizeOf();
    ^

    FAILURE: Build failed with an exception.

    * Where:
    Script ‘C:\flutter\packages\flutter_tools\gradle\flutter.gradle’ line: 991

    * What went wrong:
    Execution failed for task ‘:app:compileFlutterBuildDebug’.
    > Process ‘command ‘C:\flutter\bin\flutter.bat” finished with non-zero exit value 1

    * Try:
    Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output. Run with –scan to get full insights.

    * Get more help at https://help.gradle.org

    BUILD FAILED in 1m 30s
    Exception: Gradle task assembleDebug failed with exit code 1

    Reply
  3. Ajay badole

    The following LateError was thrown building Home(dirty, state: _HomeState#529b4):
    LateInitializationError: Field ‘_feed@23019299’ has not been initialized.

    Reply
    1. James Post author

      Make sure to initialize the variable before using it.

      Reply
  4. Pingback: #Google's Flutter Tutorial – Parsing RSS Feeds (coderzheaven.com) - The Small World

Leave a Reply

Your email address will not be published. Required fields are marked *