Ad Code

Build a Flutter News App with NewsApi Org

  

How does this App work?

We get breaking news headlines and search for articles from news sources and blogs all over the web with news API and Flutter. We can also browse through different news based on categories.

so let’s get started I have divided this blogs into Steps so that it’s easy to follow, let’s continue

Full App Source codegithub.com/theindianappguy/FlutterNewsApp

1.Create Free Account at NewsApi.org

click on get API Key and then register with first name, email, and pass then check all the checkboxes and click submit.

On the home page, you will also be able to see the response so that we can understand how to structure our HTTP request.

2.Create a new Flutter Project

this can be done via Android Studio, VS Code or Terminal/CMD, make sure you have successfully installed flutter on your system.

3.Create views package/folder inside lib

so this package/folder will store all the screen our app will have

  • home.dart
  • category_news.dart
  • article_view.dart

inside home import material

import 'package:flutter/material.dart';

then create a stateful widget by typing stful and hit enter/return. similarly do for categorie_news.dart and article_view.dart.

update MyHomePage(title:: ‘Flutter Demo Home Page’), -> Home(), make sure to remove MyHomePage Statefull widget.

now to remove debug banner in flutter just add

MaterialApp(
  title: 'NewsBazaar',
  debugShowCheckedModeBanner: false,
  .....
  home: Home(),
);

… dots represent something. it can be theme data or maybe something else.

4.Building home.dart user interface

  • AppBar : To user first return Scaffold rather container in stateful widget build then add appBar inside Scaffold
appBar: AppBar mainAppBar() {
    return AppBar(
    title: Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(
          "Flutter",
          style:
          TextStyle(color: Colors.black87, fontWeight: FontWeight.w600),
        ),
        Text(
          "News",
          style: TextStyle(color: Colors.blue, fontWeight: FontWeight.w600),
        )
      ],
    ),
    backgroundColor: Colors.transparent,
    elevation: 0.0,
  );
 }
  • now we need categories views

for categories section in our flutter news app, I will be using listview so let’s create the custom list view tile.

class CategoryCard extends StatelessWidget {
  final String imageAssetUrl, categoryName;

  CategoryCard({this.imageAssetUrl, this.categoryName});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: (){
        Navigator.push(context, MaterialPageRoute(
          builder: (context) => CategoryNews(
            newsCategory: categoryName.toLowerCase(),
          )
        ));
      },
      child: Container(
        margin: EdgeInsets.only(right: 14),
        child: Stack(
          children: <Widget>[
            ClipRRect(
              borderRadius: BorderRadius.circular(5),
              child: CachedNetworkImage(
                imageUrl: imageAssetUrl,
                height: 60,
                width: 120,
                fit: BoxFit.cover,
              ),
            ),
            Container(
              alignment: Alignment.center,
              height: 60,
              width: 120,
              decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(5),
                color: Colors.black26
              ),
              child: Text(
                categoryName,
                textAlign: TextAlign.center,
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 14,
                    fontWeight: FontWeight.w500),
              ),
            )
          ],
        ),
      ),
    );
  }
}

now list tile is done lets create a data file to add and fetch category name and url

create a helper package/folder inside the lib then add this funtion which basically add categories via a custom class CategorieModel and we can get data by calling this function.

