Create a News application using Flutter with Node.js backend (part 2)

Rudraksh Dixit
4 min readMar 31, 2020

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

To check part 1 click here. To check API click here

Goto Search.dart file in your project and import packages

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'News.dart';

Create a stateful widget Search and Scaffold out the application

class Search extends StatefulWidget {
@override
_SearchState createState() => _SearchState();
}

class _SearchState extends State<Search> {

@override
Widget build(BuildContext context) {
return Scaffold();
}
}

To get the response from our API, we call the function fetchData

class _SearchState extends State<Search> {
final myController = TextEditingController();
var first = true;

List list = List();
var isLoading = false;

fetchData(String text) async {
if (text == "" || text == null) { //text is a function argument
} else {
setState(() {
isLoading = true;
first = false;
});
final response = await http.get("https://newsapprd.herokuapp.com/$text");
if (response.statusCode == 200) {
setState(() {
list = json.decode(response.body) as List;
});
setState(() {
isLoading = false;
});
} else {
throw Exception('Failed to load');
}
}
}

@override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
myController.dispose();

super.dispose();
}

@override
Widget build(BuildContext context) {

Creating an appbar which will enable the instant search from web

appBar: AppBar(
backgroundColor: Color(0xff303240),
title: TextField(
onChanged: (text) {
fetchData("$text");
},
controller: myController,
style: TextStyle(fontSize: 20, color: Colors.white),
decoration: InputDecoration(
hintText: "Search",
hintStyle: TextStyle(fontSize: 20.0, color: Colors.white),
border: OutlineInputBorder(borderSide: BorderSide.none),
suffixIcon: InkWell(
onTap: () {
fetchData("${myController.text}");
},
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.search,
color: Colors.white,
size: 25,
),
),
),
),
),
),
),

body of the Scaffold will contain a circular progress bar while data is being fetched, a text when data is not found and if data is found then a card will be displayed in a listview

body: isLoading
? Center(
child: CircularProgressIndicator(),
)
: first
? Text("")
: list.isEmpty
? Center(child: Text('Not found'))
: ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 8.0,
offset: Offset(
1.0,
1.0,
),
),
],
borderRadius: BorderRadius.circular(2),
),
height: MediaQuery.of(context).size.height * .2,
width: double.infinity,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => News(
title: list[index]['title'],
imageURL: list[index]['imageURL'],
content: list[index]['content'],
author: list[index]['author'],
),
),
);
},
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
list[index]['imageURL'],
width:
MediaQuery.of(context).size.height *
.15,
height:
MediaQuery.of(context).size.height *
.15,
),
),
Flexible(
child: Padding(
padding:
const EdgeInsets.only(right: 8.0),
child: Text(
list[index]['title'],
style: GoogleFonts.openSans(
textStyle: TextStyle(
fontSize: 14,
color: Color(0xff423535),
letterSpacing: .5,
fontWeight: FontWeight.bold),
),
),
),
)
],
),
),
),
),
);
},
),

The search function is completed, now goto News.dart file.

Import the packages.

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

Create a stateless widget, News

class News extends StatelessWidget {
News({this.title, this.imageURL, this.content, this.author});
String title, imageURL, content, author;
//title, imageURL, content, author is passed from previous activity/route, which will be used here. @override
Widget build(BuildContext context) {
return Scaffold(
);
}
}

Inside Scaffold the body will contain a SingleChildScrollView which is followed by the Stack.

body:  SingleChildScrollView(
child: Stack(
children: <Widget>[
],
),
),

The children of the Stack will be two containers, one contains an image and the other where content is displayed.

Container 1:-

Container(
decoration: BoxDecoration(
color: Colors.grey,
),
height: MediaQuery.of(context).size.width, //for square image
child: Image.network(
imageURL,
fit: BoxFit.cover,
),
),

Container 2:-

Container(
margin:
EdgeInsets.only(top: MediaQuery.of(context).size.width - 40),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40),
topRight: Radius.circular(40),
),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: GoogleFonts.openSans(
textStyle: TextStyle(
fontSize: 18,
color: Color(0xff082213),
letterSpacing: .5,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Text(
"-" + author,
style: GoogleFonts.poppins(
textStyle: TextStyle(
fontSize: 12,
color: Color(0xffeb14b3),
letterSpacing: .5,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 12,
),
Text(
content,
style: GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 16,
color: Color(0xff354242),
letterSpacing: .5,
fontWeight: FontWeight.bold),
),
),
],
)),
)

Here is the final result

Check the source code of both Search.dart and News.dart

You can also email/message me for further queries.

Email: Rudrakshdixit@gmail.com

Facebook: http://bit.ly/2ELzDcL

Twitter: https://bit.ly/2JqWOdZ

Linkedin: https://bit.ly/2w4aCrO

--

--