Courses

[NEW] Flutter 3 Mobile App with Laravel 12 API

Using API Authentication Tokens

Now that our authentication is completed, we can work on consuming API tokens within our application. Tokens are important to limit access to our application.

In this lesson, we will:

  • Store the token in our Auth Provider
  • Add Headers to our API requests - including the token
  • Refactor our API Service to use the token
  • And some other minor changes

Let's start by refactoring our API service to use the token.


Refactoring API Service - Consuming API Tokens

Currently, we have no way to pass the token to our API service, so let's add that:

Note: We will declare the token as a [non-nullable variable] using the late keyword (https://dart.dev/language/variables#late-variables).

lib/services/api.dart

class ApiService {
ApiService();
late String token;
 
ApiService(String token) {
this.token = token;
}
}

Next, we need to use that token by adding headers to our API requests:

lib/services/api.dart

// ...
 
Future<List<Category>> fetchCategories() async {
final http.Response response =
await http.get(Uri.parse('$baseUrl/api/categories'));
final http.Response response = await http
.get(Uri.parse('$baseUrl/api/categories'), headers: <String, String>{
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
});
// ...
}
 
Future saveCategory(Category category) async {
String url = '$baseUrl/api/categories/${category.id}';
final http.Response response = await http.put(
Uri.parse(url),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
body: jsonEncode(<String, String>{
'name': category.name,
}),
);
// ...
}
 
Future<void> deleteCategory(id) async {
String url = '$baseUrl/api/categories/$id';
final http.Response response = await http.delete(
Uri.parse(url),
);
final http.Response response = await http.delete(Uri.parse(url), headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
});
 
// ...
}
 
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',
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
body: jsonEncode(<String, String>{
'name': name,
}),
);
 
// ...
}
 
// ...

Updating Category Provider

Now that we have the token in our API service, we can update our Category Provider to use the token:

import 'package:laravel_api_flutter_app/providers/auth_provider.dart';
 
// ...
 
class CategoryProvider extends ChangeNotifier {
List<Category> categories = [];
late ApiService apiService;
late AuthProvider authProvider;
 
CategoryProvider() {//
apiService = ApiService();
CategoryProvider(AuthProvider authProvider) {
this.authProvider = authProvider;
apiService = ApiService(authProvider.token);
init();
}
 
// ...
}

Updating Registration and Login

We have to fix one thing that we left out - we are storing the token as a response from our Login/Register functions:

lib/screens/auth/Login.dart

// ...
 
try {
String token = await provider.login(
await provider.login(
emailController.text, passwordController.text, deviceName);
} catch (Exception) {
 
// ...

And:

lib/screens/auth/Register.dart

// ...
 
try {
String token = await provider.register(
await provider.register(
nameController.text,
emailController.text,
 
// ...

While this might work even without the changes, it is better to store the token in the AuthProvider and use it from there.


Removing API Service From Add/Edit Category

We have some redundant code on our Add/Edit Category pages, as we did with Auth pages. We can remove the API service from there:

lib/widgets/category_add.dart

import 'package:laravel_api_flutter_app/services/api.dart';
 
// ...
 
class CategoryAddState extends State<CategoryAdd> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final categoryNameController = TextEditingController();
ApiService apiService = ApiService();
String errorMessage = '';
 
// ...

And:

lib/widgets/category_edit.dart

import 'package:laravel_api_flutter_app/services/api.dart';
 
// ...
 
class CategoryEditState extends State<CategoryEdit> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final categoryNameController = TextEditingController();
ApiService apiService = ApiService();
String errorMessage = '';
 
// ...

Modifying How Auth Provider is Registered

The last change we have to do is to modify how we register our AuthProvider in our main.dart file:

// ...
return MultiProvider(
providers: [
ChangeNotifierProvider<CategoryProvider>(
create: (context) => CategoryProvider()),
create: (context) => CategoryProvider(authProvider)),
ChangeNotifierProvider<AuthProvider>(
create: (context) => AuthProvider()),
],
child: MaterialApp(title: 'Welcome to Flutter', routes: {
'/': (context) {
final authProvider = Provider.of<AuthProvider>(context);
return authProvider.isAuthenticated ? Home() : Login();
return authProvider.isAuthenticated ? CategoriesList() : Login();
},

Laravel Changes

We have to make some changes to our Laravel application to make it work with API tokens. First, we need to add the sanctum middleware to our API routes:

routes/api.php

// ...
 
Route::group([], function () {
Route::group(['middleware' => 'auth:sanctum'], function () {
//...

And restore the CategoryController create() method:

app/Http/Controllers/CategoryController.php

// ...
 
$category = Category::create($request->validated());
$category = auth()->user()->categories()->create($request->validated());
 
// ...

That's it - we should be able to log in normally and use the API token to access our API.


In the next lesson, we will add a bottom navigation bar to navigate between multiple pages like Categories, Transactions, and Home.


Check out the GitHub Commit for this lesson.

Previous: Adding Login Functionality
avatar
Luis Antonio Parrado

I found some errors:

  • In lib/services/api.dart in the method Future saveCategory(Category category) the headers was added in the body.
  • We also can't find where the changes were made to the lib/providers/auth_provider.dart
avatar

I've updated the saveCategory method to be correct, thanks!

As for the changes - they did not impact the code at this point (for example, the removal of return has 0 impact at this current stage) :)

avatar
You can use Markdown
avatar
You can use Markdown