List<CategorieModel> getCategories(){

  List<CategorieModel> myCategories = List<CategorieModel>();
  CategorieModel categorieModel;

  //1
  categorieModel = new CategorieModel();
  categorieModel.categorieName = "Business";
  categorieModel.imageAssetUrl = "https://images.unsplash.com/photo-1507679799987-c73779587ccf?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1502&q=80";
  myCategories.add(categorieModel);

  //2
  categorieModel = new CategorieModel();
  categorieModel.categorieName = "Entertainment";
  categorieModel.imageAssetUrl = "https://images.unsplash.com/photo-1522869635100-9f4c5e86aa37?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80";
  myCategories.add(categorieModel);

  //3
  categorieModel = new CategorieModel();
  categorieModel.categorieName = "General";
  categorieModel.imageAssetUrl = "https://images.unsplash.com/photo-1495020689067-958852a7765e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60";
  myCategories.add(categorieModel);

  //4
  categorieModel = new CategorieModel();
  categorieModel.categorieName = "Health";
  categorieModel.imageAssetUrl = "https://images.unsplash.com/photo-1494390248081-4e521a5940db?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1595&q=80";
  myCategories.add(categorieModel);

  //5
  categorieModel = new CategorieModel();
  categorieModel.categorieName = "Science";
  categorieModel.imageAssetUrl = "https://images.unsplash.com/photo-1554475901-4538ddfbccc2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1504&q=80";
  myCategories.add(categorieModel);

  //5
  categorieModel = new CategorieModel();
  categorieModel.categorieName = "Sports";
  categorieModel.imageAssetUrl = "https://images.unsplash.com/photo-1495563923587-bdc4282494d0?ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80";
  myCategories.add(categorieModel);

  //5
  categorieModel = new CategorieModel();
  categorieModel.categorieName = "Technology";
  categorieModel.imageAssetUrl = "https://images.unsplash.com/photo-1519389950473-47ba0277781c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80";
  myCategories.add(categorieModel);

  return myCategories;

}

this will show error because we have not created the CategorieModel class so let’s do that.

create models package/folder insider lib then create a file called categorie_model.dart(just the name of the file can be whatever you want).

create class with two variable one for category name and one for image url

class CategorieModel {
  String imageAssetUrl;
  String categorieName;

}

now we have function which adds data visit data file and import this class and the error will be gone now we can fetch data in home.dart by calling the function in data.dart

inside _HomePageState we will create list to store list of categories and call the function in initState to get list of categories as soon as this widget loads.

List<CategorieModel> categories = List<CategorieModel>();

  @override
  void initState() {
    super.initState();
    categories = getCategories(); 
  }

since in our home screen, we want to show categories list (which is a horizontal list) and news list (vertical) in vertical arrangement one above the other we will create a column and then will have listview.builder as a child for categories.

/// this is inside column in body:
Container(
  padding: EdgeInsets.symmetric(horizontal: 16),
  height: 70,
  child: ListView.builder(
      scrollDirection: Axis.horizontal,
      itemCount: categories.length,
      itemBuilder: (context, index) {
        return CategoryCard(
          imageAssetUrl: categories[index].imageAssetUrl,
          categoryName: categories[index].categorieName,
        );
      }),
),

Create Function to Fetch News from NewsApi.Org

first, we need to add two packages to pubspect.yaml in main project directory below “cupertino_icons: ^0.1.2”

http: ^0.12.0+4
cached_network_image: ^2.0.0

do pub get, then create news.dart file inside helper package/folder for this we will need a model.

so create article_model.dart inside models package/folder and create a class with all the variables the API is providing.

class Article{

  String title;
  String author;
  String description;
  String urlToImage;
  DateTime publshedAt;
  String content;
  String articleUrl;

  Article({this.title,this.description,this.author,this.content,this.publshedAt,
    this.urlToImage, this.articleUrl});
}

now open news.dart in helper package/folder.

Inside this, we will create a class News we will create a list of ArticleModel named news. To fetch data through API we will require URL so we will create a variable for its URL,

All this will be done inside a function to get the news which can be called when we want to fetch data.

now we will import http then we will perform http.get request and save that to var response.

then we will decode the response and save it to jsonData. then we make sure if status is “ok”.

one we know status is ok we will run a for each loop to “articles” key and with some condition we will save it the list of articles. here is the full code.

import 'package:http/http.dart' as http;
import 'package:news_app_api/models/article.dart';
import 'dart:convert';

import 'package:news_app_api/secret.dart';

class News {

  List<Article> news  = [];

