Xata + Flutter: A getting started guide.
Tags: xata, flutter, dart, mobileapplication
Published: 2024-02-05 09:15:58

[Xata](https://xata.io/?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog) is a serverless data platform for building modern and robust applications. Built on top of PostgreSQL, Xata provides a unified REST API for efficient data management. Setting itself apart from other data platforms, Xata introduces unique functionalities that significantly streamline the developer workflow. Here are some key benefits of integrating Xata into any application:

-   **Robust file management**: Xata provides APIs and SDKs to manage and securely upload images, documents, and more, directly to a database record.
-   **Multiple environments support and workflow**: With Xata, creating isolated production environments for testing, staging, or feature releases is seamless.
-   **Fast search support**: Xata automatically indexes uploaded data, facilitating fast and efficient data searches across tables and branches.
-   **AI support**: Xata offers vector embedding and AI solutions that empower the development of intelligent applications.

To experience the capabilities of Xata, we will build a project management application with Xata and Flutter. The project repository can be found [here](https://github.com/Mr-Malomz/flutter_xata/tree/dev).

## Prerequisites

To follow along with this tutorial, the following are needed:

-   Basic understanding of Dart and Flutter
-   Xata account. Signup is [free](https://app.xata.io/signin?mode=signup?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog)

## Setup the database on Xata

To get started, log into the [Xata workspace](https://app.xata.io/workspaces) and create a `project` database. Inside the `project` database, create a `Project` table and add columns as shown below:

| **Column type** | **Column name** |
| --------------- | --------------- |
| String          | name            |
| Text            | description     |
| String          | status          |

![Create database](https://paper-attachments.dropboxusercontent.com/s_E1CBF4A45089EFC3D9C461B80BB9F97D882823FECDCA010DD86FEAC6EC26A79C_1706440285877_Screenshot+2024-01-28+at+12.11.03.png)
![Add table](https://paper-attachments.dropboxusercontent.com/s_E1CBF4A45089EFC3D9C461B80BB9F97D882823FECDCA010DD86FEAC6EC26A79C_1706442245485_Screenshot+2024-01-20+at+22.09.07.png)
![Add field](https://paper-attachments.dropboxusercontent.com/s_E1CBF4A45089EFC3D9C461B80BB9F97D882823FECDCA010DD86FEAC6EC26A79C_1706442310277_Screenshot+2024-01-20+at+22.35.14.png)

Inside a table, Xata automatically adds an `id`, `xata.createdAt`, `xata.updatedAt`, and `xata.version` columns that we can also leverage to perform advanced data operations.

![Created column](https://paper-attachments.dropboxusercontent.com/s_E1CBF4A45089EFC3D9C461B80BB9F97D882823FECDCA010DD86FEAC6EC26A79C_1706441400678_Screenshot+2024-01-28+at+12.19.01.png)

**Get the Database URL and set up the API Key**

To securely connect to the database, Xata provides a unique and secure URL for accessing it. To get the database URL, click the **Get code snippet** button and copy the URL. Then click the **API Key** link, add a new key, save and copy the API key.

![click the Get code snippet button](https://paper-attachments.dropboxusercontent.com/s_E1CBF4A45089EFC3D9C461B80BB9F97D882823FECDCA010DD86FEAC6EC26A79C_1706442511712_Screenshot+2024-01-20+at+22.40.52.png)
![Copy URL](https://paper-attachments.dropboxusercontent.com/s_E1CBF4A45089EFC3D9C461B80BB9F97D882823FECDCA010DD86FEAC6EC26A79C_1706442537662_Screenshot+2024-01-20+at+22.42.04.png)
![Add a new key](https://paper-attachments.dropboxusercontent.com/s_724B5DB0ED02FBEDE105C37DC2102A0D29EC66EAFDCC27146EA9AEF451272040_1703596030165_Screenshot+2023-12-21+at+23.42.03.png)

We must keep the copied URL and API key as they will come in handy when building our application.

## Building the project management application with Xata and Flutter

To get started, we need to clone the project by navigating to the desired directory and running the command below:

```bash
git clone https://github.com/Mr-Malomz/flutter_xata.git
```

**Running the project**

We need to install the project dependencies by running the command below:

```bash
flutter pub get
```

Then, run the project using the following command:

```
flutter run
```

The command above will run the application on the selected device.

![Home screen](https://paper-attachments.dropboxusercontent.com/s_2D197ACB76C713F70DA9598647372925B7D6E4260260797B9E4DC9FB20111240_1706708100374_Screenshot+2024-01-31+at+02.09.39.png)
![Create screen](https://paper-attachments.dropboxusercontent.com/s_2D197ACB76C713F70DA9598647372925B7D6E4260260797B9E4DC9FB20111240_1706708118715_Screenshot+2024-01-31+at+02.09.50.png)

**Setup environment variable**

Next, we must add our database URL and API key as an environment variable. To do this, create `.env` file in the root directory and add the copied URL and API key.

```bash
XATA_DATABASE_URL= <REPLACE WITH THE COPIED DATABASE URL>
XATA_API_KEY=<REPLACE WITH THE COPIED API KEY>
```

**Create the API models**

To represent the application data, we need to create a `utils.dart` file in the `lib` folder and add the snippet below. The model will cater to converting the response sent from the JSON response to a Dart object and JSON serialization

```dart
class Project {
  String? id;
  String name;
  String description;
  String status;

  Project({
    this.id,
    required this.name,
    required this.description,
    required this.status,
  });

  Map<dynamic, dynamic> toJson() {
    return {
      "name": name,
      "description": description,
      "status": status,
    };
  }

  factory Project.fromJson(Map<dynamic, dynamic> json) {
    return Project(
      id: json['id'],
      name: json['name'],
      description: json['description'],
      status: json['status'],
    );
  }
}
```

**Create a service**

With that done, we need to create a service file to separate the application core logic from the UI. To do this, create a `xata_service.dart` file inside the `lib` directory. Then, add the snippet below:

```dart
import 'package:dio/dio.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_xata/utils.dart';

class XataService {
  final _dio = Dio();
  static String _apiKey = dotenv.get("XATA_API_KEY");
  static String _baseURL = dotenv.get("XATA_DATABASE_URL");

  final _headers = {
    "content-type": "application/json",
    "AUTHORIZATION": "Bearer $_apiKey",
  };

  Future<List<Project>> getProjects() async {
    var response = await _dio.post(
      "$_baseURL:main/tables/Project/query",
      options: Options(headers: _headers),
    );

    if (response.statusCode == 200) {
      var respList = response.data['records'] as List;
      var projectList = respList.map((json) => Project.fromJson(json)).toList();
      return projectList;
    } else {
      throw Exception('Error getting projects');
    }
  }

  Future<Project> getSingleProject(String id) async {
    var response = await _dio.get(
      "$_baseURL:main/tables/Project/data/$id",
      options: Options(headers: _headers),
    );

    if (response.statusCode == 200) {
      var resp = response.data;
      var project = Project.fromJson(resp);
      return project;
    } else {
      throw Exception('Error getting project');
    }
  }

  Future createProject(Project newProject) async {
    var response = await _dio.post(
      "$_baseURL:main/tables/Project/data",
      options: Options(headers: _headers),
      data: newProject.toJson(),
    );

    if (response.statusCode == 201) {
      return response.data;
    } else {
      throw Exception('Error creating project');
    }
  }

  Future updateProject(String id, Project updatedProject) async {
    var response = await _dio.put(
      "$_baseURL:main/tables/Project/data/$id",
      options: Options(headers: _headers),
      data: updatedProject.toJson(),
    );

    if (response.statusCode == 200) {
      return response.data;
    } else {
      throw Exception('Error updating project');
    }
  }

  Future deleteProject(String id) async {
    var response = await _dio.delete(
      "$_baseURL:main/tables/Project/data/$id",
      options: Options(headers: _headers),
    );

    if (response.statusCode == 204) {
      return response.data;
    } else {
      throw Exception('Error deleting project');
    }
  }
}
```

The snippet above does the following:

-   Imports the required dependencies
-   Creates an `XataService` class with `_apiKey`, `_baseURL`, and `_headers` properties to connect to the Xata instance
-   Creates a `getProjects`, `getSingleProject`, `createProject`, `updateProject`, and `deleteProject` method that uses the `_storage` property to get, save, and preview images

**Consuming the service**

With that done, we can use the service to perform the required operation.

**Get all projects**

To get started, we need to modify the `home.dart` file in the `screens` directory and update it by doing the following:

First, we need to import the required dependencies and create a method to get the list of projects saved in the database:

```dart
//Other imports goes here
import 'package:flutter_xata/utils.dart';
import 'package:flutter_xata/xata_service.dart';

class Home extends StatefulWidget {
  const Home({super.key});
  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late List<Project> projects;
  bool _isLoading = false;
  bool _isError = false;

  @override
  void initState() {
    getProjects();
    super.initState();
  }

  getProjects() {
    setState(() {
      _isLoading = true;
    });
    XataService().getProjects().then((value) {
      setState(() {
        projects = value;
        _isLoading = false;
      });
    }).catchError((onError) {
      setState(() {
        _isLoading = false;
        _isError = true;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    //UI CODE GOES HERE
  }
}
```

The snippet above does the following:

-   Imports the required dependencies
-   **Lines 12-14**: Creates the `projects`, `_isLoading`, and `_isError` properties to manage the application state
-   **Lines 16-37**: Creates a `getProjects` method to get the list of available projects on the database using the `XataService().getProjects` and set states accordingly

Lastly, we need to modify the UI to use the states and method created to get the projects list.

```dart
//imports goes here

class Home extends StatefulWidget {
  //code goes here
}

class _HomeState extends State<Home> {
  //states goes here

  @override
  void initState() {
    //code goes here
  }

  getProjects() {
    //code goes here
  }

  @override
  Widget build(BuildContext context) {
    return _isLoading
        ? const Center(
            child: CircularProgressIndicator(
            color: Colors.blue,
          ))
        : _isError
            ? const Center(
                child: Text(
                  'Error getting projects',
                  style: TextStyle(
                    color: Colors.red,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              )
            : Scaffold(
                appBar: AppBar(
                  title: const Text('Projects',
                      style: TextStyle(color: Colors.white)),
                  backgroundColor: Colors.black,
                ),
                body: ListView.builder(
                  itemCount: projects.length,
                  itemBuilder: (context, index) {
                    return InkWell(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) =>
                                Detail(id: projects[index].id as String),
                          ),
                        );
                      },
                      child: Container(
                        decoration: const BoxDecoration(
                          border: Border(
                            bottom: BorderSide(width: .5, color: Colors.grey),
                          ),
                        ),
                        padding: EdgeInsets.fromLTRB(10, 20, 10, 20),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            Expanded(
                              flex: 7,
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    projects[index].name,
                                    style: TextStyle(
                                        color: Colors.black,
                                        fontWeight: FontWeight.w800),
                                  ),
                                  const SizedBox(height: 10.0),
                                  Row(
                                    children: [
                                      Icon(projects[index].status == "Started"
                                          ? Icons.start
                                          : Icons.stop_circle_outlined),
                                      const SizedBox(width: 5.0),
                                      Text(projects[index].status)
                                    ],
                                  ),
                                  const SizedBox(height: 10.0),
                                  Text(projects[index].description)
                                ],
                              ),
                            ),
                            const Column(
                              crossAxisAlignment: CrossAxisAlignment.end,
                              children: [
                                SizedBox(height: 10.0),
                                Icon(Icons.arrow_forward_ios_rounded)
                              ],
                            ),
                          ],
                        ),
                      ),
                    );
                  },
                ),
                floatingActionButton: FloatingActionButton(
                  onPressed: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => const Create(),
                      ),
                    );
                  },
                  backgroundColor: Colors.black,
                  tooltip: 'Create project',
                  child: const Icon(
                    Icons.add,
                    color: Colors.white,
                  ),
                ),
              );
  }
}
```

**Create project**

To create a project, we need to modify the `create.dart` file in the `screen` directory and update it by doing the following:

First, we need to import the required dependency and create a method to save the project to the database:

```dart
//other import goes here
import 'package:flutter_xata/screens/home.dart';
import 'package:flutter_xata/utils.dart';
import 'package:flutter_xata/xata_service.dart';

class Create extends StatefulWidget {
  const Create({
    Key? key,
  }) : super(key: key);
  @override
  State<Create> createState() => _CreateState();
}

class _CreateState extends State<Create> {
  final _formKey = GlobalKey<FormState>();
  var _selected = '';
  var _dropdownItems = ["Started", "Not_Started"];
  final TextEditingController _name = TextEditingController();
  final TextEditingController _description = TextEditingController();
  bool _isLoading = false;

  createProject() {
    setState(() {
      _isLoading = true;
    });

    Project newProject = Project(
      name: _name.text,
      description: _description.text,
      status: _selected,
    );

    XataService().createProject(newProject).then((value) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Project created successfully!')),
      );
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => const Home()),
      );
    }).catchError((onError) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Error creating project!')),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    //UI CODE GOES HERE
  }
}
```

The snippet above does the following:

-   Import the required dependencies
-   **Lines 18-20**: Creates the `_name`, `_description`, and `_isLoading` properties to manage the application state
-   **Lines 22-52**: Creates a `createProject` method to save the project using the `XataService().createProject` service, set states accordingly

Lastly, we need to modify the UI to use the method and states created to process the form.

```dart
//import goes here

class Create extends StatefulWidget {
  //code goes here
}

class _CreateState extends State<Create> {
  //states goes here

  createProject() {
    //code goes here
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title:
            const Text("Create project", style: TextStyle(color: Colors.white)),
        backgroundColor: Colors.black,
        iconTheme: const IconThemeData(color: Colors.white),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 30.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Name',
                        style: TextStyle(
                          color: Colors.grey,
                          fontSize: 14.0,
                        ),
                      ),
                      const SizedBox(height: 5.0),
                      TextFormField(
                        controller: _name,
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Please input name';
                          }
                          return null;
                        },
                        decoration: InputDecoration(
                          contentPadding: const EdgeInsets.symmetric(
                              vertical: 10, horizontal: 20),
                          hintText: "input name",
                          fillColor: Colors.white,
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          errorBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.red),
                          ),
                        ),
                        keyboardType: TextInputType.text,
                        maxLines: null,
                      ),
                      const SizedBox(height: 30.0),
                      const Text(
                        'Status',
                        style: TextStyle(
                          color: Colors.grey,
                          fontSize: 14.0,
                        ),
                      ),
                      const SizedBox(height: 5.0),
                      DropdownButtonFormField(
                        items: _dropdownItems.map((String item) {
                          return DropdownMenuItem(
                            value: item,
                            child: Text(item),
                          );
                        }).toList(),
                        onChanged: (value) {
                          setState(() => _selected = value!);
                        },
                        decoration: InputDecoration(
                          contentPadding: const EdgeInsets.symmetric(
                              vertical: 10, horizontal: 20),
                          hintText: "select status",
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          errorBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.red),
                          ),
                        ),
                      ),
                      const SizedBox(height: 30.0),
                      const Text(
                        'Description',
                        style: TextStyle(
                          color: Colors.grey,
                          fontSize: 14.0,
                        ),
                      ),
                      const SizedBox(height: 5.0),
                      TextFormField(
                        controller: _description,
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Please input ydescription';
                          }
                          return null;
                        },
                        maxLines: 5,
                        decoration: InputDecoration(
                          contentPadding: const EdgeInsets.symmetric(
                              vertical: 10, horizontal: 20),
                          hintText: "input description",
                          fillColor: Colors.white,
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          errorBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.red),
                          ),
                        ),
                        keyboardType: TextInputType.multiline,
                      ),
                    ],
                  ),
                ],
              ),
              const SizedBox(height: 30.0),
              SizedBox(
                height: 45,
                width: double.infinity,
                child: TextButton(
                  onPressed: _isLoading
                      ? null
                      : () {
                          if (_formKey.currentState!.validate()) {
                            createProject();
                          }
                        },
                  style: ButtonStyle(
                    backgroundColor:
                        MaterialStateProperty.all<Color>(Colors.black),
                  ),
                  child: const Text(
                    'Create project',
                    style: TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 14.0,
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```

**Get a project, edit a project and delete project**

To perform the stated operations in our application, we need to modify the `detail.dart` file in the `screens` directory and update it by doing the following:

First, we need to import the required dependencies and create methods to get, edit, and delete projects.

```dart
//import goee here
import 'package:flutter_xata/screens/home.dart';
import 'package:flutter_xata/utils.dart';
import 'package:flutter_xata/xata_service.dart';

class Detail extends StatefulWidget {
  const Detail({Key? key, required this.id}) : super(key: key);
  final String id;
  @override
  State<Detail> createState() => _DetailState();
}

class _DetailState extends State<Detail> {
  final _formKey = GlobalKey<FormState>();
  var _selected = '';
  var _dropdownItems = ["Started", "Not_Started"];
  final TextEditingController _name = TextEditingController();
  final TextEditingController _description = TextEditingController();
  bool _isLoading = false;
  bool _isSubmitting = false;
  bool _isError = false;

  @override
  void initState() {
    getSingleProject();
    super.initState();
  }

  getSingleProject() {
    setState(() {
      _isLoading = true;
    });
    XataService().getSingleProject(widget.id).then((value) {
      setState(() {
        _isLoading = false;
      });
      _name.text = value.name;
      _description.text = value.description;
      _selected = value.status;
    }).catchError((onError) {
      setState(() {
        _isLoading = false;
        _isError = true;
      });
    });
  }

  updateProject() {
    setState(() {
      _isSubmitting = true;
    });
    Project updatedProject = Project(
      name: _name.text,
      description: _description.text,
      status: _selected,
    );
    XataService().updateProject(widget.id, updatedProject).then((value) {
      setState(() {
        _isSubmitting = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Project updated successfully!')),
      );
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => const Home()),
      );
    }).catchError((onError) {
      setState(() {
        _isSubmitting = false;
        _isError = true;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Error updating project!')),
      );
    });
  }

  deleteProject() {
    setState(() {
      _isSubmitting = true;
    });
    XataService().deleteProject(widget.id).then((value) {
      setState(() {
        _isSubmitting = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Project deleted successfully!')),
      );
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => const Home()),
      );
    }).catchError((onError) {
      setState(() {
        _isSubmitting = false;
        _isError = true;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Error deleting project!')),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    //UI CODE GOES HERE
  }
}
```

The snippet above does the following:

-   Import the required dependencies
-   **Lines 17-21**: Creates the `_name`, `_description`, `_isLoading`, `_isSubmitting`, and `_isError` properties to manage the application state
-   **Lines 23-103**: Creates the `getSingleProject`, `updateProject`, and `deleteProject` methods to retrieve details of the selected project, update it, and delete it using the `XataService().getSingleProject`, `XataService().updateProject`, and `XataService().deleteProject` services, respectively. Set states accordingly.

Lastly, we need to modify the UI to use the methods and states created to process the operations.

```dart
//imports goes here

class Detail extends StatefulWidget {
  //code goes here
}

class _DetailState extends State<Detail> {
  //state goes here

  @override
  void initState() {
    //code goes here
  }

  getSingleProject() {
    //code goes here
  }

  updateProject() {
    //code goes here
  }

  deleteProject() {
    //code goes here
  }

  @override
  Widget build(BuildContext context) {
    return _isLoading
        ? const Center(
            child: CircularProgressIndicator(
            color: Colors.blue,
          ))
        : _isError
            ? const Center(
                child: Text(
                  'Error getting project,
                  style: TextStyle(
                    color: Colors.red,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              )
            : Scaffold(
                appBar: AppBar(
                  title: const Text("Details",
                      style: TextStyle(color: Colors.white)),
                  backgroundColor: Colors.black,
                  iconTheme: const IconThemeData(color: Colors.white),
                ),
                body: Padding(
                  padding: const EdgeInsets.symmetric(
                      horizontal: 16.0, vertical: 30.0),
                  child: Form(
                    key: _formKey,
                    child: Column(
                      children: [
                        Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                const Text(
                                  'Name',
                                  style: TextStyle(
                                    color: Colors.grey,
                                    fontSize: 14.0,
                                  ),
                                ),
                                const SizedBox(height: 5.0),
                                TextFormField(
                                  controller: _name,
                                  validator: (value) {
                                    if (value == null || value.isEmpty) {
                                      return 'Please input name';
                                    }
                                    return null;
                                  },
                                  decoration: InputDecoration(
                                    contentPadding: const EdgeInsets.symmetric(
                                        vertical: 10, horizontal: 20),
                                    hintText: "input name",
                                    fillColor: Colors.white,
                                    focusedBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.grey),
                                    ),
                                    enabledBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.grey),
                                    ),
                                    errorBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.red),
                                    ),
                                  ),
                                  keyboardType: TextInputType.text,
                                  maxLines: null,
                                ),
                                const SizedBox(height: 30.0),
                                const Text(
                                  'Status',
                                  style: TextStyle(
                                    color: Colors.grey,
                                    fontSize: 14.0,
                                  ),
                                ),
                                const SizedBox(height: 5.0),
                                DropdownButtonFormField(
                                  value: _selected,
                                  items: _dropdownItems.map((String item) {
                                    return DropdownMenuItem(
                                      value: item,
                                      child: Text(item),
                                    );
                                  }).toList(),
                                  onChanged: (value) {
                                    setState(() => _selected = value!);
                                  },
                                  decoration: InputDecoration(
                                    contentPadding: const EdgeInsets.symmetric(
                                        vertical: 10, horizontal: 20),
                                    hintText: "select status",
                                    focusedBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.grey),
                                    ),
                                    enabledBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.grey),
                                    ),
                                    errorBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.red),
                                    ),
                                  ),
                                ),
                                const SizedBox(height: 30.0),
                                const Text(
                                  'Description',
                                  style: TextStyle(
                                    color: Colors.grey,
                                    fontSize: 14.0,
                                  ),
                                ),
                                const SizedBox(height: 5.0),
                                TextFormField(
                                  controller: _description,
                                  validator: (value) {
                                    if (value == null || value.isEmpty) {
                                      return 'Please input ydescription';
                                    }
                                    return null;
                                  },
                                  maxLines: 5,
                                  decoration: InputDecoration(
                                    contentPadding: const EdgeInsets.symmetric(
                                        vertical: 10, horizontal: 20),
                                    hintText: "input description",
                                    fillColor: Colors.white,
                                    focusedBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.grey),
                                    ),
                                    enabledBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.grey),
                                    ),
                                    errorBorder: OutlineInputBorder(
                                      borderRadius: BorderRadius.circular(10),
                                      borderSide:
                                          const BorderSide(color: Colors.red),
                                    ),
                                  ),
                                  keyboardType: TextInputType.multiline,
                                ),
                              ],
                            ),
                          ],
                        ),
                        const SizedBox(height: 30.0),
                        SizedBox(
                          height: 45,
                          width: double.infinity,
                          child: TextButton(
                            onPressed: _isSubmitting
                                ? null
                                : () {
                                    if (_formKey.currentState!.validate()) {
                                      updateProject();
                                    }
                                  },
                            style: ButtonStyle(
                              backgroundColor: MaterialStateProperty.all<Color>(
                                  Colors.black),
                            ),
                            child: const Text(
                              'Update project',
                              style: TextStyle(
                                color: Colors.white,
                                fontWeight: FontWeight.bold,
                                fontSize: 14.0,
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                floatingActionButton: FloatingActionButton(
                  onPressed: _isSubmitting
                      ? null
                      : () {
                          deleteProject();
                        },
                  backgroundColor: Colors.red,
                  tooltip: 'Delete',
                  child: const Icon(
                    Icons.delete,
                    color: Colors.white,
                  ),
                ),
              );
  }
}
```

With that done, we restart the application using the code editor or run the command below:

```bash
flutter run
```

![](https://media.giphy.com/media/RkaedMynrKspOXncN3/giphy.gif)

## Conclusion

This post discussed how to build a basic project management application with Xata and Flutter. In addition to the functionalities explored earlier, Xata also includes well-tailored features that developers can harness to build applications ranging from small to large.

These resources may also be helpful:

-   [Xata documentation](https://xata.io/docs?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog)
-   [DIO package](https://github.com/cfug/dio/tree/main/dio?utm_source=fullstackwriter&utm_medium=fullstackwriter-blog)