Flutter HTTP JSON ListView MasterDetail Images Text Tutorial
How to download JSON with images and Text via HTTP and bind to a custom listview with images and text. Then open a detail view of the listview when a listview item is clicked.
Demo
Here are demo for this project:
Flutter HTTP JSON Images text
JSON data
Here's the json data view. It's available in this url https://raw.githubusercontent.com/Oclemy/SampleJSON/338d9585/spacecrafts.json.
JSON Data
Video Tutorial.
Here's the corresponding video tutorial.
Video Tutorial
Here we have a video tutorial for this example.
ListView
One of the most commonly used widgets in any mobile app development framework is the ListView. It normally renders items in a linear manner and is always very flexible. A good way of getting a feel of a framework is to render items in a listview. Especially images and text.
That's exactly what we do here. Then listen to listview itemClick events. Thus we show pass the clicked data to a new detail page/screen.
Here are several things you do here with a ListView
(a). How to Create a CustomListView
This ListView will be capable of rendering both images and text. It will render them in beautiful cardviews.
We will make the custom listview extend the StatelessWidget
class:
class CustomListView extends StatelessWidget {..}
Inside the class we will have a method called createViewItem()
. This method will define for us a single listview view item. Thata is a ListTile and its contents.
Widget createViewItem(Spacecraft spacecraft, BuildContext context) {
return new ListTile(
title: new Card(
...
Our ListTile
s will have CardViews. Those CardViews will have both image and texts.
(b). How to Listen to ListView itemClicks
Well you just use the onTap()
callback.
onTap: () {
//..Open Second Page here.
});
MaterialPageRoute
A MaterialPageRoute is a modal route that replaces the entire screen with a platform-adaptive transition.
(a). How to Create a MaterialPageRoute
We create one using the new
constructor.
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new SecondScreen(value: spacecraft),
);
(b). How to Open a Second Page
We use the Navigator class. A Navigator is a widget that manages a set of child widgets with stack discipline.It allows us navigate pages. We invoke the push()
method, passing in our MaterialPageRoute
.
Navigator.of(context).push(route);
http
http
is the package in flutter that allows us perform our HTTP Requests. In this case we want to perform a HTTP GET request. This means we want to download some data.
First we need to import http
package:
import 'package:http/http.dart' show get;
Then we need a jsonEndpoint, basically our url string. Then we invoke the get
which is equivalent to http.get
:
final jsonEndpoint =
"https://raw.githubusercontent.com/Oclemy/SampleJSON/338d9585/spacecrafts.json";
final response = await get(jsonEndpoint);
We will check if our response is HTTP.OK
which universally equals 200
response code.
We are making this operation asynchronous. We will be returning a Future<T>
object. That means this is a delayed computation and our result is not going to be immediately available. Instead it is avaialable in the future, once our download is complete.
Future<List<Spacecraft>> downloadJSON() async {
final jsonEndpoint =
"https://raw.githubusercontent.com/Oclemy/SampleJSON/338d9585/spacecrafts.json";
final response = await get(jsonEndpoint);
if (response.statusCode == 200) {
List spacecrafts = json.decode(response.body);
return spacecrafts
.map((spacecraft) => new Spacecraft.fromJson(spacecraft))
.toList();
} else
throw Exception('We were not able to successfully download the json data.');
}
(a). main.dart
Here's the complete code.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' show get;
import 'dart:convert';
class Spacecraft {
final String id;
final String name, imageUrl, propellant;
Spacecraft({
this.id,
this.name,
this.imageUrl,
this.propellant,
});
factory Spacecraft.fromJson(Map<String, dynamic> jsonData) {
return Spacecraft(
id: jsonData['id'],
name: jsonData['name'],
propellant: jsonData['propellant'],
imageUrl: jsonData['imageurl'],
);
}
}
class CustomListView extends StatelessWidget {
final List<Spacecraft> spacecrafts;
CustomListView(this.spacecrafts);
Widget build(context) {
return ListView.builder(
itemCount: spacecrafts.length,
itemBuilder: (context, int currentIndex) {
return createViewItem(spacecrafts[currentIndex], context);
},
);
}
Widget createViewItem(Spacecraft spacecraft, BuildContext context) {
return new ListTile(
title: new Card(
elevation: 5.0,
child: new Container(
decoration: BoxDecoration(border: Border.all(color: Colors.orange)),
padding: EdgeInsets.all(20.0),
margin: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Padding(
child: Image.network(spacecraft.imageUrl),
padding: EdgeInsets.only(bottom: 8.0),
),
Row(children: <Widget>[
Padding(
child: Text(
spacecraft.name,
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
),
padding: EdgeInsets.all(1.0)),
Text(" | "),
Padding(
child: Text(
spacecraft.propellant,
style: new TextStyle(fontStyle: FontStyle.italic),
textAlign: TextAlign.right,
),
padding: EdgeInsets.all(1.0)),
]),
],
),
),
),
onTap: () {
//We start by creating a Page Route.
//A MaterialPageRoute is a modal route that replaces the entire
//screen with a platform-adaptive transition.
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new SecondScreen(value: spacecraft),
);
//A Navigator is a widget that manages a set of child widgets with
//stack discipline.It allows us navigate pages.
Navigator.of(context).push(route);
});
}
}
//Future is n object representing a delayed computation.
Future<List<Spacecraft>> downloadJSON() async {
final jsonEndpoint =
"https://raw.githubusercontent.com/Oclemy/SampleJSON/338d9585/spacecrafts.json";
final response = await get(jsonEndpoint);
if (response.statusCode == 200) {
List spacecrafts = json.decode(response.body);
return spacecrafts
.map((spacecraft) => new Spacecraft.fromJson(spacecraft))
.toList();
} else
throw Exception('We were not able to successfully download the json data.');
}
class SecondScreen extends StatefulWidget {
final Spacecraft value;
SecondScreen({Key key, this.value}) : super(key: key);
@override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Detail Page')),
body: new Container(
child: new Center(
child: Column(
children: <Widget>[
Padding(
child: new Text(
'SPACECRAFT DETAILS',
style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 20.0),
textAlign: TextAlign.center,
),
padding: EdgeInsets.only(bottom: 20.0),
),
Padding(
//<code>widget
is the current configuration. A State object's configuration
//is the corresponding StatefulWidget instance.
child: Image.network( '${widget.value.imageUrl}'),
padding: EdgeInsets.only(bottom: 8.0),
),
Padding(
child: new Text(
'NAME : ${widget.value.name}',
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
padding: EdgeInsets.all(20.0),
),
Padding(
child: new Text(
'PROPELLANT : ${widget.value.propellant}',
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
padding: EdgeInsets.all(20.0),
)
], ),
),
),
);
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.deepOrange,
),
home: new Scaffold(
appBar: new AppBar(title: const Text('JSON Images Text')),
body: new Center(
//FutureBuilder is a widget that builds itself based on the latest snapshot
// of interaction with a Future.
child: new FutureBuilder<List<Spacecraft>>(
future: downloadJSON(),
//we pass a BuildContext and an AsyncSnapshot object which is an
//Immutable representation of the most recent interaction with
//an asynchronous computation.
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Spacecraft> spacecrafts = snapshot.data;
return new CustomListView(spacecrafts);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
//return a circular progress indicator.
return new CircularProgressIndicator();
},
),
),
),
);
}
}
void main() {
runApp(MyApp());
}
(b). pubspec.yaml
We are not using any third party library.
name: json_images
description: A new Flutter project.
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
How to Download and Run.
We are not using any third party library hence all you need to do is copy the main.dart
file into your project. The JSON is luckily available online so you just run the app and it will work.