DEV Community

Cover image for Adding a GitHub login to your Flutter app with Appwrite
Femi-ige Muyiwa for Hackmamba

Posted on • Originally published at hackmamba.io

9

Adding a GitHub login to your Flutter app with Appwrite

As the most popular version control tool, GitHub is not new for developers. Besides version control, GitHub enables other features, such as Open Authorization (OAuth). OAuth is an open standard for access delegation that allows users to grant third-party application access to their resources without sharing their login credentials. It is commonly used for users to log in to third-party applications using their existing credentials from another provider.

Appwrite is a platform that helps developers easily integrate OAuth authentication into their applications. It allows developers to authenticate users and access various services, such as databases or cloud storage, without implementing the OAuth protocol.

This tutorial elaborates on how to add a GitHub login to our Flutter application using Appwrite. After successfully authenticating user credentials from GitHub, we’ll achieve this helpful feature by building a login interface that routes users to the homepage.

Prerequisites

The following are required to follow along with this tutorial:

  • Xcode (with developer account for Mac users).
  • To run the application, use iOS Simulator, Android Studio, or Chrome web browsers.
  • An Appwrite instance running on either Docker, DigitalOcean droplet, or Gitpod. Check out this article for the setup
  • Create a GitHub account.

Setting up the Appwrite project

After setting up an Appwrite instance, head to your browser and type in the IP address or hostname. Next, select Create Project and fill in the desired project name and ID.

Note: ID can be auto-generated
Create-project 1
Create-project 2

After creating a project, we’ll scroll down within the Appwrite console and select create platform. When a popup appears, select Flutter and choose the operating system you plan to work on (in this case, choose Android).

Next, specify the application and package names (the package name is in the app-level build.gradle file).

Create-platform 1
Create-platform 2

Getting started

This section demonstrates how to enable GitHub OAuth on Appwrite and create an OAuth App on GitHub.

Enabling GitHub OAuth on Appwrite
Let’s start by heading to the Auth section, then settings. We will then select GitHub, enable it, and copy the callback URL, which will come in handy when creating our OAuth app.

enabling OAuth 1
enabling OAuth 2

Creating a GitHub OAuth app
After completing the signup process, log in to your GitHub account. Then, in the home section, select the profile icon and head to the settings page.

enabling OAuth 3

Next, select Developer settings on the settings page and head to OAuth Apps in the Developer settings area. We will then click on the New OAuth Apps button and fill in the following information:

  • Application name
  • Homepage URL
  • Callback URL from Appwrite

enabling OAuth 4
enabling OAuth 5
enabling OAuth 6

Creating a Flutter App

To create a new Flutter project, we will need to create a new directory. Do so by running the command below in a terminal:

mkdir <directory name>
Enter fullscreen mode Exit fullscreen mode

Next, we’ll change the directory using the following command:

cd <directory name>
Enter fullscreen mode Exit fullscreen mode

After that, let’s proceed to creating a new Flutter application, either in the directory we created or a sub-directory within the main directory:

# within the directory
flutter create .

# sub-directory
flutter create <new-directory-name>
Enter fullscreen mode Exit fullscreen mode

Note: replace and with your chosen directory names

After creating the Flutter application, we’ll run it on our simulator or emulator using the command

flutter run
Enter fullscreen mode Exit fullscreen mode

Our default Flutter homepage should now look like the image below:

homepage

Next, we will need to install the Appwrite and Provider package into our app. We can do this by adding them to the dependencies section of our pubspec.yaml file, like the image below:

pubspec.yaml

Also, we can use the terminal by typing the command below:

flutter pub add appwrite

# and

flutter pub add Provider
Enter fullscreen mode Exit fullscreen mode

To use our Appwrite instance within our Flutter app, we need to connect our project to Appwrite.

iOS
First, obtain the bundle ID by going to the project.pbxproj file (ios > Runner.xcodeproj > project.pbxproj) and searching for the PRODUCT_BUNDLE_IDENTIFIER.

Now, head to the Runner.xcworkspace folder in the applications iOS folder in the project directory on Xcode. To select the runner target, choose the Runner project in the Xcode project navigator and find the Runner target. Then, select General and IOS 11.0 in the deployment info section as the target.

Android
For Android, copy the XML script below and paste it below the activity tag in the Androidmanifest.xml file (to find this file, head to android > app > src > main).

<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity" android:exported="true">
<intent-filter android:label="flutter_web_auth_2">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="appwrite-callback-[PROJECT-ID]" />
</intent-filter>
</activity>

Note: change [PROJECT-ID] to the ID you used when creating the Appwrite project.