  Future<void> getNews() async{

    String url = "http://newsapi.org/v2/top-headlines?country=in&excludeDomains=stackoverflow.com&sortBy=publishedAt&language=en&apiKey=${apiKey}";

    var response = await http.get(url);

    var jsonData = jsonDecode(response.body);

    if(jsonData['status'] == "ok"){
      jsonData["articles"].forEach((element){

        if(element['urlToImage'] != null && element['description'] != null){
          Article article = Article(
            title: element['title'],
            author: element['author'],
            description: element['description'],
            urlToImage: element['urlToImage'],
            publshedAt: DateTime.parse(element['publishedAt']),
            content: element["content"],
            articleUrl: element["url"],
          );
          news.add(article);
        }

      });
    }
  }
}

i have also created a secret file inside lib to store apikey which you can get from your NewsApi.org account just click on right hand side on your email.

Fetch Data from NewsAPI and Show in a ListView

  • create list tile for News List View

for the list view first, let’s create a list tile which will be BlogTile add this at home. dart below our home widget.

class NewsTile extends StatelessWidget {
  final String imgUrl, title, desc, content, posturl;

  NewsTile({this.imgUrl, this.desc, this.title, this.content, @required this.posturl});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: (){
        Navigator.push(context, MaterialPageRoute(
            builder: (context) => ArticleView(
              postUrl: posturl,
            )
        ));
      },
      child: Container(
          margin: EdgeInsets.only(bottom: 24),
          width: MediaQuery.of(context).size.width,
          child: Container(
            child: Container(
              padding: EdgeInsets.symmetric(horizontal: 16),
              alignment: Alignment.bottomCenter,
              decoration: BoxDecoration(
                  borderRadius: BorderRadius.only(bottomRight: Radius.circular(6),bottomLeft:  Radius.circular(6))
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  ClipRRect(
                      borderRadius: BorderRadius.circular(6),
                      child: Image.network(
                        imgUrl,
                        height: 200,
                        width: MediaQuery.of(context).size.width,
                        fit: BoxFit.cover,
                      )),
                  SizedBox(height: 12,),
                  Text(
                    title,
                    maxLines: 2,
                    style: TextStyle(
                        color: Colors.black87,
                        fontSize: 20,
                        fontWeight: FontWeight.w500),
                  ),
                  SizedBox(
                    height: 4,
                  ),
                  Text(
                    desc,
                    maxLines: 2,
                    style: TextStyle(color: Colors.black54, fontSize: 14),
                  )
                ],
              ),
            ),
          )),
    );
  }
  • Fetch Data and Show in List View

now lest use function at home.dart, create a variable news list and a function getNews(); and call this inside the initState function.

 var newslist;

 void getNews() async {
    News news = News();
    await news.getNews();
    newslist = news.news;
    setState(() {
      _loading = false;
    });
  }

  @override
  void initState() {
    _loading = true;
    // TODO: implement initState
    super.initState();

    categories = getCategories();
    getNews();
  }

now time for list view

/// News Article
Container(
  margin: EdgeInsets.only(top: 16),
  child: ListView.builder(
      itemCount: newslist.length,
      shrinkWrap: true,
      physics: ClampingScrollPhysics(),
      itemBuilder: (context, index) {
        return NewsTile(
          imgUrl: newslist[index].urlToImage ?? "",
          title: newslist[index].title ?? "",
          desc: newslist[index].description ?? "",
          content: newslist[index].content ?? "",
          posturl: newslist[index].articleUrl ?? "",
        );
      }),
)

Finally if not already run the app this is what we can expect.

Now we want when the user will click any article it should open in webview. Add this to your package’s pubspec.yaml file:

dependencies:
  webview_flutter: ^0.3.22+1

Since we already have added Navigation to ArticleView on BlogTile click,

Hence open ArticleView screen and add this code.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class ArticleView extends StatefulWidget {

  final String postUrl;
  ArticleView({@required this.postUrl});

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

class _ArticleViewState extends State<ArticleView> {

  final Completer<WebViewController> _controller = Completer<WebViewController>();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "Flutter",
              style:
              TextStyle(color: Colors.black87, fontWeight: FontWeight.w600),
            ),
            Text(
              "News",
              style: TextStyle(color: Colors.blue, fontWeight: FontWeight.w600),
            )
          ],
        ),
        actions: <Widget>[
          Opacity(
           opacity:0,
           Container(
            padding: EdgeInsets.symmetric(horizontal: 16),
              child: Icon(Icons.share,))
        ],
        backgroundColor: Colors.transparent,
        elevation: 0.0,
      ),
      body: Container(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        child: WebView(
          initialUrl:  widget.postUrl,
          onWebViewCreated: (WebViewController webViewController){
            _controller.complete(webViewController);
          },
        ),
      ),
    );
  }
}

