
Creating a News App in Flutter using ChatGPT
24 May, 2023
9
9
0
Contributors
Introduction
Hey everyone, I am Hasnain Makada and I am currently a Founding Creator, a CCO as well as a Rotational Tech Writer at Showwcase. I also maintain an open-source project named "Open Source with Hasnain" where I provide resources for beginners to seasoned developers. I am also currently an MLSA - Beta where I explore and learn new things from Microsoft and educate my community as well.
Today, In this blog, I am going to show you how I created a news app in Flutter in under 2 hours with the help of chatGPT
I am going to show you how I implemented the Ui, how I implemented the logic and how I managed the state using riverpod 2.0.
So without wasting any furthermore time, Let's get started π₯
Gathering the requirements
When developing any application first, we first gather the requirements, Like which dependencies we're going to use, which state management libraries we're going to use etc...
So the initial gathering criteria was easy for me, these are the dependencies we're going to use inside our app.
flutter_dotenv
- To load environment variables, such as API key.flutter_riverpod
- For managing state across the app.http
- Handling API calls from the news API which we are going to use.url_launcher
- For launching URLs in an external browser
Now that our dependencies are gathered, create a new Flutter app by running the below command and all of these dependencies inside the pubspec.yaml
file.
flutter create innews
I am currently using the latest Flutter version inside which there are a lot of Ui changes are there which you can check out here π
Designing the UI
The design is pretty much simple, in the home screen a box will and it will include the image, title and description of the news inside it, once the user clicks on it, it will redirect the user to the main article screen. If the user wants to read the full news, it will also provide a link to the full article.
Here is the rough sketch of the Ui,
Initially, when I designed the UI, I was confused that how should I start it. So I had gone to chatGPT and I prompted it the design I wanted
ChatGPT helped me generate the newsItem layout which I wanted, Although I modified the layout a little bit, it was great and after modifying this was the end result.
Here is the code π and its output.
return Card(
elevation: 2,
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 30),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArticleDetailScreen(article: article),
),
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 200,
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(article.imageUrl),
fit: BoxFit.cover,
),
),
),
const SizedBox(height: 8),
Text(
article.title,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 8),
Text(
article.description,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
article.sourceName,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
);
Now that our NewsItem
widget was successfully designed, It was time to design the Article detail screen, if a user clicks on any area of the NewsItem
widget, it should redirect him/her to the article screen.
So I again prompted ChatGPT for designing the Article detail screen,
ChatGPT generated for me the article, but It needed some modifications so I modified it a little and the end result was good.
Here's the code π
class ArticleDetailScreen extends StatelessWidget {
final NewsArticle article;
ArticleDetailScreen({required this.article});
Future<void> _launchURL(String url) async {
if (!await launchUrl(
Uri.parse(url),
mode: LaunchMode.externalApplication,
)) {
throw Exception('Could not launch $url');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Article Details'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(article.imageUrl),
Text(
article.title,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
ExpandableText(
article.content
.toString()
.replaceAll(RegExp(r'\[\+\d+ chars\]$'), ''),
style: const TextStyle(fontSize: 16),
expandText: 'Show more',
collapseText: 'Show less',
maxLines: 3,
expanded: false,
),
const SizedBox(height: 16),
GestureDetector(
onTap: () {
_launchURL(article.url);
},
child: const Text(
'Read More',
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
),
const SizedBox(height: 16),
Text(
'Source: ${article.sourceName}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Text(
'Published At: ${article.publishedAt}',
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 16),
Text(
'Author: ${article.author}',
style: const TextStyle(fontSize: 14),
),
],
),
),
),
);
}
}
And here is the output π
So far now our app ui is almost completed, Now it's time to work on the logic.
Creating the logic
For creating the logic of the app, I used the NewsAPI site to get the API, It's really simple to get started with it.
Fetching the News API
Now I wanted to use Riverpod for managing the state across the app because I always try to manage the state of my app dynamically and don't want to go with the traditional setState
the method as it becomes difficult over a period of time.
As our riverpod dependency is already installed, create a new directory inside your /lib
folder as our models and logic will reside over there.
After creating the directory, create a new file named NewsApi.dart
And inside the file, paste this code π
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
class NewsApiClient {
final http.Client client;
NewsApiClient(this.client);
Future<String> fetchNews() async {
final response = await client.get(
Uri.parse(
'https://newsapi.org/v2/everything?q=apple&from=2023-05-19&to=2023-05-19&sortBy=popularity&apiKey=${dotenv.env['newapikey']}'),
);
if (response.statusCode == 200) {
return response.body;
} else {
throw Exception('Failed to fetch the news');
}
}
}
final newsApiClientProvider = Provider<NewsApiClient>((ref) {
return NewsApiClient(http.Client());
});
final newsProvider = FutureProvider<String>(
(ref) async {
final newsApiClient = ref.read(newsApiClientProvider);
return await newsApiClient.fetchNews();
},
);
We first created a NewsApiClient
class which will take the help of thehttp.Client
instance and will get the data from our API.
After that, we created a Future<String> fetchNews
method which will asynchronously wait for our API to get fetched successfully, and if our API was successfully got, then it will return the body of the API else it will throw an error.
Then we created a newsApiClientProvider
which will use the Provider
object to expose the state of http
to the NewsApiClient
class
In the end, we created a newsProvider
which will read the data from the NewsApiClient
and it will return the response in String format. Because as we now have access to the client provider then we can access the methods of the NewsApiClient
class.
Creating the Article Model
When I first checked the response of my API, It was like this π
{
"status": "ok",
"totalResults": 1972,
"articles": [
{
"source": {
"id": "engadget",
"name": "Engadget"
},
"author": "Mat Smith",
"title": "The Morning After: Meta could launch a Twitter competitor next month",
"description": "Meta has long been rumored to be building a platform to rival Twitter. After months of speculation, there are finally some details on how it might turn out, according to digital media marketing expert Lia Haberman, who has heard about the app through content β¦",
"url": "https://www.engadget.com/the-morning-after-meta-could-launch-a-twitter-competitor-next-month-111543954.html",
"urlToImage": "https://s.yimg.com/uu/api/res/1.2/nbbyqy5Ca.eCKGzLfcl5CA--~B/Zmk9ZmlsbDtoPTYzMDtweW9mZj0wO3c9MTIwMDthcHBpZD15dGFjaHlvbg--/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2022-11/6e32a9f0-608e-11ed-85af-6f6f0330bc92.cf.jpg",
"publishedAt": "2023-05-22T11:15:43Z",
"content": "Meta has long been rumored to be building a platform to rival Twitter. After months of speculation, there are finally some details on how it might turn out, according to digital media marketing exper⦠[+3401 chars]"
},
So I provided my API to chatGPT and asked him to create the model for me and after a few minutes and modifying the code which it provided me, I designed the model.
import 'dart:convert';
class NewsArticle {
final String sourceName;
final String author;
final String title;
final String description;
final String url;
final String imageUrl;
final String publishedAt;
final String content;
NewsArticle({
required this.sourceName,
required this.author,
required this.title,
required this.description,
required this.url,
required this.imageUrl,
required this.publishedAt,
required this.content,
});
factory NewsArticle.fromJson(Map<String, dynamic> json) {
return NewsArticle(
sourceName: json['source']['name'] as String? ?? '',
author: json['author'] as String? ?? '',
title: json['title'] as String? ?? '',
description: json['description'] as String? ?? '',
url: json['url'] as String? ?? '',
imageUrl: json['urlToImage'] as String? ?? '',
publishedAt: json['publishedAt'] as String? ?? '',
content: json['content'] as String? ?? '',
);
}
}
List<NewsArticle> parseNewsData(String newsData) {
final jsonData = json.decode(newsData);
final List<NewsArticle> newsArticles = [];
if (jsonData['articles'] != null) {
for (final article in jsonData['articles']) {
final newsArticle = NewsArticle.fromJson(article);
newsArticles.add(newsArticle);
}
}
return newsArticles;
}
Integrating the logic with the final UI
Now when I asked chatGPT how can I manage the state and the UI dynamically, it answered me that I should use the Consumer
widget inside my home screen UI and it will help me update the Ui without reloading the whole screen again and again.
It also provided me with a snippet to get an idea of how to implement it.
Consumer(builder: (context, watch, child) {
final newsAsyncValue = watch(newsProvider);
return newsAsyncValue.when(
loading: () => CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error: $error'),
data: (newsList) => ListView.builder(
itemCount: newsList.length,
itemBuilder: (context, index) {
final news = newsList[index];
// Build your widget using the news data
return ListTile(
title: Text(news.title),
subtitle: Text(news.description),
// ...
);
},
),
);
},
),
After modifying the code, The final output I got was this π
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:news_app/NewsApi/NewsApi.dart';
import 'package:news_app/NewsApi/NewsArticle.dart';
import 'newsItem.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
'InNews',
style: TextStyle(
color: Colors.purple,
fontStyle: FontStyle.italic,
fontSize: 25,
),
),
),
body: Consumer(
builder: (context, ref, child) {
final newsAsyncValue = ref.watch(newsProvider);
return newsAsyncValue.when(
data: (data) {
final newsArticles = parseNewsData(data);
return ListView.builder(
itemCount: newsArticles.length,
itemBuilder: (context, index) {
return NewsItemWidget(article: newsArticles[index]);
},
);
},
error: (error, stackTrace) {
return Center(
child: Text("Error $error"),
);
},
loading: () {
return const Center(
child: CircularProgressIndicator(),
);
},
);
},
),
);
}
}
- The
newsAsyncValue
represents the current value ofnewsProvider
. It can be in three states:loading
,error
, ordata
. - When the
newsAsyncValue
is in thedata
state, the code retrieves the news articles data usingparseNewsData
function, which converts the data into a list ofNewsArticle
objects. - A
ListView.builder
widget is used to display the list of news articles. It takes the number of articles as theitemCount
and generates each item by calling theNewsItemWidget
constructor, passing the correspondingNewsArticle
object. - When the
newsAsyncValue
is in theerror
state, an error message is displayed at the centre of the screen. - When the
newsAsyncValue
is in theloading
state, aCircularProgressIndicator
is displayed at the centre of the screen, indicating that the data is being loaded.
Overall, this code sets up the HomeScreen
widget to display a list of news articles. It uses the Consumer
widget to listen to changes in the state of the newsProvider
and updates the UI accordingly based on the current state.
The Final Output
Here's the final output of the app π
You can also check out the project here π
Conclusion
In this blog, We explored how we can build our flutter apps efficiently with the help of Ai tools such as ChatGPT, We explored a lot of things and as a developer, it also improves our productivity, So if you have any doubts related to DevOps or Flutter, feel free to reach out on my Twitter and Showwcase handle
Till then, Happy Coding π
mobile
news
flutter
developer
chatgtpt
newsapp
aplication