0
votes

So I am trying to connect my Django rest framework backend with my Flutter UI and I'm having some issues. I currently only have a register, login and logout links that are fully function on the backend, I tested them in postman and they work. But when I try to connect to them through my Flutter app I get a 404 error saying that it couldn't find the API link. I can't seem to figure out what the problem is and I've been trying at it for a couple of days now lol any help is help.

Here is my code:


DRF Backend:


views.py:

from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated, AllowAny
from .serializers import RegistrationSerializer
from rest_framework.authtoken.models import Token

@api_view(['POST'])
@permission_classes([AllowAny])
def registration_view(request):
    print("got here first")

    if request.method == 'POST':
        print("got here")
        serializer = RegistrationSerializer(data=request.data)
        data = {}
        if serializer.is_valid():
            print("got valid")
            user = serializer.save()
            data['response'] = "succesfully registered a new user."
            data['email'] = user.email
            data['username'] = user.username
            token = Token.objects.get(user=user).key
            data['token'] = token
        else:
            data = serializer.errors
        return Response(data)

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def logout_view(request):
    cToken = Token.objects.get(user=request.user)
    cToken.delete()
    return Response("Succesfully logged out")

urls.py:

from django.urls import path
from .views import registration_view, logout_view
from rest_framework.authtoken.views import obtain_auth_token
# from rest_framework.permissions import IsAuthenticated


app_name = "backApp"

urlpatterns = [
    path('register', registration_view, name="register"),
    path('login', obtain_auth_token, name="login"),
    path('logout', logout_view, name="logout")
]

serializers.py:

from rest_framework import serializers

from backApp.models import User

class RegistrationSerializer(serializers.ModelSerializer):

    password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)

    class Meta:
        model = User
        fields = ['email', 'username', 'password', 'password2']
        extra_kwargs = {
            'password': {'write_only': True}
        }

    def save(self):
        user = User(
            email=self.validated_data['email'],
            username=self.validated_data['username'],
        )
        password = self.validated_data['password']
        password2 = self.validated_data['password2']

        if password != password2:
            raise serializers.ValidationError({'password': 'Passwords must match.'})
        user.set_password(password)
        user.save()
        return user

models.py:

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

class MyUserManager(BaseUserManager):
    def create_user(self, email, username, first_name, last_name,password=None):
        if not email:
            raise ValueError("Users must have an email address.")
        if not username:
            raise ValueError("Users must have a username.")
        if not first_name:
            raise ValueError("Users must have a first name.")
        if not last_name:
            raise ValueError("Users must have a last name.")

        user = self.model(
            email=self.normalize_email(email),
            username=username,
            first_name=first_name,
            last_name=last_name
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, username, password, first_name, last_name):
        user = self.create_user(
            email=self.normalize_email(email),
            username=username,
            password=password,
            first_name=first_name,
            last_name=last_name
        )
        user.is_admin = True
        user.is_staff = True
        user.is_superuser = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser):
    email = models.EmailField(verbose_name="email", max_length=80, unique=True)
    first_name = models.CharField(verbose_name="first name", max_length=80)
    last_name = models.CharField(verbose_name="last name", max_length=80)
    tap_coins = models.IntegerField(default=0)
    username = models.CharField(max_length=80, unique=True)
    date_joined = models.DateTimeField(verbose_name="date joined", auto_now_add=True)
    last_login = models.DateTimeField(verbose_name="last login", auto_now=True)
    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username", "first_name", "last_name"]

    objects = MyUserManager()

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True

@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)

Flutter:


main.dart:

