Flutter Applications With Python Flask Rest API with Flutter in Tamil


CategoryMobile Application
Language Flutter
Author Tutor Joes

The purpose of this article is to demonstrate to learn how to create Simple Mobile application with Python flask Rest API in Flutter.

What is Python Rest API?

REST stands for REpresentational State Transfer and is an architectural style used in modern web development.We will build a REST API in Python using the Flask framework. Flask is a popular micro framework for building Mobile applications.

This is often how the backend of web and mobile application is created. Returning data is in JSON format and requests we are using are PUT, DELETE, POST, and GET

There are two ways of creating a REST API

  • Using Flask without any external libraries
  • Using flask_restful library

We would be following these steps

  • Build Flutter web application
  • Modify build files for Flask API
  • Build and configure Flask application
  • Code REST API to serve Flutter application

Source Code


main.py (python file)

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
 
app = Flask(__name__)
 
 
@app.route('/',methods=['GET'])
def home():
  return "Tutor Joes Api"
 
basedir=os.path.abspath(os.path.dirname(__file__))
#print(basedir)
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///'+os.path.join(basedir,'db.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
 
 
db = SQLAlchemy(app)
ma = Marshmallow(app)
 
# User Table Model 
class User(db.Model):
  id=db.Column(db.Integer, primary_key=True)
  name = db.Column(db.String(100))
  contact = db.Column(db.String(100), unique=True)
 
  def __init__(self,name,contact) :
    self.name=name
    self.contact=contact
 
class UserSchema(ma.Schema):
  class Meta:
    fields = ('id', 'name', 'contact')
 
user_schema = UserSchema()
users_schema=UserSchema(many=True)
 
# Add New User
@app.route('/user',methods=['POST'])
def add_user():
  name=request.json['name']
  contact=request.json['contact']
  new_user=User(name,contact)
  db.session.add(new_user)
  db.session.commit()
  return user_schema.jsonify(new_user)
 
# Show All User
@app.route('/user',methods=['GET'])
def getAllUser():
  all_users=User.query.all()
  result=users_schema.dump(all_users)
  return jsonify(result)
 
# Show User By ID
@app.route('/user/<id>',methods=['GET'])
def getUserByid(id):
  user=User.query.get(id)
  return user_schema.jsonify(user)
 
 
# Update User By ID
@app.route('/user/<id>',methods=['PUT'])
def UpdateUser(id):
  user=User.query.get(id)
  name=request.json['name']
  contact=request.json['contact']
  user.name=name
  user.contact=contact
  db.session.commit()
  return user_schema.jsonify(user)
 
# Delete User By ID
@app.route('/user/<id>',methods=['DELETE'])
def DeleteUserById(id):
  user=User.query.get(id)
  db.session.delete(user)
  db.session.commit()
  return user_schema.jsonify(user)
 
 
if __name__=='__main__':
  app.run(debug=True,port=5000)

Flutter file


pubspec.yaml

name: rest_api_flask
description: A new Flutter project.
 
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.17.1 <3.0.0"
 
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter
 
 
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  http:

dev_dependencies:
  flutter_test:
    sdk: flutter
 
  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^2.0.0
 
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
 
# The following section is specific to Flutter packages.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true
 
  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg
 
  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware
 
  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages
 
  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

main.dart

import 'package:flutter/material.dart';
import 'package:rest_api_flask/view/home_page.dart';
 
void main() {
  runApp(const MyApp());
}
 
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}
 

view/home_page.dart

import 'package:flutter/material.dart';
import 'package:rest_api_flask/models/user.dart';
import 'package:rest_api_flask/services/userApi.dart';
import 'package:rest_api_flask/view/addUserForm.dart';
import 'package:rest_api_flask/view/updateUserForm.dart';
 
class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);
 
  @override
  State<HomePage> createState() => _HomePageState();
}
 
class _HomePageState extends State<HomePage> {
  List<User>? users;
  var isLoaded = false;
 
