Tutorial 8: Flutter Networking, Authentication, and Integration
Platform-Based Programming (CSGE602022) — organized by the Faculty of Computer Science, University of Indonesia, Odd Semester 2024/2025
Learning Objectives
After completing this tutorial, students are expected to:
- Understand the structure and creation of models in Flutter.
- Understand how to fetch, process, and display data from web services.
- Understand basic state management using Provider in Flutter.
- Able to authenticate with the Django web service with the Flutter application.
Model in Flutter
In this tutorial, we will call a web service and display the results on the Flutter page we created. However, before making the web service call, we need to define the model we use for the web service call. In Flutter, models are defined using a class, similar to what has been learned in the OOP section of Programming Foundations 2.
The code below is just an example. However, we highly recommend you to read it because the concepts will be used in the following sections.
Here is an example of class in Flutter.
class Song {
Song({
this.id,
this.title,
this.artist,
this.releaseDate,
});
int id;
String title;
String artist;
DateTime releaseDate;
}
If you encounter errors while creating the class, add the required
keyword to each class parameter in the constructor section.
So far, we have successfully created the class. Next, we will add some codes to form a Song
model. This Song
model represents the response from the web service call.
Import dart:convert
at the top of the file.
import 'dart:convert';
...
In the Song
class, add the following code.
factory Song.fromJson(Map<String, dynamic> json) => Song(
id: json["id"],
title: json["title"],
artist: json["artist"],
releaseDate: json["releaseDate"],
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"artist": artist,
"releaseDate": releaseDate,
};
Add the following code outside the Song
class.
Song songFromJson(String str) => Song.fromJson(json.decode(str));
String songToJson(Song data) => json.encode(data.toJson());
The final code will look like this. It will display a single Song
object from the web service.
import 'dart:convert';
Song songFromJson(String str) => Song.fromJson(json.decode(str));
String songToJson(Song data) => json.encode(data.toJson());
class Song {
Song({
this.id,
this.title,
this.artist,
this.releaseDate,
});
int id;
String title;
String artist;
String releaseDate;
factory Song.fromJson(Map<String, dynamic> json) => Song(
id: json["id"],
title: json["title"],
artist: json["artist"],
releaseDate: json["releaseDate"],
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"artist": artist,
"releaseDate": releaseDate,
};
}
Explanation
There are additional codes such as the toJson
and fromJson
methods inside the Song
class. This is because when we make a request to a web service with the GET method, we usually receive the result in the form of JSON. Therefore, we need to convert the data with the fromJson
method so that Flutter recognizes this JSON as an object of the Song
class. Additionally, there is also the toJson
method, which will be used when sending data to the web service (such as POST or PUT).
Here is an example response from a web service with the GET method that can be converted into the Song
model.
{
"id": 1,
"title": "APT.",
"artist": "Rosé & Bruno Mars",
"releaseDate": "2024-10-18T00:00:00+0000"
}
Now, what if the response from the web service consists of a collection of JSON objects? It is actually the same as the code above, but with some modifications to the songFromJson
and songToJson
methods.
The code is as follows.
List<Song> songsFromJson(String str) => List<Song>.from(json.decode(str).map((song) => Song.fromJson(song)));
String songsToJson(List<Song> data) => json.encode(List<dynamic>.from(data.map((song) => song.toJson())));
Here is an example response from a web service with the GET method that can be converted into the Song
model.
[
{
"id": 1,
"title": "APT.",
"artist": "Rosé & Bruno Mars",
"releaseDate": "2024-10-18T21:39:14+0200"
},
{
"id": 2,
"title": "Espresso",
"artist": "Sabrina Carpenter",
"releaseDate": "2024-10-18T00:00:00+0000"
}
{
"id": 3,
"title": "Supernatural",
"artist": "NewJeans",
"releaseDate": "2024-10-18T00:00:00+0000"
}
]
Fetching Data from Web Service in Flutter
During application development, there are times when we need to fetch external data from outside our application (the Internet) to display in our application. This tutorial aims to understand how to fetch data from a web service in Flutter.
In general, there are several steps when you want to display data from another web service to a Flutter application, namely:
-
Add the
http
dependency to the project; this dependency is used for exchanging HTTP requests. -
Create a model according to the response from the data originating from that web service.
-
Make an HTTP request to the web service using the
http
dependency. -
Convert the object obtained from the web service to the model we created in step two.
-
Display the converted data to the application using
FutureBuilder
.
For further explanation, you can read the details here.
Basic State Management using Provider
Provider
is a wrapper around InheritedWidget
to make InheritedWidget
easier to use and more reusable. InheritedWidget
itself is the basic class for Flutter widgets that efficiently spread information to other widgets in one tree.
The benefits of using provider
are as follows:
- Allocating resources becomes simpler.
- Lazy-loading.
- Reducing boilerplate every time a new class is created.
- Supported by Flutter Devtools so that
provider
can be tracked from Devtool. - Scalability improvement for classes that utilize complex built-in listening mechanisms.
For more information about provider
, please visit the Provider package page.
Tutorial: Django-Flutter Authentication Integration
NOTES
For now, if you follow the steps to completion, integration between Flutter and PWS cannot be achieved yet. This is due to PWS still being insecure (using http://
, not https://
). Therefore, for this tutorial, you are welcome to experiment using only localhost or another deployment platform that is already secure.
Set Up Authentication in Django for Flutter
Follow the steps below to integrate the authentication system in Django.
-
Create a
django-app
namedauthentication
in your Django project you created earlier. -
Add
authentication
toINSTALLED_APPS
in the main projectsettings.py
file of your Django application.tipIf you forgot how to perform steps 1 and 2, you can read Tutorial 1 again.
-
Run the command
pip install django-cors-headers
to install the required library. Don't forget to enable the Python virtual environment prior. Don't forget to adddjango-cors-headers
torequirements.txt
as well. -
Add
corsheaders
toINSTALLED_APPS
in the main projectsettings.py
file of your Django application. -
Add
corsheaders.middleware.CorsMiddleware
toMIDDLEWARE
in the main projectsettings.py
file of your Django application. -
Add the following variables to the main project
settings.py
file of your Django application.CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None' -
For the purpose of integrating with Django from the Android emulator, add
10.0.2.2
toALLOWED_HOSTS
in thesettings.py
file.ALLOWED_HOSTS = [..., ..., "10.0.2.2"]
-
Create a view method for login in
authentication/views.py
.from django.contrib.auth import authenticate, login as auth_login
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def login(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
auth_login(request, user)
# Successful login status.
return JsonResponse({
"username": user.username,
"status": True,
"message": "Login successful!"
# Add other data if you want to send data to Flutter.
}, status=200)
else:
return JsonResponse({
"status": False,
"message": "Login failed, account disabled."
}, status=401)
else:
return JsonResponse({
"status": False,
"message": "Login failed, check email or password again."
}, status=401) -
Create a
urls.py
file in theauthentication
folder and add URL routing to the function created with thelogin/
endpoint.from django.urls import path
from authentication.views import login
app_name = 'authentication'
urlpatterns = [
path('login/', login, name='login'),
] -
Finally, add
path('auth/', include('authentication.urls'))
, to theshopping_list/urls.py
file.
Integrate Authentication System in Flutter
To facilitate the creation of the authentication system, the teaching assistant team has created a Flutter package that can be used to contact the Django web service (including GET
and POST
operations).
The package can be accessed via the following link: pbp_django_auth
Follow the steps below to integrate the authentication system into Flutter.
-
Install the package provided by the teaching assistant team by running the following commands in the Terminal.
flutter pub add provider
flutter pub add pbp_django_auth -
To use the package, you need to modify the root widget to provide the
CookieRequest
library to all child widgets usingProvider
.For example, if your application was like this before:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Mental Health Tracker',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.deepPurple,
).copyWith(secondary: Colors.deepPurple[400]),
),
home: MyHomePage(),
);
}
}Change it to:
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return Provider(
create: (_) {
CookieRequest request = CookieRequest();
return request;
},
child: MaterialApp(
title: 'Mental Health Tracker',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.deepPurple,
).copyWith(secondary: Colors.deepPurple[400]),
),
home: MyHomePage(),
),
);
}
}This will create a new
Provider
object that will share an instance ofCookieRequest
with all components in the application.noteDon't forget to add
import 'package:pbp_django_auth/pbp_django_auth.dart';
andimport 'package:provider/provider.dart';
to the top of the file. -
Create a new file in the
screens
folder namedlogin.dart
. -
Fill the
login.dart
file with the following code.import 'package:mental_health_tracker/screens/menu.dart';
import 'package:flutter/material.dart';
import 'package:pbp_django_auth/pbp_django_auth.dart';
import 'package:provider/provider.dart';
// TODO: Import RegisterPage later
void main() {
runApp(const LoginApp());
}
class LoginApp extends StatelessWidget {
const LoginApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Login',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.deepPurple,
).copyWith(secondary: Colors.deepPurple[400]),
),
home: const LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
Widget build(BuildContext context) {
final request = context.watch<CookieRequest>();
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Login',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 30.0),
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
hintText: 'Enter your username',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
contentPadding:
EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
),
),
const SizedBox(height: 12.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
contentPadding:
EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
),
obscureText: true,
),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: () async {
String username = _usernameController.text;
String password = _passwordController.text;
// Check credentials
// TODO: Change the URL and don't forget to add a trailing slash (/) at the end of the URL!
// To connect the Android emulator to Django on localhost,
// use the URL http://10.0.2.2/
final response = await request
.login("http://[YOUR_APP_URL]/auth/login/", {
'username': username,
'password': password,
});
if (request.loggedIn) {
String message = response['message'];
String uname = response['username'];
if (context.mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => MyHomePage()),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content:
Text("$message Welcome, $uname.")),
);
}
} else {
if (context.mounted) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Login Failed'),
content: Text(response['message']),
actions: [
TextButton(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
);
}
}
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
minimumSize: Size(double.infinity, 50),
backgroundColor: Theme.of(context).colorScheme.primary,
padding: const EdgeInsets.symmetric(vertical: 16.0),
),
child: const Text('Login'),
),
const SizedBox(height: 36.0),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RegisterPage()),
);
},
child: Text(
'Don\'t have an account? Register',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: 16.0,
),
),
),
],
),
),
),
),
),
);
}
} -
In the
main.dart
file, in theMaterialApp(...)
widget, changehome: MyHomePage()
tohome: LoginPage()
. -
In this step, you will implement a register function in your project. Before that, modify the
authentication
module on your Django project that you have made. Add another method in your views inauthentication/views.py
that you have already made.
from django.contrib.auth.models import User
import json
...
@csrf_exempt
def register(request):
if request.method == 'POST':
data = json.loads(request.body)
username = data['username']
password1 = data['password1']
password2 = data['password2']
# Check if the passwords match
if password1 != password2:
return JsonResponse({
"status": False,
"message": "Passwords do not match."
}, status=400)
# Check if the username is already taken
if User.objects.filter(username=username).exists():
return JsonResponse({
"status": False,
"message": "Username already exists."
}, status=400)
# Create the new user
user = User.objects.create_user(username=username, password=password1)
user.save()
return JsonResponse({
"username": user.username,
"status": 'success',
"message": "User created successfully!"
}, status=200)
else:
return JsonResponse({
"status": False,
"message": "Invalid request method."
}, status=400)
- Add another path in
authentication/urls.py
, modify it as follows
from authentication.views import login, register # add "register" to this line
...
path('register/', register, name='register'),
-
In your Flutter project, create another file in
screens
folder with the nameregister.dart
. -
Fill the file
register.dart
as follows.import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:mental_health_tracker/screens/login.dart';
import 'package:pbp_django_auth/pbp_django_auth.dart';
import 'package:provider/provider.dart';
class RegisterPage extends StatefulWidget {
const RegisterPage({super.key});
State<RegisterPage> createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
Widget build(BuildContext context) {
final request = context.watch<CookieRequest>();
return Scaffold(
appBar: AppBar(
title: const Text('Register'),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text(
'Register',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 30.0),
TextFormField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
hintText: 'Enter your username',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
contentPadding:
EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your username';
}
return null;
},
),
const SizedBox(height: 12.0),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
contentPadding:
EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
return null;
},
),
const SizedBox(height: 12.0),
TextFormField(
controller: _confirmPasswordController,
decoration: const InputDecoration(
labelText: 'Confirm Password',
hintText: 'Confirm your password',
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
contentPadding:
EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please confirm your password';
}
return null;
},
),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: () async {
String username = _usernameController.text;
String password1 = _passwordController.text;
String password2 = _confirmPasswordController.text;
// Check credentials
// TODO: Change the url, don't forget to add a slash (/) inthe end of the URL!
// To connect Android emulator with Django on localhost,
// use the URL http://10.0.2.2/
final response = await request.postJson(
"http://[YOUR_APP_URL]/auth/register/",
jsonEncode({
"username": username,
"password1": password1,
"password2": password2,
}));
if (context.mounted) {
if (response['status'] == 'success') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Successfully registered!'),
),
);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const LoginPage()),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Failed to register!'),
),
);
}
}
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
minimumSize: Size(double.infinity, 50),
backgroundColor: Theme.of(context).colorScheme.primary,
padding: const EdgeInsets.symmetric(vertical: 16.0),
),
child: const Text('Register'),
),
],
),
),
),
),
),
);
}
} -
Run your Flutter application and try to login.
Custom Model Creation
In creating a model that adapts to JSON data, we can use the Quicktype website with the following steps.
-
Open the
JSON
endpoint you created earlier in tutorial 2. -
Copy the
JSON
data and open the Quicktype website. -
On the Quicktype website, change the setup name to
MoodEntry
, source type toJSON
, and language toDart
. -
Paste the previously copied
JSON
data into the textbox textbox provided on Quicktype. -
Click the
Copy Code
on Quicktype.Here is an example result.
After obtaining the model code through Quicktype, open the Flutter project again, create a new file under the models/
folder in the subdirectory lib/
with the name mood_entry.dart
, and paste the copied code from Quicktype.
Fetch Data from Django and Show Data in the Flutter App
Add HTTP Dependency
To create HTTP requests, we need the http package.
-
Run
flutter pub add http
in your Flutter project terminal to add thehttp
package. -
In the file
android/app/src/main/AndroidManifest.xml
, add this code to allow your Flutter app to access the internet....
<application>
...
</application>
<!-- Required to fetch data from the Internet. -->
<uses-permission android:name="android.permission.INTERNET" />
...
Fetch Data from Django
-
Create a new file in the
lib/screens
directory with namelist_moodentry.dart
. -
In the file
list_moodentry.dart
, import the necessary libraries. Change the [APP_NAME] to your Flutter project app name.import 'package:flutter/material.dart';
import 'package:[APP_NAME]/models/mood_entry.dart';
... -
Copy and paste these lines of code
list_moodentry.dart
. Do not forget to import the necessary files....
import 'package:[APP_NAME]/widgets/left_drawer.dart';
class MoodEntryPage extends StatefulWidget {
const MoodEntryPage({super.key});
State<MoodEntryPage> createState() => _MoodEntryPageState();
}
class _MoodEntryPageState extends State<MoodEntryPage> {
Future<List<MoodEntry>> fetchMood(CookieRequest request) async {
// TODO: Don't forget to add the trailing slash (/) at the end of the URL!
final response = await request.get('http://[YOUR_APP_URL]/json/');
// Decoding the response into JSON
var data = response;
// Convert json data to a MoodEntry object
List<MoodEntry> listMood = [];
for (var d in data) {
if (d != null) {
listMood.add(MoodEntry.fromJson(d));
}
}
return listMood;
}
Widget build(BuildContext context) {
final request = context.watch<CookieRequest>();
return Scaffold(
appBar: AppBar(
title: const Text('Mood Entry List'),
),
drawer: const LeftDrawer(),
body: FutureBuilder(
future: fetchMood(request),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(child: CircularProgressIndicator());
} else {
if (!snapshot.hasData) {
return const Column(
children: [
Text(
'There is no mood data in mental health tracker.',
style: TextStyle(fontSize: 20, color: Color(0xff59A5D8)),
),
SizedBox(height: 8),
],
);
} else {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (_, index) => Container(
margin:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${snapshot.data![index].fields.mood}",
style: const TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text("${snapshot.data![index].fields.feelings}"),
const SizedBox(height: 10),
Text("${snapshot.data![index].fields.moodIntensity}"),
const SizedBox(height: 10),
Text("${snapshot.data![index].fields.time}")
],
),
),
);
}
}
},
),
);
}
} -
Add the page
list_moodentry.dart
towidgets/left_drawer.dart
with this code.// ListTile Menu code
...
ListTile(
leading: const Icon(Icons.add_reaction_rounded),
title: const Text('Mood List'),
onTap: () {
// Route to the mood page
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const MoodEntryPage()),
);
},
),
... -
Change the function of the
Lihat Mood
button in the main page to redirect the page toMoodPage
. You can add the redirection by addingelse if
after theif(...){...}
code at the end of theonTap: () { }
code in thewidgets/mood_card.dart
file....
else if (item.name == "View Mood") {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => const MoodEntryPage()
),
);
}
... -
Import the necessary files while adding the
MoodEntryPage
to theleft_drawer.dart
andtracker_card.dart
. -
Run the app and try to add some
MoodEntry
in your website. Check the result in the newDaftar Mood
page in Flutter.
Integrate Flutter Form with the Django Service
Do these steps in your Django project.
-
Create a new views function in the
main/views.py
in your Django project with this code. Don;t forget to add the necessary imports.from django.views.decorators.csrf import csrf_exempt
import json
from django.http import JsonResponse
...
@csrf_exempt
def create_mood_flutter(request):
if request.method == 'POST':
data = json.loads(request.body)
new_mood = MoodEntry.objects.create(
user=request.user,
mood=data["mood"],
mood_intensity=int(data["mood_intensity"]),
feelings=data["feelings"]
)
new_mood.save()
return JsonResponse({"status": "success"}, status=200)
else:
return JsonResponse({"status": "error"}, status=401) -
Add new path in the
main/urls.py
file with this code.path('create-flutter/', create_mood_flutter, name='create_mood_flutter'),
-
Rerun (and redeploy) your app. If you have deployed your app before, any data and transactions will be lost after the redeployment.
Do these steps in your Flutter project.
-
Connect the page
moodentry_form.dart
toCookieRequest
by adding this code....
Widget build(BuildContext context) {
final request = context.watch<CookieRequest>();
return Scaffold(
... -
change the
onPressed: ()
button instructions as follows....
onPressed: () async {
if (_formKey.currentState!.validate()) {
// Send request to Django and wait for the response
// TODO: Change the URL to your Django app's URL. Don't forget to add the trailing slash (/) if needed.
final response = await request.postJson(
"http://[YOUR_APP_URL]/create-flutter/",
jsonEncode(<String, String>{
'mood': _mood,
'mood_intensity': _moodIntensity.toString(),
'feelings': _feelings,
// TODO: Adjust the fields with your project
}),
);
if (context.mounted) {
if (response['status'] == 'success') {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(
content: Text("New mood has saved successfully!"),
));
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => MyHomePage()),
);
} else {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(
content:
Text("Something went wrong, please try again."),
));
}
}
}
},
... -
Do some quick fixes by importing the necessary files.
-
Rerun your app and try add new transactions from your Flutter app.
Logout Feature Implementation
Do these steps in your Django project.
-
Add a new views method for logout in the
authentication/views.py
.from django.contrib.auth import logout as auth_logout
...
@csrf_exempt
def logout(request):
username = request.user.username
try:
auth_logout(request)
return JsonResponse({
"username": username,
"status": True,
"message": "Logged out successfully!"
}, status=200)
except:
return JsonResponse({
"status": False,
"message": "Logout failed."
}, status=401) -
Add a new path in the
authentication/urls.py
file.from authentication.views import logout
...
path('logout/', logout, name='logout'),
Do these steps in the Flutter project.
-
Add this code to the
lib/widgets/mood_card.dart
file. Do not forget to resolve the import issues. after you add the follong lines of code....
Widget build(BuildContext context) {
final request = context.watch<CookieRequest>();
return Material(
... -
Change the
onTap: () {...}
for the widgetInkwell
toonTap: () async {...}
This allows the widgetInkwell
do the logout process asynchronously. -
Add the following code into the
async {...}
in the last part:...
// previous if statement
// add the else if statement below
else if (item.name == "Logout") {
final response = await request.logout(
// TODO: Change the URL to your Django app's URL. Don't forget to add the trailing slash (/) if needed.
"http://[YOUR_APP_URL]/auth/logout/");
String message = response["message"];
if (context.mounted) {
if (response['status']) {
String uname = response["username"];
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("$message Goodbye, $uname."),
));
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const LoginPage()),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
),
);
}
}
}
... -
Rerun the app and try to logout.
Closing
Congratulations! You have completed the Tutorial 8! Hopefully with this tutorial, you can understand more about models, data fetching, basic state management, and Django-Flutter integration.😄
- Read and understand the codes we listed above again. Don't forget to complete the TODO!
Don't forget to run flutter analyze
to see if there are any optimizations that can be done.
-
Run the following commands to
add
,commit
, andpush
:git add .
git commit -m "<commit_message>"
git push -u origin <main_branch>- Replace
<commit_message>
with your desired message. For example:git commit -m "tutorial 8 selesai"
. - Replace
<main_branch>
with your main branch name. For example:git push -u origin main
orgit push -u origin master
.
- Replace
Additional References
- Fetch Data From the Internet
- How to create models in Flutter Dart
- Simple app state management | Flutter
- Flutter State Management with Provider
- Pengenalan State Management Flutter dan Jenis-jenisnya
Contributors
- Abbilhaidar Farras Zulfikar
- Clarence Grady
- Fernando Valentino Sitinjak
- Reyhan Zada Virgiwibowo
- Muhammad Nabil Mu'afa & Alden Luthfi (EN Translation)
Credits
This tutorial was developed based on PBP Genap 2024 written by the 2023 Platform-Based Programming Teaching Team. All tutorials and instructions included in this repository are designed so that students who are taking Platform-Based Programming courses can complete the tutorials during lab sessions.