Mind Pal Project Documentation by Portfolio-mobile-app-team1

Mind Pal Project Documentation by Portfolio-mobile-app-team1

·

8 min read

The project topic is to build a task management (to-do) app using APIs and designs provided by another team.

Project Phases

Phase 1: Initiation and Planning

Planning is an important but often overlooked stage in software development. It sets the tone for how everything will go, who is responsible for what and what targets are to be met.

The team had a meeting in the first day the tasks dropped so it would still be fresh on our minds. I initiated the meeting in the evening with a few of the members present. We were to collaborate with a product design team and a backend team so a lot of decisions weren't ours to make, such as the name and tagname of the app which was done by the design team. And what language the custom API was to be written in, which was the responsibility of the backend team. Having little to no say on the aesthetics or background workings, we focused our efforts on understanding our role in the overall project, came up with a basic app structure of how the app would be built and how it all connected to each other. We delegated responsibilities to members and set up a good communication channel for easy collaboration.

The design team came up with the app name Mind Pal and the tagline "sit back, stay on track", while the backend team decided to use Nodejs in developing the API.

Phase 2: Launch and Execution

Setting up Flutter Project

  • Created new Flutter project flutter create mind_pal.

  • Change the application name and launcher icon For Android, we navigated to android/app/src/main/AndroidManifest.xml and the changed the android: label property of <application>.

<application
        android:name="io.flutter.app.FlutterApplication"
        android:label="Mind Pal"
        android:icon="@mipmap/ic_launcher">

For ios, we navigated to ios/Runner/Info.plist and changed the CFBundleName.

<key>CFBundleName</key>
<string>your_app_name</string>

For the launcher icon, there is a package called Flutter Launcher Icons on pub.dev. We added it to the dependencies in the pubspec.yaml file using flutter_launcher_icons: ^0.9.3 and integrated it in the same pubspec.yaml file.

flutter_icons:
  android: true
  ios: true
  image_path: "assets/images/app_logo.png"
  • Added necessary dependencies to the pubspec.yaml file which included Shared preferences, Google fonts, Smooth page indicator and Percent Indicator.
smooth_page_indicator: ^1.0.0+1
shared_preferences: ^2.0.13
google_fonts: ^2.1.0
percent_indicator: ^4.2.2
  • Added custom images and icons to an assets folder then enabled the folder in the pubspec.yaml file.
assets:
    - assets/images/
    - assets/icons/

Coding the Application

Built the Splash Screen

The Splash Screen displays the Application Logo. This was done by navigating to the next screen after a time delay.

