
Today, we’re going to integrate the Flask-RESTful backend, a Python extension, with Flutter as our frontend, taking advantage of a very simple API.
In short, we’re uniting the king of FRONTEND and the king of BACKEND.
🎨 Flutter & Flask-RESTful
Flutter is a UI toolkit to develop cross-platform and performant apps. It’s highly flexible and developed by Google.
Flask-RESTful is an extension for Flask that adds support for quickly building REST APIs. It’s powered by Python.
🕸 REST API
REST API is an application programming interface (API) based on the REST architectural style. It includes methods like GET, POST, DELETE, etc., for sending and fetching data. All HTTP methods can be used with REST APIs.
🧑🏻💻 Writing our first REST API
So, we’ll be using the flask-restful library, with is powered by Python. Hence, the first step will be installing the library— we all know that pip
is the manager that lets you install various packages in Python.
pip install flask-restful
Mac OS: If pip
isn’t working for you and you’ve installed the latest Python version, then pip3
will be your call. Same for the python
to python3
keyword.
We’ll start off very simply by creating the file app.py
and writing a very simple hello world
API response.
import flask
from flask_restful import Resource, Api
app = flask.Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return {
'hello': 'world',
}
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8080)
Run the command:
python app.py
And open http://localhost:8080

A little deeper
Now, let’s make this API response a little more complex so we can showcase it on our Flutter frontend.
import flask
from flask_restful import Resource, Api
app = flask.Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
data = [
'first',
'API',
'Response',
'with',
'random List',
'python',
]
return {
'data': data,
}
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8080)
Now, when you open the localhost
it's going to look something like this:

💻 Integration in Flutter
I’m going to use flutter_bloc to handle the REST APIs, followed by cubits and states. So, the project structure is going to be like this:

Model
We’ll start with a very simple model class that holds just a List<String>
import 'dart:convert';
class Data {
final List<String> words;
Data({
required this.words,
});
Data copyWith({
List<String>? words,
}) {
return Data(
words: words ?? this.words,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'words': words,
};
}
factory Data.fromMap(Map<String, dynamic> map) {
return Data(
words: List<String>.from((map['words'] as List)),
);
}
String toJson() => json.encode(toMap());
factory Data.fromJson(String source) =>
Data.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'Data(words: $words)';
@override
int get hashCode => words.hashCode;
}
data_provider.dart
Then, we’ll request the data from the API in the data_provider
layer.
You may use dio here as well, which is my preference.
part of 'cubit.dart';
class DataDataProvider {
static Future<Data> fetch() async {
try {
final request = await http.get(Uri.parse('http://localhost:8080'));
return Data.fromJson(request.body);
} catch (e) {
throw Exception("Internal Server Error");
}
}
}
repository.dart
Next, just pass the data from API to the cubits
:
part of 'cubit.dart';
class DataRepository {
Future<Data> fetch() => DataDataProvider.fetch();
}
state.dart
We’re handling a few different states here:
- Loading state
- Success state
- Failure state
part of 'cubit.dart';
@immutable
class DataState extends Equatable {
final Data? data;
final String? message;
const DataState({
this.data,
this.message,
});
@override
List<Object?> get props => [
data,
message,
];
}
@immutable
class DataDefault extends DataState {}
@immutable
class DataFetchLoading extends DataState {
const DataFetchLoading() : super();
}
@immutable
class DataFetchSuccess extends DataState {
const DataFetchSuccess({Data? data}) : super(data: data);
}
@immutable
class DataFetchFailed extends DataState {
const DataFetchFailed({String? message}) : super(message: message);
}
cubit.dart
Finally, we’re omitting states based on if we have the data or not.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:http/http.dart' as http;
import 'package:jugaad/models/data.dart';
part 'data_provider.dart';
part 'repository.dart';
part 'state.dart';
class DataCubit extends Cubit<DataState> {
static DataCubit cubit(BuildContext context, [bool listen = false]) =>
BlocProvider.of<DataCubit>(context, listen: listen);
DataCubit() : super(DataDefault());
final repo = DataRepository();
Future<void> fetch() async {
emit(const DataFetchLoading());
try {
final data = await repo.fetch();
emit(DataFetchSuccess(data: data));
} catch (e) {
emit(DataFetchFailed(message: e.toString()));
}
}
}
Now let’s handle the frontend using BlocBuilder
. The final product will be something like this:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:jugaad/cubits/data/cubit.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => DataCubit()),
],
child: const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'flutter.py',
home: DataScreen(),
),
);
}
}
class DataScreen extends StatefulWidget {
const DataScreen({Key? key}) : super(key: key);
@override
State<DataScreen> createState() => _DataScreenState();
}
class _DataScreenState extends State<DataScreen> {
@override
void initState() {
super.initState();
DataCubit.cubit(context).fetch();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<DataCubit, DataState>(
builder: (context, state) {
// loading
if (state is DataFetchLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
// success
else if (state is DataFetchSuccess) {
return ListView(
children: state.data!.words
.map(
(word) => ListTile(
title: Text(word),
),
)
.toList(),
);
}
// failure
else if (state is DataFetchFailed) {
return Center(
child: Text(state.message!),
);
}
// something unexpected
return const Center(
child: Text('Something went wrong'),
);
},
),
);
}
}
And …… 🥁🥁🥁

🔥 Complexity++
For the sake of more understanding, I’ll make our API response a bit more complex and then change the data.dart model class accordingly to fill up the UI.
API response
import flask
from flask_restful import Resource, Api
app = flask.Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
data = [
{
'word': 'cat',
'type': 'animal',
},
{
'word': 'football',
'type': 'sports',
},
{
'word': 'rice',
'type': 'food',
},
]
return {
'data': data,
}
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8080)
Data.dart
import 'dart:convert';
class Data {
final String word;
final String type;
Data({
required this.word,
required this.type,
});
Data copyWith({
String? word,
String? type,
}) {
return Data(
word: word ?? this.word,
type: type ?? this.type,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'word': word,
'type': type,
};
}
factory Data.fromMap(Map<String, dynamic> map) {
return Data(
word: map['word'] as String,
type: map['type'] as String,
);
}
String toJson() => json.encode(toMap());
factory Data.fromJson(String source) =>
Data.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() => 'Data(word: $word, type: $type)';
@override
int get hashCode => word.hashCode ^ type.hashCode;
@override
bool operator ==(covariant Data other) {
if (identical(this, other)) return true;
return other.word == word && other.type == type;
}
}
And the final look on our UI will be something like this:

That’s all, folks! I hope you’ve learned something today, and I hope some genius reads this article and comes up with an actual dart package named flutter.py
that handles all the fuss with ease!
People always ask questions like, “What kind of backend we can use with Flutter?” “Can we use Python as the backend?” “Can we use Node.js?” and so on.
Flutter is just a UI toolkit, which means you can attach anything in the backend. We used Flask-RESTful for this purpose, but you can use Django, Node.js or any of your favorite backends.
So, keep exploring and keep creating awesome stuff!
Thanks, y’all and #happyfluttering 💙