Implementation
static Future<void> implementAppwrite(String projectName) async {
// Import appwrite sdk
final ProcessResult addPackagesResult = await Process.run(
'dart',
['pub', 'add', 'appwrite'],
);
final ProcessResult addProviderResult = await Process.run(
'dart',
['pub', 'add', 'provider'],
);
if (addPackagesResult.exitCode != 0) {
print(
'Error adding appwrite packages. Check your internet connection and try again.');
print(addPackagesResult.stderr);
return;
}
print('added appwrite sdk to project');
if (addProviderResult.exitCode != 0) {
print(
'Error adding provider package. Check your internet connection and try again.');
print(addProviderResult.stderr);
return;
}
print('added appwrite sdk to project');
// create auth folder
final authFolder = Directory('lib/features/auth');
authFolder.createSync();
// create auth service folder
final authServiceFolder = Directory('lib/features/auth/service');
authServiceFolder.createSync();
final File authStatusFile =
File('lib/features/auth/service/auth_status.dart');
if (!authStatusFile.existsSync()) {
authStatusFile.createSync();
authStatusFile.writeAsStringSync("""
// ignore_for_file: avoid_print
import 'package:appwrite/appwrite.dart';
import 'package:appwrite/models.dart';
import 'package:flutter/material.dart';
enum AuthStatus {
uninitialized,
authenticated,
unauthenticated,
}
class AuthService extends ChangeNotifier {
Client client = Client();
late final Account account;
late User _currentUser;
AuthStatus _status = AuthStatus.uninitialized;
// Getter methods
User get currentUser => _currentUser;
AuthStatus get status => _status;
String? get username => _currentUser.name;
String? get email => _currentUser.email;
String? get userid => _currentUser.\$id;
// Constructor
AuthService() {
init();
loadUser();
}
// Initialize the Appwrite client
init() {
client
.setEndpoint('replace_with_your_endpoint')
.setProject('replace_with_project_id')
.setSelfSigned();
account = Account(client);
}
loadUser() async {
try {
final user = await account.get();
_status = AuthStatus.authenticated;
_currentUser = user;
} catch (e) {
_status = AuthStatus.unauthenticated;
} finally {
notifyListeners();
}
}
Future<User> createUser(
{required String email, required String password, required String name}) async {
notifyListeners();
try {
final user = await account.create(
userId: ID.unique(),
email: email,
password: password,
name: name);
return user;
} finally {
notifyListeners();
}
}
Future<Session> createEmailSession(
{required String email, required String password}) async {
notifyListeners();
try {
final session =
await account.createEmailSession(email: email, password: password);
_currentUser = await account.get();
_status = AuthStatus.authenticated;
return session;
} finally {
notifyListeners();
}
}
signInWithProvider({required String provider}) async {
try {
final session = await account.createOAuth2Session(provider: provider);
_currentUser = await account.get();
_status = AuthStatus.authenticated;
return session;
} finally {
notifyListeners();
}
}
signOut() async {
try {
await account.deleteSession(sessionId: 'current');
_status = AuthStatus.unauthenticated;
} finally {
notifyListeners();
}
}
Future<Preferences> getUserPreferences() async {
return await account.getPrefs();
}
updatePreferences({required String bio}) async {
return account.updatePrefs(prefs: {'bio': bio});
}
}
""");
}
// change the content of androidmanifest.xml
final File androidManifest =
File('android/app/src/main/AndroidManifest.xml');
androidManifest.writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="$projectName"
android:name="\${applicationName}"
android:icon="@mipmap/ic_launcher">
<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-<YOUR-APPWRITE-PROJECT-ID>" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
''');
// change the content of main.dart
final File mainFile = File('lib/main.dart');
mainFile.writeAsStringSync('''
import 'package:flutter/material.dart';
import 'package:appwrite/appwrite.dart';
import 'package:$projectName/features/auth/service/auth_status.dart';
import 'package:$projectName/features/auth/ui/auth_screen.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:$projectName/features/onBoarding/ui/on_boarding_screen.dart';
import 'package:$projectName/shared/nav_bar.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Client client = Client();
client = Client()
.setEndpoint("<YOUR_PROJECT_ENDPOINT>")
.setProject("<YOUR_PROJECT_ID>");
Account account = Account(client);
SharedPreferences prefs = await SharedPreferences.getInstance();
bool hasSeenOnboarding =
prefs.getBool('hasSeenOnboarding') ?? false;
runApp(ChangeNotifierProvider(
create: (context) => AuthService(),
child: MyApp(account: account, hasSeenOnboarding: hasSeenOnboarding,),
));
}
class MyApp extends StatelessWidget {
final Account account;
final bool hasSeenOnboarding;
const MyApp({
required this.account,
required this.hasSeenOnboarding,
super.key,
});
@override
Widget build(BuildContext context) {
final value = context.watch<AuthService>().status;
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
),
home: !hasSeenOnboarding
? const OnBoardingScreen()
: value == AuthStatus.uninitialized
? const Scaffold(
body: Center(child: CircularProgressIndicator()),
)
: value == AuthStatus.authenticated
? const NavigationScreen()
: const AuthScreen(),
);
}
}
''');
// create auth_screen.dart
final authUiFolder = Directory('lib/features/auth/ui');
if (!authUiFolder.existsSync()) {
authUiFolder.createSync();
final File authScreen = File('lib/features/auth/ui/auth_screen.dart');
authScreen.createSync();
authScreen.writeAsStringSync('''
import 'package:flutter/material.dart';
import 'package:appwrite/models.dart' as models;
import 'package:$projectName/constants/dimensions.dart';
import 'package:$projectName/features/auth/ui/login_screen.dart';
import 'package:$projectName/features/auth/ui/register_screen.dart';
import 'package:$projectName/shared/custom_button.dart';
class AuthScreen extends StatefulWidget {
const AuthScreen({super.key});
@override
State<AuthScreen> createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
models.User? loggedInUser;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset('assets/img/logo.png',
width: getScreenWidth(context) * 0.7)),
SizedBox(height: getScreenheight(context) * 0.02),
const Text(
'Made with quickfire',
style: TextStyle(fontWeight: FontWeight.w800),
),
SizedBox(height: getScreenheight(context) * 0.13),
CustomButton(
text: 'Sign up',
function: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RegisterScreen()));
}),
CustomButton(text: 'Continue with Phone number', function: () {}),
CustomButton(text: 'Continue with Google', function: () {}),
CustomButton(text: 'Continue with Facebook', function: () {}),
CustomButton(
text: 'Login',
function: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LogInScreen(),
),
);
}),
],
),
),
);
}
}
''');
}
// Create login screen
final File authLoginScreen = File('lib/features/auth/ui/login_screen.dart');
authLoginScreen.createSync();
authLoginScreen.writeAsStringSync('''
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:appwrite/appwrite.dart';
import 'package:$projectName/features/auth/service/auth_status.dart'; // service/auth_status.dart
import 'package:provider/provider.dart';
import 'package:$projectName/shared/custom_button.dart';
import 'package:$projectName/shared/custom_text_field.dart';
import 'package:$projectName/shared/nav_bar.dart';
class LogInScreen extends StatefulWidget {
const LogInScreen({super.key});
@override
State<LogInScreen> createState() => _LogInScreenState();
}
class _LogInScreenState extends State<LogInScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool loading = false;
// sign in func
signIn() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return const Dialog(
backgroundColor: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CircularProgressIndicator(),
],
),
);
});
try {
final AuthService appwrite = context.read<AuthService>();
await appwrite.createEmailSession(
email: _emailController.text,
password: _passwordController.text,
);
Navigator.pop(context);
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const NavigationScreen()));
} on AppwriteException catch (e) {
Navigator.pop(context);
showAlert(title: 'Login failed', text: e.message.toString());
}
}
showAlert({required String title, required String text}) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(title),
content: Text(text),
actions: [
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('OK'))
],
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CustomTextField(
controller: _emailController, label: 'Enter your email'),
CustomTextField(
controller: _passwordController,
label: 'Enter your pssword',
obscure: true,
),
CustomButton(
text: 'Sign in',
function: () {
signIn();
})
],
)),
);
}
}
''');
// Create registration screen
final File authRegisterScreen =
File('lib/features/auth/ui/register_screen.dart');
authRegisterScreen.createSync();
authRegisterScreen.writeAsStringSync('''
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:appwrite/appwrite.dart';
import 'package:provider/provider.dart';
import 'package:$projectName/shared/custom_button.dart';
import 'package:$projectName/shared/custom_text_field.dart';
import 'package:$projectName/shared/nav_bar.dart';
import 'package:$projectName/features/auth/service/auth_status.dart';
class RegisterScreen extends StatefulWidget {
const RegisterScreen({super.key});
@override
State<RegisterScreen> createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _nameController = TextEditingController();
createAccount() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return const Dialog(
backgroundColor: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [CircularProgressIndicator()],
),
);
});
try {
final AuthService appwrite = context.read<AuthService>();
await appwrite.createUser(
email: _emailController.text,
password: _passwordController.text,
name: _nameController.text,
);
Navigator.pop(context);
const snackbar = SnackBar(content: Text('Account created!'));
ScaffoldMessenger.of(context).showSnackBar(snackbar);
Navigator.pushReplacement(
context,
(MaterialPageRoute(
builder: (context) => const NavigationScreen(),
)),
);
} on AppwriteException catch (e) {
Navigator.pop(context);
showAlert(title: 'Account creation failed', text: e.message.toString());
}
}
showAlert({required String title, required String text}) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(title),
content: Text(text),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Ok'))
],
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTextField(
controller: _nameController, label: 'Enter your name!'),
CustomTextField(
controller: _emailController, label: 'Enter your email!'),
CustomTextField(
controller: _passwordController,
label: 'Enter your password!',
obscure: true,
),
CustomButton(
text: 'Create new account!',
function: () {
createAccount();
}),
],
)),
);
}
}
''');
}