  @override
  void initState() {
    getRecord();
  }
  getRecord() async {
    users=await UserApi().getAllUsers();
    if(users!=null){
      setState((){
        isLoaded = true;
      });
    }
  }
 
 
  Future<void> showMessageDialog(String title,String msg) async {
    return showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text(title),
          content: SingleChildScrollView(
            child: Text(
              msg,
            ),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Ok'),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ],
        );
      },
    );
  }
 
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Python RestAPI Flutter"),
      ),
      body: Visibility(
        visible: isLoaded,
        replacement: const Center(child:CircularProgressIndicator()),
        child: ListView.builder(
            itemCount: users?.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text(users![index].name),
                subtitle: Text(users![index].contact),
                trailing: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    IconButton(
                      onPressed: () async {
                        Navigator.push(
                            context,
                            MaterialPageRoute(builder: (context)=> updateUserForm(users![index]))
                        ).then((data){
                          if (data != null) {
                            showMessageDialog("Success","$data Detail Updated Success.");
                            getRecord();
                          }
                        });
                      },
                      icon: const Icon(Icons.edit,color: Colors.blue,),
                    ),
                    IconButton(
                      onPressed: () async{
                        User user=await UserApi().deleteUSer(users![index].id);
                        showMessageDialog("Success","$user Detail Deleted Success.");
                        getRecord();
                      },
                      icon: const Icon(Icons.delete,color: Colors.red,),
                    ),
                  ],
                ),
              );
            }),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async{
          Navigator.push(
            context,
              MaterialPageRoute(builder: (context)=>const addUserForm())
          ).then((data){
            if (data != null) {
              showMessageDialog("Success","$data Detail Added Success.");
              getRecord();
            }
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

view/addUserForm.dart

import 'package:flutter/material.dart';
import 'package:rest_api_flask/services/userApi.dart';
 
class addUserForm extends StatefulWidget {
  const addUserForm({Key? key}) : super(key: key);
 
  @override
  State<addUserForm> createState() => _addUserFormState();
}
 
class _addUserFormState extends State<addUserForm> {
  var _userNameController = TextEditingController();
  var _contactController = TextEditingController();
 
  bool _validateName = false;
  bool _validateContact = false;
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Add New User'),
      ),
      body: SingleChildScrollView(
        child: Container(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              const Text(
                'Add New User Details',
                style: TextStyle(
                    fontSize: 20,
                    color: Colors.blue,
                    fontWeight: FontWeight.w500),
              ),
              const SizedBox(
                height: 20.0,
              ),
              TextField(
                  controller: _userNameController,
                  decoration: InputDecoration(
                    border: const OutlineInputBorder(),
                    hintText: 'Enter Name',
                    labelText: 'Name',
                    errorText:
                        _validateName ? 'Name Value Can\'t Be Empty' : null,
                  )),
              const SizedBox(
                height: 20.0,
              ),
              TextField(
                  controller: _contactController,
                  decoration: InputDecoration(
                    border: const OutlineInputBorder(),
                    hintText: 'Enter Contact Number',
                    labelText: 'Contact No',
                    errorText:
                        _validateName ? 'Contact Value Can\'t Be Empty' : null,
                  )),
              const SizedBox(
                height: 20.0,
              ),
              Row(
                children: [
                  TextButton(
                      onPressed: () async{
                        setState(() {
                          _userNameController.text.isEmpty
                              ? _validateName = true
                              : _validateName = false;
                          _contactController.text.isEmpty
                              ? _validateContact = true
                              : _validateContact = false;
 
                        });
                        if (_validateName == false &&  _validateContact == false) {
                          var result=await UserApi().addUser(_userNameController.text, _contactController.text);
                          Navigator.pop(context,result);
                        }
                      },
                      style: TextButton.styleFrom(
                          primary: Colors.white,
                          backgroundColor: Colors.blue,
                          textStyle: const TextStyle(fontSize: 15)),
                      child: const Text('Save Details')),
                  const SizedBox(
                    width: 10.0,
                  ),
                  TextButton(
                      onPressed: () {
                        _userNameController.text="";
                        _contactController.text="";
                      },
                      style: TextButton.styleFrom(
                          primary: Colors.white,
                          backgroundColor: Colors.red,
                          textStyle: const TextStyle(fontSize: 15)),
                      child: const Text('Clear Details')),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

view/updateUserForm.dart

import 'package:flutter/material.dart';
import 'package:rest_api_flask/models/user.dart';
import 'package:rest_api_flask/services/userApi.dart';
 
class updateUserForm extends StatefulWidget {
  final User user;
  const updateUserForm(this.user,{Key? key}) : super(key: key);
 
  @override
  State<updateUserForm> createState() => _updateUserFormState();
}
 
class _updateUserFormState extends State<updateUserForm>  {
  var _userNameController = TextEditingController();
  var _contactController = TextEditingController();
 
  bool _validateName = false;
  bool _validateContact = false;
 
  @override
  void initState() {
    _userNameController.text=widget.user.name;
    _contactController.text=widget.user.contact;
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Update New User'),
      ),
      body: SingleChildScrollView(
        child: Container(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              const Text(
                'Edit User Details',
                style: TextStyle(
                    fontSize: 20,
                    color: Colors.blue,
                    fontWeight: FontWeight.w500),
              ),
              const SizedBox(
                height: 20.0,
              ),
              TextField(
                  controller: _userNameController,
                  decoration: InputDecoration(
                    border: const OutlineInputBorder(),
                    hintText: 'Enter Name',
                    labelText: 'Name',
                    errorText:
                    _validateName ? 'Name Value Can\'t Be Empty' : null,
                  )),
              const SizedBox(
                height: 20.0,
              ),
              TextField(
                  controller: _contactController,
                  decoration: InputDecoration(
                    border: const OutlineInputBorder(),
                    hintText: 'Enter Contact Number',
                    labelText: 'Contact No',
                    errorText:
                    _validateName ? 'Contact Value Can\'t Be Empty' : null,
                  )),
              const SizedBox(
                height: 20.0,
              ),
              Row(
                children: [
                  TextButton(
                      onPressed: () async{
                        setState(() {
                          _userNameController.text.isEmpty
                              ? _validateName = true
                              : _validateName = false;
                          _contactController.text.isEmpty
                              ? _validateContact = true
                              : _validateContact = false;
 
                        });
                        if (_validateName == false &&  _validateContact == false) {
                          var result=await UserApi().updateUser(_userNameController.text, _contactController.text,widget.user.id);
                          Navigator.pop(context,result);
                        }
                      },
                      style: TextButton.styleFrom(
                          primary: Colors.white,
                          backgroundColor: Colors.blue,
                          textStyle: const TextStyle(fontSize: 15)),
                      child: const Text('Update Details')),
                  const SizedBox(
                    width: 10.0,
                  ),
                  TextButton(
                      onPressed: () {
                        _userNameController.text="";
                        _contactController.text="";
                      },
                      style: TextButton.styleFrom(
                          primary: Colors.white,
                          backgroundColor: Colors.red,
                          textStyle: const TextStyle(fontSize: 15)),
                      child: const Text('Clear Details')),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

models/user.dart

// To parse this JSON data, do
//
//     final user = userFromJson(jsonString);
 
import 'dart:convert';
 
List<User> userFromJson(String str) => List<User>.from(json.decode(str).map((x) => User.fromJson(x)));
 
String userToJson(List<User> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
 
class User {
  User({
    required this.contact,
    required this.id,
    required this.name,
  });
 
  String contact;
  int id;
  String name;
 
  factory User.fromJson(Map<String, dynamic> json) => User(
    contact: json["contact"],
    id: json["id"],
    name: json["name"],
  );
 
  Map<String, dynamic> toJson() => {
    "contact": contact,
    "id": id,
    "name": name,
  };
  @override
  String toString(){
    String result=name;
    return result;
  }
}

services/userApi.dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:rest_api_flask/models/user.dart';
 
class UserApi
{
  //Get all User Details
  Future<List<User>?> getAllUsers() async{
    var client =http.Client();
    //http://127.0.0.1:5000/user
    var uri=Uri.parse("http://10.0.2.2:5000/user");
    var response = await client.get(uri);
    if(response.statusCode==200){
      var json=response.body;
      return userFromJson(json);
    }
  }
  //Add New User
  Future<User> addUser(String name,String contact) async{
    var client =http.Client();
    var uri=Uri.parse("http://10.0.2.2:5000/user");
    final http.Response response = await client.post(
        uri,
        headers: <String, String>{
         'Content-Type': 'application/json; charset=UTF-8',
         },
        body: jsonEncode(<String, String>{
        'name': name,
        'contact': contact
      }),
    );
    if(response.statusCode==200){
      var json=response.body;
      return User.fromJson(jsonDecode(json));
    }else {
      throw Exception('Failed to Save User.');
    }
  }
  //Delete User
  Future<User> deleteUSer(int id) async{
    var client =http.Client();
    var uri=Uri.parse("http://10.0.2.2:5000/user/$id");
    final http.Response response = await client.delete(
      uri,
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      }
    );
    if(response.statusCode==200){
      var json=response.body;
      return User.fromJson(jsonDecode(json));
    }else {
      throw Exception('Failed to Delete User.');
    }
  }
  //Update User
  Future<User> updateUser(String name,String contact,int id) async{
    var client =http.Client();
    var uri=Uri.parse("http://10.0.2.2:5000/user/$id");
    final http.Response response = await client.put(
      uri,
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      },
      body: jsonEncode(<String, String>{
        'name': name,
        'contact': contact
      }),
    );
    if(response.statusCode==200){
      var json=response.body;
      return User.fromJson(jsonDecode(json));
    }else {
      throw Exception('Failed to Update User.');
    }
  }
}

Conclusion

In this article, I demonstrated how to create a E-commerce using Python Django and MySQL. Hope you can easily understand thank you "Happy Coding".