News Aggregator App Flutter

Last Updated : 23 Jul, 2025

To develop an app that can Fetch news and Display them on your mobile screen using Flutter, you need to follow these steps carefully:

Project Directory Structure

Before diving into the code, let's take a look at the directory structure of our project:

Screenshot-2024-09-22-184520

Steps to Create News App in Flutter

Step 1: Create a New Flutter Project

Open your terminal and create a new Flutter project by running the following command:

flutter create news_app

Navigate to the project directory:

cd news_app


Step 2 : Add packages to your pubspec.yaml file

The http package will help us to connect with api :

dependencies:
http:

Step 3 : Create an Article model :

Head over to model folder and create a file , article.dart , this model will contain details of our article , like author , title,imageUrl etc.

article.dart
class Article {
  final Source source;
  final String author;
  final String title;
  final String description;
  final String url;
  final String urlToImage;
  final String publishedAt;

  Article({
    required this.source,
    required this.author,
    required this.title,
    required this.description,
    required this.url,
    required this.urlToImage,
    required this.publishedAt,
  });

  factory Article.fromJson(Map<String, dynamic> json) {
    return Article(
      source: Source.fromJson(json['source']),
      author: json['author'] ?? 'Unknown Author',
      title: json['title'],
      description: json['description'],
      url: json['url'],
      urlToImage: json['urlToImage'] ?? '',
      publishedAt: json['publishedAt'],
    );
  }
}

class Source {
  final String id;
  final String name;

  Source({
    required this.id,
    required this.name,
  });

  factory Source.fromJson(Map<String, dynamic> json) {
    return Source(
      id: json['id'] ?? 'unknown',
      name: json['name'] ?? 'unknown',
    );
  }
}


Step 4 : Create a custom widget NewsCard

This widget will receive an Article object and display the info related to that article.

Dart
import 'package:flutter/material.dart';
import 'package:news_aggregator_app/model/article.dart';

class NewsCard extends StatelessWidget {
  final Article article;

  const NewsCard({super.key, required this.article});

  @override
  Widget build(BuildContext context) {
    final String imageUrl = article.urlToImage;
    final String title = article.title;
    final String description = article.description;
    final String author = article.author;
    final String source = article.source.name;
    final String publishedAt = article.publishedAt;

    return Card(
      elevation: 6,
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
      color: const Color(0xFFF5F5F5),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Image Section
          ClipRRect(
            borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
            child: imageUrl.isNotEmpty
                ? Image.network(
                    imageUrl,
                    height: 200,
                    width: double.infinity,
                    fit: BoxFit.cover,
                  )
                : const SizedBox(
                    height: 200,
                    child: Center(
                      child: Icon(
                        Icons.image_not_supported,
                        size: 50,
                        color: Colors.grey,
                      ),
                    ),
                  ),
          ),
          // Content Section
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // Source and Author
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      source,
                      style: const TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.w600,
                        color: Color(0xFF00796B), // Teal shade
                      ),
                    ),
                    Text(
                      'By $author',
                      style: TextStyle(
                        fontSize: 12,
                        color: Colors.grey[600],
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                // Title
                Text(
                  title,
                  style: const TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                    color: Color(0xFF37474F), // Dark grey shade
                  ),
                ),
                const SizedBox(height: 8),
                // Description
                Text(
                  description,
                  style: TextStyle(
                    fontSize: 14,
                    color: Colors.grey[700],
                  ),
                  maxLines: 3,
                  overflow: TextOverflow.ellipsis,
                ),
                const SizedBox(height: 12),
                // Date Published
                Text(
                  'Published: $publishedAt',
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.grey[500],
                  ),
                ),
              ],
            ),
          ),
          // Button to Read More
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: ElevatedButton(
              onPressed: () {
                // Implement navigation to the article's URL
              },
              style: ElevatedButton.styleFrom(
                backgroundColor:
                    const Color(0xFF009688), // Teal color for button
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12),
                ),
                padding: const EdgeInsets.symmetric(vertical: 12),
              ),
              child: const Center(
                child: Text(
                  'Read More',
                  style: TextStyle(
                    fontSize: 16,
                    color: Colors.white,
                    fontWeight: FontWeight.w600,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }


}


Step 5 : Setting up api :

We are using Newsapi for this project , you can create a free account using your email id :

newsapi


Step 6 : Communicate with api :

In your service Folder create a new file news_service.dart. This file will contain necessary logic for our app to communicate with the external api :

news_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:news_aggregator_app/model/article.dart';

class NewsApiService {
  final String apiKey =
      'Your_api_key'; // Replace with your actual API key

  Future<List<Article>> fetchNews() async {
    final String apiUrl =
        'https://newsapi.org//v2/everything?q=Technology&from=2024-09-10&sortBy=popularity&apiKey=$apiKey';

    final response = await http.get(Uri.parse(apiUrl));

    if (response.statusCode == 200) {
      final Map<String, dynamic> data = jsonDecode(response.body);
      if (data['status'] == 'ok') {
        List articles = data['articles'];
        return articles.map((article) => Article.fromJson(article)).toList();
      } else {
        throw Exception('Failed to load news');
      }
    } else {
      throw Exception('Failed to load news');
    }
  }
}


Don't forget to replace Your_api_key with your actual api key.

Step 7 : Create News page :

Create a new file news_page.dart inside the pages folder, this file has Future builder that will receive a list of Article objects and from the api and display number of NewsCards on the UI:

news_page.dart
import 'package:flutter/material.dart';
import 'package:news_aggregator_app/model/article.dart';
import 'package:news_aggregator_app/model/news_card.dart';
import 'package:news_aggregator_app/services/news_service.dart';

class NewsListPage extends StatefulWidget {
  const NewsListPage({super.key});

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

class _NewsListPageState extends State<NewsListPage> {
  late Future<List<Article>> futureArticles;

  @override
  void initState() {
    super.initState();
    futureArticles = NewsApiService().fetchNews();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('News'),
        backgroundColor: const Color(0xFF00796B),
      ),
      body: FutureBuilder<List<Article>>(
        future: futureArticles,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          } else if (snapshot.hasData) {
            final articles = snapshot.data!;
            return ListView.builder(
              itemCount: articles.length,
              itemBuilder: (context, index) {
                final article = articles[index];
                return NewsCard(article: article);
              },
            );
          } else {
            return const Center(child: Text('No News Found'));
          }
        },
      ),
    );
  }
}



Step 8 : Finally modify the main.dart file :

Call the NewsListPage from the root of the app :

main.dart
import 'package:flutter/material.dart';
import 'package:news_aggregator_app/pages/news_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: NewsListPage(),
    );
  }
}


Step 9 : Running the App

Save all the files and ensure that your project is correctly set up.

Run the app in the terminal:

flutter run

This will launch the app and show a list of recipe cards on the screen.


Output:


You can find the link for the app here : News_Aggregator_Application_Flutter

Comment

Explore