import 'package:TapCoinsApp/api/api.dart';
import 'package:TapCoinsApp/screens/profile.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './screens/register.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => UserProvider()),
        // ChangeNotifierProvider(create: (context) => BankProvider())
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    final userP = Provider.of<UserProvider>(context);
    // final bankP = Provider.of<BankProvider>(context);
    int prizeCoins = 0;
    int coinsTapped = 0;
    return MaterialApp(
      home: SafeArea(
        child: DefaultTabController(
          length: 2,
          child: Scaffold(
            appBar: TabBar(
              tabs: [
                Tab(icon: new Icon(Icons.home)),
                Tab(icon: new Icon(Icons.person_outline_rounded)),
              ],
              labelColor: Colors.red,
              unselectedLabelColor: Colors.yellow,
              indicatorSize: TabBarIndicatorSize.label,
              indicatorPadding: EdgeInsets.all(5.0),
              indicatorColor: Colors.red,
            ),
            backgroundColor: Colors.purple,
            body: Stack(
              children: <Widget>[
                TabBarView(children: [
                  new Scaffold(
                    backgroundColor: Colors.purple,
                    floatingActionButton: FloatingActionButton(
                      onPressed: () {
                        Navigator.of(context).push(MaterialPageRoute(
                            builder: (ctx) => RegisterUser()));
                      },
                    ),
                  ),
                  ProfilePage(),
                ]),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

register.dart:

import 'package:TapCoinsApp/api/api.dart';
import 'package:TapCoinsApp/models/user.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class RegisterUser extends StatefulWidget {
  @override
  _RegisterUserState createState() => _RegisterUserState();
}

class _RegisterUserState extends State<RegisterUser> {
  final userFirstNameController = TextEditingController();

  final userLastNameController = TextEditingController();

  final userEmailController = TextEditingController();

  final userUserNameController = TextEditingController();

  final userPasswordController = TextEditingController();

  final userPassword2Controller = TextEditingController();

  void _onRegister() {
    final String firstVal = userFirstNameController.text;
    final String lastVal = userLastNameController.text;
    final String emailVal = userEmailController.text;
    final String userVal = userUserNameController.text;
    final String passwordVal = userPasswordController.text;
    final String password2Val = userPassword2Controller.text;

    if (firstVal.isNotEmpty &&
        lastVal.isNotEmpty &&
        emailVal.isNotEmpty &&
        userVal.isNotEmpty &&
        passwordVal.isNotEmpty &&
        password2Val.isNotEmpty) {
      final User user = User(
          firstName: firstVal,
          lastName: lastVal,
          email: emailVal,
          userName: userVal,
          password: passwordVal,
          password2: password2Val);
      print(user);
      Provider.of<UserProvider>(context, listen: false).registerUser(user);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("Register!")),
        body: ListView(
          children: [
            Container(
                child: Column(
              children: [
                TextField(
                  controller: userFirstNameController,
                  decoration: InputDecoration(labelText: "First Name: "),
                ),
                TextField(
                    controller: userLastNameController,
                    decoration: InputDecoration(labelText: "Last Name: ")),
                TextField(
                    controller: userEmailController,
                    decoration: InputDecoration(labelText: "Email: ")),
                TextField(
                    controller: userUserNameController,
                    decoration: InputDecoration(labelText: "Username: ")),
                TextField(
                    controller: userPasswordController,
                    decoration: InputDecoration(labelText: "Password: ")),
                TextField(
                    controller: userPassword2Controller,
                    decoration:
                        InputDecoration(labelText: "Confirm Password: ")),
                MaterialButton(
                  child: Text("Register"),
                  onPressed: _onRegister,
                )
              ],
            ))
          ],
        ));
  }
}

User.dart:

class User {
  int id;
  final String firstName;
  final String lastName;
  final String userName;
  final String email;
  final String password;
  final String password2;
  int tapCoins;

  User(
      {this.id,
      this.firstName,
      this.lastName,
      this.email,
      this.password,
      this.password2,
      this.userName,
      this.tapCoins});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
        id: json['id'],
        firstName: json['first_name'],
        lastName: json['last_name'],
        userName: json['username'],
        email: json['email'],
        password: json['password'],
        tapCoins: json['tapCoins']);
  }

  dynamic toJson() => {
        'id': id,
        'first_name': firstName,
        'last_name': lastName,
        'username': userName,
        'email': email,
        'password': password,
        'password2': password2,
        'tapCoins': tapCoins
      };
}

api.dart:

import 'dart:convert';
import 'package:flutter/material.dart';
import '../models/user.dart';
// import '../models/bank.dart';
import 'package:http/http.dart' as http;

class UserProvider with ChangeNotifier {
  var resHere;

  void registerUser(User user) async {
    print("got here");
    final response = await http.post('http://10.0.2.2:8000/api/user/register/',
        headers: {"Content-Type": "application/json"}, body: json.encode(user));

    print("got response");
    print(response.statusCode);
    print(response.body);

    if (response.statusCode == 201) {
      // user.id = json.decode(response.body)['id'];
      resHere = json.decode(response.body);
      print(resHere);
    }
  }
}

I'm also getting the error back through the response.body as the html error message that says page not found and the response.statusCode is 404.

2

2 Answers

0
votes

I think that you have tested the API on your personal machine. You have to publish your API and database on some online server or web, so you can access it in the mobile app.

0
votes
  • You need to find the IP address of your personal machine.

  • Use ifconfig to get the IP address.

  • Once you have the IP in hand(***.***.***.***), you need to change a line in settings.py. Replace ALLOWED_HOSTS inside the Django settings.py file with this.

    ALLOWED_HOSTS = ['***.***.***.***', 'localhost', '127.0.0.1']
    
  • Now turn off the Django server, and restart it with the following command.

    python manage.py runserver 0.0.0.0:8000
    
  • This will allow you to access your Django API in the following URL

    http://***.***.***.***:8000/api/user/register/