You are currently viewing Make a News App in Flutter with Retrofit
flutter_news_app

Make a News App in Flutter with Retrofit

Nowadays network calling is essential in almost every app. SO, in this blog we will learn about how we can use “Retrofit” in flutter to fetch data through API and show it to user.

flutter_news_app

Pre-requisite: I hope you have set up flutter environment in your pc. So, I am directly jumping into the project.

Let’s Start

Step 1 : Create a new flutter project.

Step 2 : Add the below dependencies in pubspec.yaml file.

dependencies:
  flutter:
    sdk: flutter
  retrofit: 3.0.1+1
  dio: 4.0.6
  built_value: 8.3.2
  json_annotation: 4.5.0
  flutter_speed_dial: ^6.0.0
  fluttertoast: ^8.0.9
  transparent_image: ^2.0.0

Now, run the command “flutter pub get” to download the dependencies.

Step 3 : Then we will create Model class named “” for API response for Headlines.

class HeadlinesResponse {
  String? status;
  int? totalResults;
  List<Articles>? articles;

  HeadlinesResponse({this.status, this.totalResults, this.articles});

  HeadlinesResponse.fromJson(Map<String, dynamic> json) {
    status = json['status'];
    totalResults = json['totalResults'].toInt();
    if (json['articles'] != null) {
      articles = <Articles>[];
      json['articles'].forEach((v) {
        articles!.add(new Articles.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['status'] = this.status;
    data['totalResults'] = this.totalResults;
    if (this.articles != null) {
      data['articles'] = this.articles!.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Articles {
  Source? source;
  String? author;
  String? title;
  String? description;
  String? url;
  String? urlToImage;
  String? publishedAt;
  String? content;

  Articles(
      {this.source,
        this.author,
        this.title,
        this.description,
        this.url,
        this.urlToImage,
        this.publishedAt,
        this.content});

  Articles.fromJson(Map<String, dynamic> json) {
    source =
    json['source'] != null ? new Source.fromJson(json['source']) : null;
    author = json['author'];
    title = json['title'];
    description = json['description'];
    url = json['url'];
    urlToImage = json['urlToImage'];
    publishedAt = json['publishedAt'];
    content = json['content'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (this.source != null) {
      data['source'] = this.source!.toJson();
    }
    data['author'] = this.author;
    data['title'] = this.title;
    data['description'] = this.description;
    data['url'] = this.url;
    data['urlToImage'] = this.urlToImage;
    data['publishedAt'] = this.publishedAt;
    data['content'] = this.content;
    return data;
  }
}

class Source {
  String? id;
  String? name;

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

  Source.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['name'] = this.name;
    return data;
  }
}

Step 4 : Now, let’s create an abstract API request class.

import 'package:dart_news/Models/HeadlinesResponse.dart';
import 'package:dio/dio.dart';
import 'package:retrofit/http.dart';
part 'api_client.g.dart';

@RestApi(baseUrl: "https://newsapi.org/")
abstract class ApiClient {
  factory ApiClient(Dio dio, {String baseUrl}) = _ApiClient;

  @GET('v2/top-headlines')
  Future<HeadlinesResponse> getHeadlines(
      @Query("country") String country,
      @Query("apiKey") String apiKey
      );
}

Right now, you will observe some errors in the file in “part ‘api_client.g.dart’ ” and _ApiClient.

We need to run a command and generate an “api_client.g.dart” file.

Step 5 : Now run the command in the terminal

“flutter pub run build_runner build

The above command will generate the api_client.g.dart file & contains all data about creating Retrofit instance and fetching data from the network.

You should never modify the code in api_client.g.dart file by hand.

If you made any changes in ApiClient then also run the command to update part file.

Step 6 : Now, Let’s call the get method from main.dart class with a FutureBuilder

FutureBuilder<HeadlinesResponse> _getHeadlines(BuildContext context) {

    final client = ApiClient(Dio(BaseOptions(contentType: "application/json")));
    return FutureBuilder<HeadlinesResponse>(

      future: client.getHeadlines(country, apiKey),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          final HeadlinesResponse? posts = snapshot.data;
          return _buildHeadlineList(context, posts!);
        } else {
          return const Center(
            child: CircularProgressIndicator(
              color: Colors.red,
              // backgroundColor: Colors.white,
            ),
          );
        }
      },
    );
  }

Step 7 : Now we can create the _buildHeadlineList() method to show the list in a widget.

ListView _buildHeadlineList(BuildContext context, HeadlinesResponse posts) {
    List<Articles>? list = posts.articles;
    return ListView.builder(
      itemCount: posts.articles?.length,
      scrollDirection: Axis.vertical,
      shrinkWrap: true,
      padding: EdgeInsets.all(8),
      itemBuilder: (context, index) {
        return InkWell(
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => ArticlePage(article: list![index],)),
            );
          },
          child: Card(
            color: Colors.white,
            elevation: 8,
            margin: EdgeInsets.all(8),
            child: Padding(
              padding: EdgeInsets.all(8),
              child: Column(
                children: [
                  Text(list![index].title.toString(), style: AppTheme.titleText,),
                  const SizedBox(
                    height: 8,
                  ),
                  Container(
                    height: 140,
                    child: Row(
                      children: [
                        FadeInImage.memoryNetwork(
                          image: list[index].urlToImage.toString(),
                          width: 130,
                          height: 130,
                          placeholder: kTransparentImage,
                          imageErrorBuilder:
                              (context, error, stackTrace) {
                            return Image.asset(
                                'assets/images/flag_placeholder.jpg',
                                width: 130,
                                height: 130,
                                fit: BoxFit.fitWidth);
                          },
                          fit: BoxFit.fitHeight,
                        ),
                        const SizedBox(
                          width: 8,
                        ),
                        Expanded(child: Column(children: [
                          Expanded(child: Text(list[index].description.toString(), style: AppTheme.subtitleText,)),
                          const SizedBox(
                            height: 10,
                          ),
                          Row(children: [Expanded(child: Text("Read More..", style: AppTheme.readMoreText, textAlign: TextAlign.right,),)],)
                        ],))
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }

Here I have used a Placeholder image and some custom text styles.

For the placeholder image you can use any image and set it’s path to the parameter.

My custom AppTheme class:

import 'dart:ui';

import 'package:flutter/material.dart';

class AppTheme{
  static final TextStyle subtitleText =
  TextStyle(fontSize: 14.0,fontWeight:FontWeight.normal, color: Colors.black,fontFamily: "Roboto");

  static final TextStyle titleText =
  TextStyle(fontSize: 20.0,fontWeight:FontWeight.bold, color: Colors.red,fontFamily: "Roboto");

  static final TextStyle readMoreText =
  TextStyle(fontSize: 14.0,fontWeight:FontWeight.bold, color: Colors.red,fontFamily: "Roboto");
}

Step 8 : Now just call the _getHeadlines(context) method in your build methods body:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: _getHeadlines(context),
    );
  }

Finally you can run the code and see below result:

Hope this blog helped you for understanding Retrofit API calling in flutter.

Source code: https://github.com/evanemran/DartNews
Video Tutorials: http://youtube.com/codingwithevan
Thank you for reading.

Leave a Reply