diff --git a/src/routes/docs/tutorials/flutter-auth/+layout.svelte b/src/routes/docs/tutorials/flutter-auth/+layout.svelte new file mode 100644 index 0000000000..fb9fb3980f --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/+layout.svelte @@ -0,0 +1,10 @@ + + + diff --git a/src/routes/docs/tutorials/flutter-auth/+layout.ts b/src/routes/docs/tutorials/flutter-auth/+layout.ts new file mode 100644 index 0000000000..562b11506f --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/+layout.ts @@ -0,0 +1,11 @@ +import type { LayoutLoad } from './$types'; + +export const load: LayoutLoad = ({ url }) => { + const tutorials = import.meta.glob('./**/*.markdoc', { + eager: true + }); + return { + tutorials, + pathname: url.pathname + }; +}; diff --git a/src/routes/docs/tutorials/flutter-auth/+page.ts b/src/routes/docs/tutorials/flutter-auth/+page.ts new file mode 100644 index 0000000000..94603a4e99 --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/+page.ts @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = async () => { + throw redirect(303, '/docs/tutorials/flutter-auth/step-1'); +}; diff --git a/src/routes/docs/tutorials/flutter-auth/step-1/+page.markdoc b/src/routes/docs/tutorials/flutter-auth/step-1/+page.markdoc new file mode 100644 index 0000000000..437ac2abf0 --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/step-1/+page.markdoc @@ -0,0 +1,18 @@ +--- +layout: tutorial +title: Authentication with Flutter +description: Add Authentication to a Flutter project using Appwrite. +step: 1 +difficulty: intermediate +readtime: 20 +--- + +Appwrite takes away your stress of building and maintaining a backend. Appwrite helps you implement authentication, databases, file storage, and respond to real-time events with **secure** APIs out of the box. +If you're a Flutter developer, examples in this guide shows you how Appwrite can help you add authentication to Flutter apps faster. + +## Before you start + +Even if you've never tried Appwrite, you will get an idea of what it'll feel like to build with Flutter and Appwrite. + +If you're inspired and wish to follow along, make sure you've followed [Start with Flutter](https://appwrite.io/docs/quick-starts/flutter) first. +You can also choose to clone our [Getting started](https://github.com/appwrite/getting-started-projects/tree/main/flutter) examples and follow along by looking at our example code. diff --git a/src/routes/docs/tutorials/flutter-auth/step-2/+page.markdoc b/src/routes/docs/tutorials/flutter-auth/step-2/+page.markdoc new file mode 100644 index 0000000000..c315cda8aa --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/step-2/+page.markdoc @@ -0,0 +1,51 @@ +--- +layout: tutorial +title: Create project +description: Add Authentication to a Flutter project using Appwrite. +step: 2 +--- + + +You can create a Flutter project using the following command + +```sh +flutter create my_flutter_project +``` + +The command will automatically create a Flutter project with the name `my_flutter_project`. You can change the name of the project by changing the name of the project in the command. + +You will see the following output after the project is created. + +```sh +Creating project my_flutter_project... +Resolving dependencies in my_flutter_project... (1.9s) +Got dependencies in my_flutter_project. +Wrote 129 files. + +All done! +You can find general documentation for Flutter at: https://docs.flutter.dev/ +Detailed API documentation is available at: https://api.flutter.dev/ +If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev + +In order to run your application, type: + + $ cd my_flutter_project + $ flutter run + +Your application code is in my_flutter_project\lib\main.dart. +``` + +After the prompt is finished, you can head over to the newly create project. + +```sh +cd my_flutter_project +flutter run +``` + +## Adding Appwrite to Your Flutter App + +Appwrite provides a Web SDK that can be used in your Flutter apps. You can use Appwrite by installing the Flutter pub package. + +```sh +flutter pub add appwrite +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter-auth/step-3/+page.markdoc b/src/routes/docs/tutorials/flutter-auth/step-3/+page.markdoc new file mode 100644 index 0000000000..76a0a4b40f --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/step-3/+page.markdoc @@ -0,0 +1,44 @@ +--- +layout: tutorial +title: Initialize SDK +description: Add Authentication to a Flutter project using Appwrite. +step: 3 +--- +Before you can use Appwrite, you need to instanciate the Appwrite `Client` class with the project ID and endpoint. +This tells the SDK where your Appwrite project is hosted and which one to connect to. + +The client is then used to initialize services like `Databases` and `Account`, so they all point to the same Appwrite project. + +You can do this by instantiating the services you need in a file like `lib/config/appwrite.dart` and **exporting the instances**. + +```dart +// lib/config/appwrite.dart +import 'package:appwrite/appwrite.dart'; + +import 'package:my_flutter_project/constants.dart' as constants; + +class Appwrite { + static final Appwrite instance = Appwrite._instance(); + + late final Client client; + + late final Account account; + + Appwrite._instance() { + client = Client() + .setEndpoint("https://cloud.appwrite.io/v1") + .setProject(constants.appwriteProjectID); + account = Account(client); + } +} +``` + +`appwriteProjectID` is a variable that you can store in a different constants file. + +For example, your `lib/constants.dart` file might look something similar to this. + +```dart +String appwriteProjectID = "642sdddf85b440dc7e5bf"; +``` + +You can get the value for this variable from the Appwrite console's **Settings** page. diff --git a/src/routes/docs/tutorials/flutter-auth/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter-auth/step-4/+page.markdoc new file mode 100644 index 0000000000..a40623a843 --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/step-4/+page.markdoc @@ -0,0 +1,147 @@ +--- +layout: tutorial +title: Check if logged in +description: Add Authentication to a Flutter project using Appwrite. +step: 4 +--- + +Before taking a user to the login screen, we should check if they're already logged in. +With Flutter, you can use the `FutureBuilder` to check if you're logged in before your app renders using `account.get()` method. + +The first step would be to create `named routes` for the login and signup screens in `lib/main.dart`: + +Also create a `lib/screens` directory and create `login.dart` and `signup.dart` files in it. + +```dart +// lib/main.dart +class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override + Widget build(BuildContext context) { + return MaterialApp( + home: const MyHomePage(), + routes: { + '/login': (context) => const LogInPage(), + '/signup': (context) => const SignUpPage(), + '/home': (context) => const MyHomePage(), + }, + ); + } +} +``` + +Now Using `FutureBuilder` and providing the `account.get()` method as the future, we can check if the user is logged in. If the user is logged in, we can show the welcome message, otherwise we can show the login and signup buttons. + + +```dart +// lib/main.dart +FutureBuilder( + future: Appwrite.instance.account.get(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Scaffold( + body: Center( + child: Text( + 'Hello ${snapshot.data!.name}', + ), + ), + ); + } else { + // Show login and signup buttons + } + }, +), +``` + +Comple code for `lib/main.dart` should look like this: + +```dart +import 'package:flutter/material.dart'; + +import 'config/appwrite.dart'; +import 'screens/login.dart'; +import 'screens/signup.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override + Widget build(BuildContext context) { + return MaterialApp( + home: const MyHomePage(), + routes: { + '/login': (context) => const LogInPage(), + '/signup': (context) => const SignUpPage(), + '/home': (context) => const MyHomePage(), + }, + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: FutureBuilder( + future: Appwrite.instance.account.get(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Hello ${snapshot.data!.name}', + ), + // add a logout button + ElevatedButton( + onPressed: () async { + await Appwrite.instance.account.deleteSession( + sessionId: 'current', + ); + setState(() {}); + }, + child: const Text('Log out'), + ), + ], + ), + ), + ); + } else { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + Navigator.pushNamed(context, '/login'); + }, + child: const Text('Log in'), + ), + ElevatedButton( + onPressed: () { + Navigator.pushNamed(context, '/signup'); + }, + child: const Text('Sign up'), + ), + ], + ), + ); + } + }, + ), + ); + } +} +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter-auth/step-5/+page.markdoc b/src/routes/docs/tutorials/flutter-auth/step-5/+page.markdoc new file mode 100644 index 0000000000..e02206390d --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/step-5/+page.markdoc @@ -0,0 +1,209 @@ +--- +layout: tutorial +title: Create login page +description: Add Authentication to a Flutter project using Appwrite. +step: 5 +--- + +We can now implement our login page. Go to your `login.dart` file in the `lib/screens` directory: + + +Create a basic login page with a form to let the user input their email and password. We'll use the `onSaved` and `validator` properties of the `TextFormField` widget to save the user's input and validate it. We'll also use the `obscureText` property to hide the password as the user types it. Finally, we'll use the `ElevatedButton` widget to create a button that will call the `onPressed` function when pressed. We'll use this function to perform the login. +```dart +// lib/screens/login.dart +class LogInPage extends StatefulWidget { + const LogInPage({super.key}); + + @override + State createState() => _LogInPageState(); +} + +class _LogInPageState extends State { + final _formKey = GlobalKey(); + String _email = ''; + String _password = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextFormField( + decoration: const InputDecoration( + labelText: 'Email', + ), + validator: (value) { + if (value == '') { + return 'Please enter your email'; + } + return null; + }, + onSaved: (value) { + _email = value!; + }, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Password', + ), + obscureText: true, + validator: (value) { + if (value == '') { + return 'Please enter your password'; + } + return null; + }, + onSaved: (value) { + _password = value!; + }, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: ElevatedButton( + onPressed: () async { + // TODO: Perform login + }, + child: const Text('Login'), + ), + ), + ], + ), + ), + ), + ); + } +} +``` + +Once the user presses the login button, we'll call the `onPressed` function. This function will first check if the form is valid. If it is, we'll call the `save` function on the form to save the user's input. We'll then call the `account.createEmailSession()` function to perform the login. If the login is successful, we'll navigate to the home page. If not, we'll show an error message using a `SnackBar`. + +```dart +// lib/screens/login.dart +onPressed: () async { + if(_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + try { + await Appwrite.instance.account.createEmailSession( + email: _email, + password: _password, + ); + if (context.mounted) { + Navigator.pushNamed(context, '/home'); + } + } on AppwriteException catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.message ?? "Error"), + ), + ); + } + } + } +} +``` + + +Complete code for the `login.dart` file: +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:flutter/material.dart'; + +import '../config/appwrite.dart'; + +class LogInPage extends StatefulWidget { + const LogInPage({super.key}); + + @override + State createState() => _LogInPageState(); +} + +class _LogInPageState extends State { + final _formKey = GlobalKey(); + String _email = ''; + String _password = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextFormField( + decoration: const InputDecoration( + labelText: 'Email', + ), + validator: (value) { + if (value == '') { + return 'Please enter your email'; + } + return null; + }, + onSaved: (value) { + _email = value!; + }, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Password', + ), + obscureText: true, + validator: (value) { + if (value == '') { + return 'Please enter your password'; + } + return null; + }, + onSaved: (value) { + _password = value!; + }, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: ElevatedButton( + onPressed: () async { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + try { + await Appwrite.instance.account.createEmailSession( + email: _email, + password: _password, + ); + if (context.mounted) { + Navigator.pushNamed(context, '/home'); + } + } on AppwriteException catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.message ?? "Error"), + ), + ); + } + } + } + }, + child: const Text('Login'), + ), + ), + ], + ), + ), + ), + ); + } +} +``` + +That's is it for the login page. We can now move on to the next step where we'll create the Signup page. \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter-auth/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter-auth/step-6/+page.markdoc new file mode 100644 index 0000000000..f7ed4083f6 --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/step-6/+page.markdoc @@ -0,0 +1,125 @@ +--- +layout: tutorial +title: Create signup page +description: Add Authentication to a SvelteKit project using Appwrite. +step: 6 +--- + +For signup, you can copy the `login.dart` file with some small changes to the file name and the form. The form will be changed to have a name field and the `account.create()` method will be used followed by `account.createEmailSession()`. + +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:flutter/material.dart'; + +import '../config/appwrite.dart'; + +class SignUpPage extends StatefulWidget { + const SignUpPage({super.key}); + + @override + State createState() => _SignUpPageState(); +} + +class _SignUpPageState extends State { + final _formKey = GlobalKey(); + String _name = ''; + String _email = ''; + String _password = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextFormField( + decoration: const InputDecoration( + labelText: 'Name', + ), + validator: (value) { + if (value == '') { + return 'Please enter your Name'; + } + return null; + }, + onSaved: (value) { + _name = value!; + }, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Email', + ), + validator: (value) { + if (value == '') { + return 'Please enter your email'; + } + return null; + }, + onSaved: (value) { + _email = value!; + }, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Password', + ), + obscureText: true, + validator: (value) { + if (value == '') { + return 'Please enter your password'; + } + return null; + }, + onSaved: (value) { + _password = value!; + }, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: ElevatedButton( + onPressed: () async { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + try { + await Appwrite.instance.account.create( + userId: ID.unique(), + name: _name, + email: _email, + password: _password, + ); + await Appwrite.instance.account.createEmailSession( + email: _email, + password: _password, + ); + if (context.mounted) { + Navigator.pushNamed(context, '/home'); + } + } on AppwriteException catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.message ?? "Error"), + ), + ); + } + } + } + }, + child: const Text('Sign up'), + ), + ), + ], + ), + ), + ), + ); + } +} +``` +With this, you have a simple authentication system! diff --git a/src/routes/docs/tutorials/flutter-auth/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter-auth/step-7/+page.markdoc new file mode 100644 index 0000000000..6a83d95d46 --- /dev/null +++ b/src/routes/docs/tutorials/flutter-auth/step-7/+page.markdoc @@ -0,0 +1,11 @@ +--- +layout: tutorial +title: All set +description: Add Authentication to a Flutter project using Appwrite. +step: 7 +--- +If you want to see these authentication concepts applied in a more robust manner, you can see them in action in this [demo app](https://github.com/Rohit-RA-2020/Flutter-Authentication-Deepdive). + +## Other authentication methods +Appwrite also supports OAuth, passwordless login, anonymous login, and phone login. +Learn more about them in the [authentication guide](https://appwrite.io/docs/products/auth).