Courses

[NEW] Flutter 3 Mobile App with Laravel 12 API

Creating Categories

We currently have List, Edit, and Delete actions for our Categories. So, let's build the Create action:


Adding API Support

Let's start by adding the API support for creating a Category:

// ...
 
Future<void> deleteCategory(id) async {
String url = '$baseUrl/api/categories/$id';
final http.Response response = await http.delete(
Uri.parse(url),
);
 
if (response.statusCode != 204) {
throw Exception('Failed to delete category');
}
}
 
Future addCategory(String name) async {
String url = '$baseUrl/api/categories';
final http.Response response = await http.post(
Uri.parse(url),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'name': name,
}),
);
 
if (response.statusCode != 201) {
throw Exception('Failed to create category');
}
 
final Map<String, dynamic> data = json.decode(response.body);
return Category.fromJson(data['data']);
}

There's nothing really new here. We are just sending a POST request to the API with the category name. The API will return the created category.


Updating Our Provider

Next, we need to update our Provider:

// ...
 
Future deleteCategory(Category category) async {
try {
await apiService.deleteCategory(category.id);
categories.remove(category);
 
notifyListeners();
} catch (e) {
print('Failed to delete category: $e');
}
}
 
Future addCategory(String name) async {
try {
Category addedCategory = await apiService.addCategory(name);
categories.add(addedCategory);
 
notifyListeners();
} catch (e) {
print('Failed to create category: $e');
}
}

At this point, we should start noticing a pattern in our Provider methods. They all follow the same structure:

  1. Call the API service method.
  2. Update the local state.
  3. Notify the listeners.
  4. Catch any errors.

And, of course, the workflow that we have been following is:

  1. Create the API service method.
  2. Update the Provider method.
  3. Use the Provider method in the UI.

Adding Floating Button to The List

Okay, we have prepared everything we need to communicate with the API, so let's add a floating button to the list screen:

import 'package:laravel_api_flutter_app/widgets/category_add.dart';
 
// ...
 
return Scaffold(
appBar: AppBar(
title: Text('Categories List'),
),
body: ListView.builder(
itemCount: categories.length,
itemBuilder: (context, index) {
Category category = categories[index];
return ListTile(
title: Text(category.name),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return CategoryEdit(category, provider.updateCategory);
},
);
},
icon: Icon(Icons.edit),
),
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Delete Category'),
content: Text(
'Are you sure you want to delete this category?'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel'),
),
TextButton(
onPressed: () {
provider.deleteCategory(category);
Navigator.pop(context);
},
child: Text('Delete'),
),
],
);
});
},
icon: Icon(Icons.delete),
),
],
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return CategoryAdd(provider.addCategory);
},
);
},
child: Icon(Icons.add),
),
);
},

Adding Create Category Screen

It's time to add the actual widgets/category_add.dart widget:

For this, we want you to practice a bit. Copy the widgets/category_edit.dart file and rename it to category_add.dart. Then, update the file to act as a form for creating a category:

  • Rename the Classes
  • Remove category from the constructor (and property)
  • Remove widget.category and all of its references
  • Change the callback parameter to include name instead of category
  • Update the Buttons

Once these are completed and you are ready to test - you should modify Laravel code a little bit:

app/Http/Controllers/Api/CategoryController.php

// ...
 
public function store(StoreCategoryRequest $request): CategoryResource
{
$category = auth()->user()->categories()->create($request->validated());
$category = Category::create($request->validated());
 
return new CategoryResource($category);
}
 
// ...

Now, you should be able to create categories from the Flutter app:

Of course, if you are stuck somewhere - here's the complete widgets/category_add.dart:

import 'package:flutter/material.dart';
import 'package:laravel_api_flutter_app/services/api.dart';
 
class CategoryAdd extends StatefulWidget {
final Function categoryCallback;
 
const CategoryAdd(this.categoryCallback, {super.key});
 
@override
CategoryAddState createState() => CategoryAddState();
}
 
class CategoryAddState extends State<CategoryAdd> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final categoryNameController = TextEditingController();
ApiService apiService = ApiService();
String errorMessage = '';
 
Future saveCategory(context) async {
final form = _formKey.currentState;
 
if (!form!.validate()) {
return;
}
 
await widget.categoryCallback(categoryNameController.text);
 
Navigator.pop(context);
}
 
@override
void initState() {
super.initState();
}
 
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Form(
key: _formKey,
child: Column(
children: [
Text('Add Category'),
TextFormField(
decoration: InputDecoration(
labelText: 'Category Name',
),
controller: categoryNameController,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Please enter category name';
}
return null;
},
onChanged: (String value) {
setState(() {
errorMessage = '';
});
},
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () {
saveCategory(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
),
child: Text('Create'),
),
],
),
),
Text(
errorMessage,
style: TextStyle(color: Colors.red),
),
],
),
),
);
}
}

That's it! You have successfully implemented the Create action for Categories.


In the next lesson, we will start implementing some authentication features. Specifically Registration.


Check out the GitHub Commit for this lesson.

Previous: Deleting Categories

No comments yet…

avatar
You can use Markdown