
I am going to share today some exciting topics such as :
- How to build RESTful api on web server using Dart and Aqueduct with integration of Postgresql.
- How to build Flutter mobile app and perform basic CRUD functions with Aqueduct application. 👈 You are here
From the previous post, I have shared how to setup your web server to implement RESTful api using Aqueduct with Postgresql. In this post, we are going to start building our flutter app to interact with our web application.
How to set up Flutter project
Create a new Flutter project call flutter_crud_demo. If you are using Visual Studio Code, you can create new project by View > Command Pallete > Flutter New Project > Enter project name > Select directory to save your project. When the workspace is done initialising, delete the widget_test.dart and empty the content of the main.dart.
Read function
Future Builder
Future builder allows us to render a list view as soon as we get our list of data asynchronously. Future builder has 2 parameters, one is the future which is the future method we use to retrieve the list of data and the other is builder which is what do we want to build with the data. Using future builder allows us to show to user a CircularProgressIndicator before data is ready and shows ListView when fetching is done. In our scaffold body, replace with the following code.
body: new Container(
child: new FutureBuilder<List<Hero>>(
future: getAll(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return new ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(snapshot.data[index].name,
style: new TextStyle(fontWeight: FontWeight.bold)),
new Divider()
]
);
}
);
} else if (snapshot.hasError) {
return new Text("${snapshot.error}");
}
// By default, show a loading spinner
return new CircularProgressIndicator();
},
),
),
Next we need to implement getAll()method to retrieve list of heroes from our web server. Our method returns a Futureof type list of hero. Inside the function, we call and awaithttp.get(_heroesUrl) . Await allows us to wait for a response before proceeding to the next line. The function getAll() needs to be marked with async to be able to use await inside the method. From the response, we retrieve the body message and convert into our
Future<List<Hero>> getAll() async {
final response = await http.get(_heroesUrl);
print(response.body);
List responseJson = json.decode(response.body.toString());
List<Hero> userList = createHeroesList(responseJson);
return userList;
}
We need to import some libraries for some helper functions.
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
Let’s create a class call Hero. This class takes in an integer for id and string as name.
class Hero {
Hero({this.id, this.name});
final int id;
String name;
}
Inside class _MyHomePageState, add static const _heroesUrl to contain our localhost url.
static const _heroesUrl = 'http://localhost:8888/heroes';
Let’s run the Flutter app. Also do remember to start your web server application with aqueduct serve command.

Delete function
From here onwards, we are going to introduce an enum to store the status of any http request. Any http request should return type HttpRequestStatus.
enum HttpRequestStatus {
NOT_DONE,
DONE,
ERROR
}
Dismissible
We are going to implement swipe to delete which is a very common pattern found in mobile apps. To do this, we going to swap Column with Dismissible as shown below. We then request to delete a hero by its id at the onDismissed parameter. The method deleteHero returns a future which is the httpRequestStatus . If the status is done, we will ask to redraw the screen using setState().
return new ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var item = snapshot.data[index];
return Dismissible(
key: Key(item.id.toString()),
onDismissed: (direction) async {
httpRequestStatus = await deleteHero(item.id);
if (httpRequestStatus == HttpRequestStatus.DONE) {
setState(() {
snapshot.data.removeAt(index);
});
}
},
background: Container(color: Colors.red),
child: ListTile(title: Text('${item.name}')),
);
});
The method deleteHero is as follows
Future deleteHero(int id) async {
httpRequestStatus = HttpRequestStatus.NOT_DONE;
final url = '$_heroesUrl/$id';
final response = await http.delete(url, headers: _headers);
if (response.statusCode == 200) {
print(response.body.toString());
httpRequestStatus = HttpRequestStatus.DONE;
} else {
httpRequestStatus = HttpRequestStatus.ERROR;
}
return httpRequestStatus;
}

Add function
Let’s add a widget icon under the AppBar to allow user to add hero.
appBar: new AppBar(
title: new Text('Flutter CRUD Demo'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: _addHeroService,
tooltip: 'Add New Hero',
)
],
),
In the _addHeroService , we call _openDialogAddHero to push a new screen for user input on hero name. This method returns a new hero name which we will then call createHero with it and if hero name is successfully updated, we will call setState() to redraw the screen.
void _addHeroService() async {
String name = await _openDialogAddHero();
HttpRequestStatus httpRequestStatus = await createHero(name);
if (httpRequestStatus == HttpRequestStatus.DONE) {
setState(() {
});
}
}
Let’s add a static const _headers to store content-type of http header.
static final _headers = {'Content-Type': 'application/json'};
The following is the code to send a create new hero request to web server application.
Future createHero(String name) async {
httpRequestStatus = HttpRequestStatus.NOT_DONE;
final response = await http.post(_heroesUrl,
headers: _headers, body: json.encode({'name': name}));
if (response.statusCode == 200) {
print(response.body.toString());
httpRequestStatus = HttpRequestStatus.DONE;
} else {
httpRequestStatus = HttpRequestStatus.ERROR;
}
return httpRequestStatus;
}

Update function
Finally we are left with one last function which is the ability to update hero name. We want user to tap on existing hero to update the name. For this, we add an onTap inside ListTile which calls method _updateHeroService , passing in id and name of the hero at that index of the list. Similar to the _addHeroService which retrieves name from the pop up dialog and pass into updateHero . The screen is then redrawn if the updateHero is successful.
Future _updateHeroService(int id, String name) async {
String updatedName = await _openDialogUpdateHero(id, name);
HttpRequestStatus httpRequestStatus = await updateHero(id, updatedName);
if (httpRequestStatus == HttpRequestStatus.DONE) {
print(httpRequestStatus.toString());
setState(() {
});
}
}
The following is the code for updateHero
Future updateHero(int id, String name) async {
httpRequestStatus = HttpRequestStatus.NOT_DONE;
final url = '$_heroesUrl/$id';
final response = await http.put(url,
headers: _headers, body: json.encode({'id': id, 'name': name}));
if (response.statusCode == 200) {
print(response.body.toString());
httpRequestStatus = HttpRequestStatus.DONE;
} else {
httpRequestStatus = HttpRequestStatus.ERROR;
}
}

As a recap, we have covered how to build our web server application implementing RESTful api and managed to build a Flutter app to perform basic CRUD functions with PostgreSQL.
Github
https://github.com/tattwei46/flutter_crud_postgresql
If you find this article helpful and educational, do give it some 👏👏👏to encourage me to write more of this in future 🙏
The Flutter Pub is a medium publication to bring you the latest and amazing resources such as articles, videos, codes, podcasts etc. about this great technology to teach you how to build beautiful apps with it. You can find us on Facebook, Twitter, and Medium or learn more about us here. We’d love to connect! And if you are a writer interested in writing for us, then you can do so through these guidelines.