Creating the component and functionality
Let’s now move on to creating the component and functionality. Create another file called app_constants.dart within the lib folder of the Flutter project. This folder will store some constants from Appwrite, such as endpoint and projectID within a class.

class Appconstants{
  static const String endpoint = "endpoint or localhost";
  static const String projectId = "Project ID";
}
Enter fullscreen mode Exit fullscreen mode

Next, we will need an interface and a button that triggers the OAuth session. To do that, replace the default Flutter code with the code below:

import 'package:flutter/material.dart';
import 'package:appwrite/appwrite.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:webappgit/app_constants.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.grey,
),
initialRoute: '/',
routes: {
'/': (context) => const MyLoginPage(title: 'Login'),
'/home': (context) => const MyHomePage(title: 'Home'),
},
// home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyLoginPage extends StatefulWidget {
const MyLoginPage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyLoginPage> createState() => _MyLoginPageState();
}
class _MyLoginPageState extends State<MyLoginPage> {
// functionality
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _buildBody(),
);
}
Widget _buildBody() {
return Center(
child: _buildStack(),
);
}
Widget _buildStack() {
return Stack(
alignment: Alignment.center,
children: [
_buildIntrinsicHeightContainer(),
],
);
}
Widget _buildIntrinsicHeightContainer() {
return IntrinsicHeight(
child: _buildContainer(),
);
}
Widget _buildContainer() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[600],
borderRadius: BorderRadius.circular(40),
boxShadow: [
BoxShadow(
blurRadius: 20,
offset: const Offset(0, 10),
color: Colors.black.withOpacity(.12),
)
],
),
child: _buildColumn(),
);
}
Widget _buildColumn() {
return Column(
children: [
_buildIconContainer(),
_buildSizedBox(),
_buildSignInButton(),
],
);
}
Widget _buildIconContainer() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
boxShadow: [
BoxShadow(
blurRadius: 10,
offset: const Offset(0, 10),
color: Colors.black.withOpacity(.12),
)
],
),
child: const Icon(
FontAwesomeIcons.github,
size: 300,
color: Colors.black,
),
);
}
Widget _buildSizedBox() {
return const SizedBox(
height: 20,
);
}
Widget _buildSignInButton() {
return ElevatedButton(
onPressed: _githubLogin,
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith((states) => Colors.black),
minimumSize:
MaterialStateProperty.resolveWith((states) => const Size(88, 36)),
shape: MaterialStateProperty.resolveWith((states) =>
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
),
child: const Text("Sign in with Github",
style: TextStyle(color: Colors.white)),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
);
}
}
view raw main.dart hosted with ❤ by GitHub

The code above is a StatelessWidget that creates a MaterialApp with a title, routes, and a theme. The initial route is the login page, represented by the MyLoginPage widget, which is a StatefulWidget.

The MyLoginPage will contain a centered container housing a Stack widget. The stack includes an IntrinsicHeight widget, a container widget that ensures its child has the minimum size to display its content. The child of the IntrinsicHeight widget is a Container widget with a column inside it. The column contains an icon container, a sized box, and a sign-in button.

Let’s link the sign-in button’s onPressed property to a function called _githubLogin. This function uses the createOAuth2Session method from Appwrite. Thus, we will need to initialize our Appwrite client first. Start by calling your Appwrite Client and Account API, and then move to create an initState function. In the initState function, we will initialize our Appwrite APIs.

late Client _client;
late Account _account;

@override
void initState() {
  super.initState();
  _initializeAppwrite();
}

void _initializeAppwrite() {
  _client = Client()
      .setEndpoint(Appconstants.endpoint)
      .setProject(Appconstants.projectId);
  _account = Account(_client);
}
Enter fullscreen mode Exit fullscreen mode

Next, we will create the _githubLogin function, and within it, we will call the createOAuth2Session method with the parameter provider. We will also call a new function called checklogin. This function will use the getSession method from the Account API to check for the current session. If it returns true, it will navigate us to the home page.

void _githubLogin() async {
  try {
    await _account.createOAuth2Session(
      provider: 'github',
    );
    checklogin();
  } catch (e) {
    rethrow;
  }
}

checklogin() {
  _account.getSession(sessionId: 'current').then((result) {
    if (result.current == true) {
      Navigator.pushNamed(context, '/home');
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

When we run it, the result should look like this:

result

Conclusion

This tutorial has shown how to add a GitHub login to a Flutter application. The use of social authentication not only brings ease but security as well, as it promotes single sign-on (SSO).

An SSO framework such as OAuth stands a chance of being the future of user authentication, and Appwrites marshals it strongly. That is because Appwrite allows the authentication process without developers having to create the OAuth protocols themselves.

Resources

Here are resources to assist during the learning process:

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay