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:
- Call the API service method.
- Update the local state.
- Notify the listeners.
- Catch any errors.
And, of course, the workflow that we have been following is:
- Create the API service method.
- Update the Provider method.
- 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 ofcategory
- 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.
No comments yet…