Timer(const Duration(seconds: 3), () {
      Navigator.pushReplacement(
          context, MaterialPageRoute(builder: (context) => const HomeScreen()));

splash.png

Built the Onboarding Screens

The onboarding screen gives a brief introduction of the app to new users. We implemented this by creating a custom widget and using it in the PageView widget. Seamless transition was made possible using the SmoothPageIndicator widget.

ContentTile(
  title: "Schedule Tasks",
  image: "assets/images/onboard1.png",
  subtitle: "Easily schedule tasks and to-dos ranging from everyday activities to occassional events.")

onboarding.png

Built the Authentication Screens

We implemented the frontend of the authentication screens as you would any regular screen. Creating custom widgets for the widgets we use often such as buttons, text form fields, containers etc. The authentication screens also send user details to the backend via REST APIs given to us by the backend team. Text form fields were subjected to form validations to prevent users from giving badly formatted inputs such as emails without domains, or passwords that are too simple.

login.png

Built the main screens using the basic application structure as a guide

The app has 3 main features, each of them with their corresponding API endpoints. These give the user the ability to;

  • Create a new task/to-do list.
  • Edit an existing task.
  • Delete a task after it's completion.

There's not much to say here, building these screens was similar to the authentication screens just with a lot more screens and logic needed.

wwyltd.png

Integrating the APIs into the UI

There are a few steps that we can follow to easily integrate an API into our Flutter app: Integrating an API into your flutter code generally follows 6 easy steps.

  • Get the API URL and endpoints.

The URL used when working with an API is usually split into two parts, the base URL and the endpoint. The base URL is always the same while the endpoint is different depending on the function it is used for. Many API providers require you to obtain a private key, majorly for security purposes. This API key will be appended to the base URL making the new base URL = oldBaseUrl/apiKey. We were provided with a custom URL without a key so we only made use of the base URL we were given.

  • Add the required packages to your pubspec.yaml file.

There are a few packages available on pub.dev that can be used to integrate APIs in Flutter. The most common ones being http, dio and chopper. This can be done easily by adding it under the dependencies.

dependencies:
  http: ^0.13.4
  • Create a file that stores the base URLs and endpoints.

We created a file named apiURLs.dart to hold all the URLs and endpoints we would be using. This makes your code look neater and is generally good practice not to litter your main code with API URLs.

class ApiURLs {
  final String _url = "of course I won't show you";
  final String _apiUrl = "/our endPoint";
  var fullUrl = _url + _apiUrl;
  }
  • Create a model class to parse the JSON.

Here we create a model class, parse the JSON and get an object out of that JSON response. We would need the entire JSON response we want to get from the API in order to create a model class. Since this is a custom API from our backend, we had this available to us in the documentation they provided us. For other publics APIs, you can use Postman to hit the API and get the response, while for private APIs the documentation should be provided to you as well. For a stress free way of creating a model class, search for any JSON to dart converter on the internet and paste in the full JSON response, generate and your model class, change the name of the class and paste it back into your model class file.

class TaskModel {
  int? id;
  int? userId;
  String? todo;
  int? status;
  String? createdOn;

  TaskModel({this.id, this.userId, this.todo, this.status, this.createdOn});
  TaskModel.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    userId = json['user_id'];
    todo = json['todo'];
    status = json['status'];
    createdOn = json['created_on'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['user_id'] = this.userId;
    data['todo'] = this.todo;
    data['status'] = this.status;
    data['created_on'] = this.createdOn;
    return data;
  }
}
  • Create a file to handle API calls and create methods to fetch and parse data.

We created a file called database_service.dart to handle the API calls.

import 'dart:convert';
import 'package:http/http.dart' as http;

class TaskApi {
  postTask(data, apiUrl) async {
    var fullUrl = ApiURLs.fullUrl;
    return await http.post(Uri.parse(fullUrl),
        body: jsonEncode(data),
        headers: {
          'Content-type': 'application/json',
          'Accept': 'application/json'
        });
  }
}
  • Display the data on your app.

After creating all the required files and methods, we then called for data from the backend and loaded the data into text fields, containers, etc. Then we call the method insides buttons and icons that load the results on the screen for users to see.

void _getData() async {
    _taskModel = (await ApiService().getTasks())!;
    Future.delayed(const Duration(seconds: 1)).then((value) => setState(() {}));
  }
_createTask() async {
    var data = {
      "user_id": "4",
      'todo': taskController.text,
    };

    var res = await TaskApi().postTask(data, 'task');
    if (res.statusCode == 200) {
      Navigator.push(
          context, MaterialPageRoute(builder: (context) => SuccessScreen()));
    } else {
      SnackBar(content: Text('Uh oh... Something went wrong.'));
    }
  }

TextButton(
    onPressed: () => _createTask(),
    child: Text("Save"),

Phase 3: Closure, Operation and Efficiency

Contrary to what the writeup would have you believe, working together with other teams isn't as smooth as we usually want it to be. We received the APIs from the backend really late and the last batch of designs for the screens even later. This is still no excuse not to go ahead and okay our part in the development the app, it only meant we would not have enough time to implement a few extra features we would have loved to. As this is not the full extent of the app's capabilities, be sure to expect a follow-up post to this one detailing some extra features that makes our task management unique from the other variants out there.

What we do have for you though is an app that authenticates it's users, as we recognize the need to keep some of your goals private, the authentication process is very quick and not information is needed. Then we come to the home screen which shows you the ongoing tasks as well an a linear progress indicator that keeps track of how many tasks you have that day and how close you are to completing them. From the home screen, you can navigate directly to a page that lets you add tasks. This page is also reachable from a few other screens.

create task.png

The home screen also has a menu button which navigates to the menu screen. This provides four buttons representing different things you can do with the app. One of which leads to a page where you can view all tasks that are are still pending. You can also filter the tasks shown if you are looking for something particular.

view all tasks.png

From this page, you can navigate to another page where you can edit the content of the task, you can also change the time or any other property of the task. And lastly, you can delete the task altogether.

edit task.png

Conclusion

Although firebase is the rave when it comes to authentication for beginners to flutter. We hope this article helps you get started with implementing authentication using custom APIs and other backend functions like storing and retrieving data. We also hope you experienced what it feels like working with in a team and collaborating with other teams, sometimes it's a joy, most times it's a hassle, but the product comes first and above all teamwork must prevail.