In this screen we have an app bar and in body we have a container with WebView with initial url which we got from the home.dart.

FOR IOS : go to ios > runner > info.plist then add

<key>io.flutter.embedded_views_preview</key>
<string>YES</string>

Run and You will be able to open the news articles in webview.

Next step is to create category section.

so in the news.dart we will create a class which will be just a copy of the same function we created before, but just with different URL.

class NewsForCategorie {

  List<Article> news  = [];

  Future<void> getNewsForCategory(String category) async{

    /*String url = "http://newsapi.org/v2/everything?q=$category&apiKey=${apiKey}";*/
    String url = "http://newsapi.org/v2/top-headlines?country=in&category=$category&apiKey=${apiKey}";

    var response = await http.get(url);

    var jsonData = jsonDecode(response.body);

    if(jsonData['status'] == "ok"){
      jsonData["articles"].forEach((element){

        if(element['urlToImage'] != null && element['description'] != null){
          Article article = Article(
            title: element['title'],
            author: element['author'],
            description: element['description'],
            urlToImage: element['urlToImage'],
            publshedAt: DateTime.parse(element['publishedAt']),
            content: element["content"],
            articleUrl: element["url"],
          );
          news.add(article);
        }

      });
    }


  }

now categorie.dart is going to be simply using the same app bar which we are using in the article view with the same list view as we are using at home.dart.

And we will be accepting newsCategory from home.dart when someone will click on any categorie.

import 'package:flutter/material.dart';
import 'package:news_app_api/helper/news.dart';
import 'package:news_app_api/helper/widgets.dart';

class CategoryNews extends StatefulWidget {

  final String newsCategory;

  CategoryNews({this.newsCategory});

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

class _CategoryNewsState extends State<CategoryNews> {
  var newslist;
  bool _loading = true;

  @override
  void initState() {
    getNews();
    // TODO: implement initState
    super.initState();
  }

  void getNews() async {
    NewsForCategorie news = NewsForCategorie();
    await news.getNewsForCategory(widget.newsCategory);
    newslist = news.news;
    setState(() {
      _loading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "Flutter",
              style:
              TextStyle(color: Colors.black87, fontWeight: FontWeight.w600),
            ),
            Text(
              "News",
              style: TextStyle(color: Colors.blue, fontWeight: FontWeight.w600),
            )
          ],
        ),
        actions: <Widget>[
          Opacity(
            opacity: 0,
            child: Container(
                padding: EdgeInsets.symmetric(horizontal: 16),
                child: Icon(Icons.share,)),
          )
        ],
        backgroundColor: Colors.transparent,
        elevation: 0.0,
      ),
      body: _loading ? Center(
        child: CircularProgressIndicator(),
      ) : SingleChildScrollView(
        child: Container(
            child: Container(
              margin: EdgeInsets.only(top: 16),
              child: ListView.builder(
                  itemCount: newslist.length,
                  shrinkWrap: true,
                  physics: ClampingScrollPhysics(),
                  itemBuilder: (context, index) {
                    return NewsTile(
                      imgUrl: newslist[index].urlToImage ?? "",
                      title: newslist[index].title ?? "",
                      desc: newslist[index].description ?? "",
                      content: newslist[index].content ?? "",
                      posturl: newslist[index].articleUrl ?? "",
                    );
                  }),
            ),
        ),
      ),
    );
  }
}

If you face any problem i will highly recommend to follow along with video Build a Flutter News App with NewsApi Org.

So the app is completed if you face any problem feel free to share it in the comments I will try to help as much I can.

Reactions

Post a Comment

0 Comments