Building a Full-Stack Application with Flutter and Flask

Prerequisites

  • Python ≥ v3 installed on your machine
  • Working knowledge of Flask and Python
  • Intermediate-level knowledge of CLIs
  • Fundamental knowledge of Flutter and the Flutter SDK installed on your machine
  • Working knowledge of building and consuming REST APIs
  • Any suitable IDE or text editor

Project Overview

Our project consists of a simple to-do app that allows users to create, execute, and delete tasks. The Flutter app will serve as the frontend component, while the Flask service will handle the backend operations.

Building the Flask Backend Service

First, let’s create a new Flask project and install the necessary dependencies. We’ll use Flask-SQLAlchemy for database operations and Flask-Marshmallow for serializing and deserializing data.

pip install flask flask-sqlalchemy flask-marshmallow

Next, create a new file called main.py and add the following code:


from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
db = SQLAlchemy(app)
ma = Marshmallow(app)

class TodoItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    is_executed = db.Column(db.Boolean, default=False)

    def __init__(self, name):
        self.name = name

todo_schema = ma.Schema(fields=('id', 'name', 'is_executed'))
todos_schema = ma.Schema(many=True, fields=('id', 'name', 'is_executed'))

@app.route('/todo', methods=['POST'])
def add_todo():
    name = request.json['name']
    todo = TodoItem(name)
    db.session.add(todo)
    db.session.commit()
    return jsonify(todo_schema.dump(todo))

@app.route('/todo', methods=['GET'])
def get_todos():
    todos = TodoItem.query.all()
    return jsonify(todos_schema.dump(todos))

@app.route('/todo/', methods=['PUT'])
def execute_todo(id):
    todo = TodoItem.query.get(id)
    todo.is_executed = not todo.is_executed
    db.session.commit()
    return jsonify(todo_schema.dump(todo))

@app.route('/todo/', methods=['DELETE'])
def delete_todo(id):
    todo = TodoItem.query.get(id)
    db.session.delete(todo)
    db.session.commit()
    return jsonify(todo_schema.dump(todo))

if __name__ == '__main__':
    app.run(debug=True)

Building the Flutter Frontend App

Next, let’s create a new Flutter project and add the necessary dependencies. We’ll use the http package for making HTTP requests and the provider package for state management.

flutter pub add http provider

Create a new file called todo_item.dart and add the following code:


class TodoItem {
  final int id;
  final String name;
  final bool isExecuted;

  TodoItem({this.id, this.name, this.isExecuted});
}

Create another file called todo_provider.dart and add the following code:


import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';

class TodoProvider with ChangeNotifier {
  List _items = [];

  Future addTodo(String name) async {
    final response = await http.post(
      Uri.parse('http://localhost:5000/todo'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'name': name}),
    );
    final todo = TodoItem.fromJson(jsonDecode(response.body));
    _items.add(todo);
    notifyListeners();
  }

  Future getTodos() async {
    final response = await http.get(Uri.parse('http://localhost:5000/todo'));
    final todos = jsonDecode(response.body).map((todo) => TodoItem.fromJson(todo)).toList();
    _items = todos;
    notifyListeners();
  }

  Future executeTodo(int id) async {
    final response = await http.put(
      Uri.parse('http://localhost:5000/todo/$id'),
      headers: {'Content-Type': 'application/json'},
    );
    final todo = TodoItem.fromJson(jsonDecode(response.body));
    _items.removeWhere((item) => item.id == id);
    _items.add(todo);
    notifyListeners();
  }

  Future deleteTodo(int id) async {
    final response = await http.delete(Uri.parse('http://localhost:5000/todo/$id'));
    _items.removeWhere((item) => item.id == id);
    notifyListeners();
  }
}

Creating the Task Widget

Create a new file called tasks.dart and add the following code:


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

class TaskWidget extends StatefulWidget {
  @override
  _TaskWidgetState createState() => _TaskWidgetState();
}

class _TaskWidgetState extends State {
  final _textController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final todoProvider = Provider.of(context);
    return Column(
      children: [
        TextField(
          controller: _textController,
          decoration: InputDecoration(hintText: 'Enter task name'),
        ),
        // ...
      ],
    );
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *