From 9e4184729b5545a3f88dfb47aa36346d43b149cd Mon Sep 17 00:00:00 2001 From: Aditya Date: Sat, 11 Apr 2026 23:02:15 +0530 Subject: [PATCH 01/18] Write tutorial to build an ideas tracker app with flutter --- .../tutorials/flutter/step-1/+page.markdoc | 33 ++- .../tutorials/flutter/step-2/+page.markdoc | 32 +++ .../tutorials/flutter/step-3/+page.markdoc | 57 +++++ .../tutorials/flutter/step-4/+page.markdoc | 236 ++++++++++++++++++ .../tutorials/flutter/step-5/+page.markdoc | 107 ++++++++ .../tutorials/flutter/step-6/+page.markdoc | 157 ++++++++++++ .../tutorials/flutter/step-7/+page.markdoc | 146 +++++++++++ .../tutorials/flutter/step-8/+page.markdoc | 17 ++ 8 files changed, 773 insertions(+), 12 deletions(-) create mode 100644 src/routes/docs/tutorials/flutter/step-2/+page.markdoc create mode 100644 src/routes/docs/tutorials/flutter/step-3/+page.markdoc create mode 100644 src/routes/docs/tutorials/flutter/step-4/+page.markdoc create mode 100644 src/routes/docs/tutorials/flutter/step-5/+page.markdoc create mode 100644 src/routes/docs/tutorials/flutter/step-6/+page.markdoc create mode 100644 src/routes/docs/tutorials/flutter/step-7/+page.markdoc create mode 100644 src/routes/docs/tutorials/flutter/step-8/+page.markdoc diff --git a/src/routes/docs/tutorials/flutter/step-1/+page.markdoc b/src/routes/docs/tutorials/flutter/step-1/+page.markdoc index 3a49cf1d88..c2f5a5fccb 100644 --- a/src/routes/docs/tutorials/flutter/step-1/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-1/+page.markdoc @@ -1,19 +1,28 @@ --- layout: tutorial -title: Coming soon -description: Learn to build an Flutter app with no backend code using an Appwrite backend. -framework: Flutter -back: /docs/tutorials -category: Mobile and native +title: Build an ideas tracker with Flutter +description: Learn to build a Flutter app with no backend code using an Appwrite backend. step: 1 -draft: true +difficulty: beginner +back: /docs/tutorials +readtime: 15 +category: Mobile and native +framework: Flutter --- -Improve the docs, add this guide. +**Idea tracker**: an app to track all the side project ideas that you'll start, but probably never finish. +In this tutorial, you will build Idea tracker with Appwrite and Flutter. + +## Concepts {% #concepts %} +This tutorial will introduce the following concepts: + +1. Setting up your first project +2. Authentication +3. Databases and tables +4. Queries and pagination -We still don't have this guide in place, but we do have some great news. -The Appwrite docs, just like Appwrite, is completely open sourced. -This means, anyone can help improve them and add new guides and tutorials. -If you see this page, **we're actively looking for contributions to this page**. -Follow our contribution guidelines, open a PR to [our Website repo](https://github.com/appwrite/website), and collaborate with our core team to improve this page. \ No newline at end of file +## Prerequisites {% #prerequisites %} +1. Android, iOS simulators, or a physical device to run the app +2. Have [Dart](https://dart.dev/) and [Flutter SDK](https://flutter.dev/) installed on your computer +3. Basic knowledge of Flutter and Provider. \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter/step-2/+page.markdoc b/src/routes/docs/tutorials/flutter/step-2/+page.markdoc new file mode 100644 index 0000000000..679c7f8d16 --- /dev/null +++ b/src/routes/docs/tutorials/flutter/step-2/+page.markdoc @@ -0,0 +1,32 @@ +--- +layout: tutorial +title: Create app +description: Create a Flutter app project using Appwrite. +step: 2 +--- + +## Create Flutter project {% #create-flutter-project %} + +Create a Flutter app with the `flutter create` command. + +```sh +flutter create ideas_tracker +cd ideas_tracker +``` + +## Add dependencies {% #add-dependencies %} + +Install the Flutter Appwrite SDK and provider. + +```sh +flutter pub add appwrite:17.0.0 +flutter pub add provider +``` + +For iOS, make sure you have CocoaPods installed. Then install the pods to complete the installation: + +``` +cd ios +pod install +cd .. +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter/step-3/+page.markdoc b/src/routes/docs/tutorials/flutter/step-3/+page.markdoc new file mode 100644 index 0000000000..faac3eb739 --- /dev/null +++ b/src/routes/docs/tutorials/flutter/step-3/+page.markdoc @@ -0,0 +1,57 @@ +--- +layout: tutorial +title: Set up Appwrite +description: Import and initialize Appwrite for your Flutter application. +step: 3 +--- + +## Create project {% #create-project %} + +Head to the [Appwrite Console](https://cloud.appwrite.io/console). + +{% only_dark %} +![Create project screen](/images/docs/quick-starts/dark/create-project.png) +{% /only_dark %} +{% only_light %} +![Create project screen](/images/docs/quick-starts/create-project.png) +{% /only_light %} + +If this is your first time using Appwrite, create an account and create your first project. + +Then, under **Add a platform**, add a Flutter platform (Android/iOS/Linux etc.) with the package/bundle ID `com.example.ideas_tracker`. + +{% only_dark %} +![Add a platform](/images/docs/quick-starts/dark/add-platform.png) +{% /only_dark %} +{% only_light %} +![Add a platform](/images/docs/quick-starts/add-platform.png) +{% /only_light %} + +You can skip optional steps. + +## Initialize Appwrite SDK {% #init-sdk %} + +To use Appwrite in our Flutter app, you'll need to find our project ID. +Find your project's ID in the **Settings** page. + +{% only_dark %} +![Project settings screen](/images/docs/quick-starts/dark/project-id.png) +{% /only_dark %} +{% only_light %} +![Project settings screen](/images/docs/quick-starts/project-id.png) +{% /only_light %} + +Create a new file `lib/appwrite.dart` to hold our Appwrite related code. +Only one instance of the `Client()` should be created per app. +Add the following code to it, replacing `` with your project ID. + +```dart +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject(""); + +final account = Account(client); +final db = Databases(client); +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc new file mode 100644 index 0000000000..443eade4fa --- /dev/null +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -0,0 +1,236 @@ +--- +layout: tutorial +title: Add authentication +description: Add authentication to your Flutter application. +step: 4 +--- + +## User context {% #user-context %} + +In Flutter, you can use [provider](https://pub.dev/packages/provider) for state management. + +Create a new file `lib/providers/user_provider.dart` and add the following code to it. + +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:appwrite/models.dart' as models; +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/appwrite.dart'; + +class UserProvider extends ChangeNotifier { + models.User? _current; + + models.User? get current => _current; + + UserProvider() { + init(); + } + + Future login(String email, String password) async { + try { + await account.createEmailPasswordSession( + email: email, + password: password, + ); + _current = await account.get(); + notifyListeners(); + print('Welcome back. You are logged in'); + } catch (e) { + rethrow; + } + } + + Future logout() async { + try { + await account.deleteSession(sessionId: 'current'); + _current = null; + notifyListeners(); + print("Logged out"); + } catch(e) { + rethrow; + } + } + + Future register(String email, String password) async { + try { + await account.create(userId: ID.unique(), email: email, password: password); + await login(email, password); + notifyListeners(); + print("Account created"); + } catch (e) { + rethrow; + } + } + + Future init() async { + try { + _current = await account.get(); + notifyListeners(); + } catch (e) { + _current = null; + notifyListeners(); + } + } +} +``` + +Add the `UserProvider` to `main.dart` to make it accessible throughout the App. + +```dart +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/providers/user_provider.dart'; +import 'package:ideas_tracker/screens/login.dart'; +import 'package:provider/provider.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => UserProvider()), + ], + child: const MyApp(), + ) + ); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: "Ideas Tracker", + debugShowCheckedModeBanner: false, + home: Login() + ); + } +} +``` + +Now, you can use `UserProvider` to access the user's data inside any Widget. + +## Styling {% #styling %} + +To maintain DRY principles, we will move all styling constants to `lib/styles.dart`. Defining these as static class members allows for consistent, reusable widget styling across the entire app. + +```dart +import 'package:flutter/material.dart'; + +class Styles { + static TextStyle heading = TextStyle(fontSize: 24, fontWeight: FontWeight.w600); + + static InputDecoration input = InputDecoration( + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey), + borderRadius: BorderRadius.circular(22), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.pink, width: 2), + borderRadius: BorderRadius.circular(22), + ), + ); + + static ButtonStyle button = ElevatedButton.styleFrom( + backgroundColor: Colors.pinkAccent, + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric(vertical: 12, horizontal: 24), + ); + + static ButtonStyle disabledButton = ElevatedButton.styleFrom( + backgroundColor: Colors.grey, + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric(vertical: 12, horizontal: 24), + ); +} +``` + +## Login page {% #login-page %} + +Create a new file `lib/screens/login.dart` and add the following code to it. +this page contains a basic form to allow the user to login or register. +Notice how this page utilizes the `UserProvider` to perform login and register actions. + +```dart +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/providers/user_provider.dart'; +import 'package:ideas_tracker/styles.dart'; +import 'package:provider/provider.dart'; + +class Login extends StatefulWidget { + + const Login({super.key}); + + @override + State createState() => _LoginState(); +} + +class _LoginState extends State { + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + + @override + void dispose() { + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 40), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Login or Register", style: Styles.heading), + SizedBox(height: 40,), + TextField( + controller: _emailController, + decoration: Styles.input.copyWith( + hintText: "Email" + ), + ), + SizedBox(height: 25), + TextField ( + controller: _passwordController, + decoration: Styles.input.copyWith( + hintText: "Password" + ), + ), + SizedBox(height: 25), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + context.read().login( + _emailController.text, + _passwordController.text, + ); + }, + style: Styles.button, + child: Text("Login") + ), + SizedBox(width: 24), + ElevatedButton( + onPressed: () { + context.read().register( + _emailController.text, + _passwordController.text, + ); + }, + style: Styles.button, + child: Text("Register"), + ), + ], + ) + ]), + ), + ) + ); + } +} +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter/step-5/+page.markdoc b/src/routes/docs/tutorials/flutter/step-5/+page.markdoc new file mode 100644 index 0000000000..4cfd5caae7 --- /dev/null +++ b/src/routes/docs/tutorials/flutter/step-5/+page.markdoc @@ -0,0 +1,107 @@ +--- +layout: tutorial +title: Add routing +description: Add routing to your Flutter application using Appwrite. +step: 5 +--- +In this step, you'll add some basic routing to your app. +Based on the user's login status, you'll redirect them to the login page or the home page. + +## Home page {% #home-page %} + +Create a new file `lib/screens/home.dart` and add the following stub code to it. +We'll update this page later to display the ideas posted by other users and allow the user to post their ideas. + +```dart +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/providers/user_provider.dart'; +import 'package:provider/provider.dart'; + +class Home extends StatelessWidget { + const Home({super.key}); + + @override + Widget build(BuildContext context) { + final userProvider = context.watch(); + final email = userProvider.current?.email ?? "Please login"; + + return Scaffold( + body: Center( + child: Text("Welcome, $email") + ) + ); + } +} +``` + +## Routing {% #routing %} +To handle routing, we can use provider to check the user's state. +This router consumes the `UserProvider` to determine if the user is logged in or not to redirect the user automatically. + +Create a file `lib/routes.dart` and add the following code to it. +```dart +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/screens/home.dart'; +import 'package:ideas_tracker/screens/login.dart'; +import 'package:provider/provider.dart'; +import 'providers/user_provider.dart'; + +class Routes extends StatelessWidget { + const Routes({super.key}); + + @override + Widget build(BuildContext context) { + final userProvider = context.watch(); + + if (userProvider.current != null) { + return const Home(); + } else { + return const Login(); + } + } +} + +``` + +We'll display this router in the `main.dart` file by replacing `Login()` by `Routes()`. + +```dart +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/providers/user_provider.dart'; +import 'package:ideas_tracker/routes.dart'; +import 'package:provider/provider.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => UserProvider()), + ], + child: const MyApp(), + ) + ); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: "Ideas Tracker", + debugShowCheckedModeBanner: false, + home: Routes() + ); + } +} +``` + +## Test the routing {% #test-routing %} +Now, if you run the app, you should see the login page. +If you create a new account, you should be logged in and redirected to the home page. + +Run the app using the following command. +```sh +flutter run +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc new file mode 100644 index 0000000000..634de8e444 --- /dev/null +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -0,0 +1,157 @@ +--- +layout: tutorial +title: Add database +description: Connect a database to your Flutter application using Appwrite Flutter SDK. +step: 6 +--- +In this step, you'll set up a database to store ideas in Appwrite, configure permissions, then create a +context to manage ideas in your Flutter app. + +# Create table {% #create-table %} +In Appwrite, data is stored as a table of rows. Create a table in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas. + +{% only_dark %} +![Create project screen](/images/docs/tutorials/dark/idea-tracker-table.png) +{% /only_dark %} +{% only_light %} +![Create project screen](/images/docs/tutorials/idea-tracker-table.png) +{% /only_light %} + +Create a new table with the following columns: +| Field | Type | Required | +|-------------|--------|----------| +| userId | Varchar | Yes | +| title | Varchar | Yes | +| description | Text | No | + +## Configure permissions {% #configure-permissions %} +{% only_dark %} +![Table permissions screen](/images/docs/tutorials/dark/idea-tracker-permissions.png) +{% /only_dark %} +{% only_light %} +![Table permissions screen](/images/docs/tutorials/idea-tracker-permissions.png) +{% /only_light %} + +Navigate to the **Settings** tab of your table, add the role **Any** and check the **Read** box. +Next, add a **Users** role and give them access to **Create** by checking those boxes. +These permissions apply to all rows in your new table. + +Finally, enable **Row security** to allow further permissions to be set at the row level. +Remember to click the **Update** button to apply your changes. + +## Ideas context {% #ideas-context %} + +Now that you have a table to hold ideas, we can read and write to it from our app. +Like you did with the user data, we will create a Flutter provider to hold our ideas. + +Create a new file `lib/providers/ideas_provider.dart` and add the following code to it. + +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:flutter/material.dart'; +import 'package:appwrite/models.dart' as models; +import 'package:ideas_tracker/appwrite.dart'; + +class IdeasProvider extends ChangeNotifier { + static const String ideasDatabaseId = "default"; // Replace with your database ID + static const String ideasCollectionId = "ideas_tracker"; // Replace with your table ID + + List _ideas = []; + List get current => _ideas; + + IdeasProvider() { + init(); + } + + Future init() async { + try { + final response = await db.listDocuments( + databaseId: ideasDatabaseId, + collectionId: ideasCollectionId, + queries: [Query.orderDesc("\$createdAt"), Query.limit(10)], + ); + _ideas = response.documents; + notifyListeners(); + } catch (e) { + debugPrint("Error fetching ideas: $e"); + } + } + + Future add(Map data, String userId) async { + try { + final document = await db.createDocument( + databaseId: ideasDatabaseId, + collectionId: ideasCollectionId, + documentId: ID.unique(), + data: data, + permissions: [Permission.write(Role.user(userId))], + ); + + _ideas = [document, ..._ideas]; + if (_ideas.length > 10) _ideas = _ideas.sublist(0, 10); + + notifyListeners(); + print('Idea added'); + } catch (e) { + rethrow; + } + } + + Future remove(String id) async { + try { + await db.deleteDocument( + databaseId: ideasDatabaseId, + collectionId: ideasCollectionId, + documentId: id, + ); + + _ideas.removeWhere((item) => item.$id == id); + notifyListeners(); + await init(); + print('Idea removed'); + } catch (e) { + rethrow; + } + } +} +``` + +Notice that new ideas have the added permission `Permission.write(Role.user(idea.userId))`. +This permission ensures that only the user who created the idea can modify it. + +Remember to add the `IdeasProvider` to your `main.dart` file. + +```dart +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/providers/ideas_provider.dart'; +import 'package:ideas_tracker/providers/user_provider.dart'; +import 'package:ideas_tracker/routes.dart'; +import 'package:provider/provider.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => UserProvider()), + // Add IdeasProvider to make it accessible throughout the App + ChangeNotifierProvider(create: (_) => IdeasProvider()), + ], + child: const MyApp(), + ) + ); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: "Ideas Tracker", + debugShowCheckedModeBanner: false, + home: Routes() + ); + } +} +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc new file mode 100644 index 0000000000..ec6a15e3d2 --- /dev/null +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -0,0 +1,146 @@ +--- +layout: tutorial +title: Create ideas page +description: Add database queries and pagination using Appwrite in your Flutter application. +step: 7 +--- + +Using `IdeasProvider` you can now display the ideas on the page and create a form to submit new ideas. + +If an idea is submitted by the logged-in user, a remove button will be displayed to remove the idea. +While this check uses the user ID to determine the render logic, permissions set in step 6 will be used to enforce +that only the owner of the idea can remove it. + +Overwrite the contents of `lib/screens/home.dart` with the following: + +```dart +import 'package:flutter/material.dart'; +import 'package:ideas_tracker/providers/ideas_provider.dart'; +import 'package:ideas_tracker/providers/user_provider.dart'; +import 'package:ideas_tracker/styles.dart'; +import 'package:provider/provider.dart'; + +class Home extends StatefulWidget { + const Home({super.key}); + + @override + State createState() => _HomeState(); +} + +class _HomeState extends State { + final TextEditingController _titleController = TextEditingController(); + final TextEditingController _descriptionController = TextEditingController(); + + @override + void dispose() { + _titleController.dispose(); + _descriptionController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final userProvider = context.watch(); + final ideasProvider = context.watch(); + final user = userProvider.current; + + return Scaffold( + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.all(32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (user != null) ...[ + Text( + "Submit Idea", + style: Styles.heading, + ), + const SizedBox(height: 10), + TextField( + controller: _titleController, + decoration: Styles.input.copyWith( + hintText: "Title" + ), + ), + const SizedBox(height: 10), + TextField( + controller: _descriptionController, + decoration: Styles.input.copyWith( + hintText: "Description" + ), + ), + const SizedBox(height: 10), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + ideasProvider.add({ + 'userId': user.$id, + 'title': _titleController.text, + 'description': _descriptionController.text, + }, user.$id); + _titleController.clear(); + _descriptionController.clear(); + }, + style: Styles.button, + child: Text("Submit"), + ), + ), + ] else ...[ + const Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Text("Please login to submit an idea."), + ), + ], + + const SizedBox(height: 40), + Text( + "Latest Ideas", + style: Styles.heading, + ), + const SizedBox(height: 20), + + ...ideasProvider.current.map((idea) { + final String ideaUserId = idea.data['userId'] ?? ''; + final bool isOwner = user != null && user.$id == ideaUserId; + + return Card( + margin: const EdgeInsets.only(bottom: 16), + elevation: 4, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + idea.data['title'] ?? '', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + Text(idea.data['description'] ?? ''), + + const SizedBox(height: 8), + ElevatedButton( + onPressed: () => isOwner? ideasProvider.remove(idea.$id) : null, + style: isOwner ? Styles.button : Styles.disabledButton, + child: const Text( + "Remove", + ), + ), + ], + ), + ), + ); + }).toList() + ], + ), + ), + ) + ); + } +} +``` \ No newline at end of file diff --git a/src/routes/docs/tutorials/flutter/step-8/+page.markdoc b/src/routes/docs/tutorials/flutter/step-8/+page.markdoc new file mode 100644 index 0000000000..a5977d890e --- /dev/null +++ b/src/routes/docs/tutorials/flutter/step-8/+page.markdoc @@ -0,0 +1,17 @@ +--- +layout: tutorial +title: Next steps +description: Run your Flutter project built with Appwrite +step: 8 +--- + +## Test your project {% #test-project %} +You can run your project using the `flutter run` command. This will compile your app and install it on your connected device or emulator. + +While the app is running, you can use Hot Reload by pressing `r` in the terminal to see changes instantly without restarting the app. + +## Bundling for production {% #bundle-production %} +Appwrite's Flutter SDK is compatible with standard Flutter build targets. When you are ready to distribute your app, you can generate a release binary for your specific platform: +- Android (APK/App Bundle): `flutter build apk` or `flutter build appbundle` +- iOS: `flutter build ios` +- Web: `flutter build web` \ No newline at end of file From 331a1686bfd9a11963ed9fcf2cb668e00847ae78 Mon Sep 17 00:00:00 2001 From: Aditya Prasad Date: Tue, 14 Apr 2026 17:01:43 +0530 Subject: [PATCH 02/18] Update step 4 and set obscure text to true in password field Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- src/routes/docs/tutorials/flutter/step-4/+page.markdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc index 443eade4fa..69c83cc3b6 100644 --- a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -196,6 +196,7 @@ class _LoginState extends State { SizedBox(height: 25), TextField ( controller: _passwordController, + obscureText: true, decoration: Styles.input.copyWith( hintText: "Password" ), From e7bbbbb9161d174a84fa86548879ddae4dd1303f Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 17:08:47 +0530 Subject: [PATCH 03/18] add type and size fields in table column properties --- src/routes/docs/tutorials/flutter/step-6/+page.markdoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc index 634de8e444..daede20d26 100644 --- a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -18,11 +18,11 @@ In Appwrite, data is stored as a table of rows. Create a table in the [Appwrite {% /only_light %} Create a new table with the following columns: -| Field | Type | Required | -|-------------|--------|----------| -| userId | Varchar | Yes | -| title | Varchar | Yes | -| description | Text | No | +| Field | Type | Required | Size | +|-------------|---------|----------|------| +| userId | Varchar | Yes | 50 | +| title | Varchar | Yes | 25 | +| description | Text | No | - | ## Configure permissions {% #configure-permissions %} {% only_dark %} From 8c909040094e57b4e6fdc4402ba4fea7d858ae1f Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 17:13:03 +0530 Subject: [PATCH 04/18] handle exceptions by wrapping login and register in try-catch block --- .../tutorials/flutter/step-4/+page.markdoc | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc index 69c83cc3b6..257365e14b 100644 --- a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -206,22 +206,34 @@ class _LoginState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( - onPressed: () { - context.read().login( - _emailController.text, - _passwordController.text, - ); + onPressed: () async { + try { + await context.read().login( + _emailController.text, + _passwordController.text, + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); + } }, style: Styles.button, child: Text("Login") ), SizedBox(width: 24), ElevatedButton( - onPressed: () { - context.read().register( - _emailController.text, - _passwordController.text, - ); + onPressed: () async { + try { + await context.read().register( + _emailController.text, + _passwordController.text, + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); + } }, style: Styles.button, child: Text("Register"), From 9debd4f7359400b6319342b651546a72b3900cf1 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 17:14:39 +0530 Subject: [PATCH 05/18] replace print with debugPrint for consistency --- src/routes/docs/tutorials/flutter/step-4/+page.markdoc | 6 +++--- src/routes/docs/tutorials/flutter/step-6/+page.markdoc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc index 257365e14b..cd8209d136 100644 --- a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -34,7 +34,7 @@ class UserProvider extends ChangeNotifier { ); _current = await account.get(); notifyListeners(); - print('Welcome back. You are logged in'); + debugPrint('Welcome back. You are logged in'); } catch (e) { rethrow; } @@ -45,7 +45,7 @@ class UserProvider extends ChangeNotifier { await account.deleteSession(sessionId: 'current'); _current = null; notifyListeners(); - print("Logged out"); + debugPrint("Logged out"); } catch(e) { rethrow; } @@ -56,7 +56,7 @@ class UserProvider extends ChangeNotifier { await account.create(userId: ID.unique(), email: email, password: password); await login(email, password); notifyListeners(); - print("Account created"); + debugPrint("Account created"); } catch (e) { rethrow; } diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc index daede20d26..bf60fb9cab 100644 --- a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -91,7 +91,7 @@ class IdeasProvider extends ChangeNotifier { if (_ideas.length > 10) _ideas = _ideas.sublist(0, 10); notifyListeners(); - print('Idea added'); + debugPrint('Idea added'); } catch (e) { rethrow; } @@ -108,7 +108,7 @@ class IdeasProvider extends ChangeNotifier { _ideas.removeWhere((item) => item.$id == id); notifyListeners(); await init(); - print('Idea removed'); + debugPrint('Idea removed'); } catch (e) { rethrow; } From 923f628cfb73bc07094b940102301b60322829d8 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 17:22:24 +0530 Subject: [PATCH 06/18] add steps to create a database in the console --- src/routes/docs/tutorials/flutter/step-6/+page.markdoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc index bf60fb9cab..d120981db0 100644 --- a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -7,6 +7,14 @@ step: 6 In this step, you'll set up a database to store ideas in Appwrite, configure permissions, then create a context to manage ideas in your Flutter app. +## Create database {% #create-database %} +To store your ideas, you need to create a database first. + +1. Go to the Databases section in your Appwrite Console +2. Click *Create Database* +3. Give it a name and ID. For this tutorial, we'll use `Ideas Tracker` as the name and `ideas_tracker` as the ID. +4. You'll need to remember the database ID as you'll need it later. + # Create table {% #create-table %} In Appwrite, data is stored as a table of rows. Create a table in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas. From 4cbfb29ea16c024cb84198111867fad33a14eafe Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 17:25:08 +0530 Subject: [PATCH 07/18] increase varchar size to 200 as per other tutorials --- src/routes/docs/tutorials/flutter/step-6/+page.markdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc index d120981db0..c8e5b74005 100644 --- a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -28,8 +28,8 @@ In Appwrite, data is stored as a table of rows. Create a table in the [Appwrite Create a new table with the following columns: | Field | Type | Required | Size | |-------------|---------|----------|------| -| userId | Varchar | Yes | 50 | -| title | Varchar | Yes | 25 | +| userId | Varchar | Yes | 200 | +| title | Varchar | Yes | 200 | | description | Text | No | - | ## Configure permissions {% #configure-permissions %} From e7e123c5d35ad14e053f09d180b70f70305861d7 Mon Sep 17 00:00:00 2001 From: Aditya Prasad Date: Tue, 14 Apr 2026 17:36:59 +0530 Subject: [PATCH 08/18] handle exceptions by Ideas provider using try-catch blocks Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .../tutorials/flutter/step-7/+page.markdoc | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc index ec6a15e3d2..cebcff6f02 100644 --- a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -74,14 +74,20 @@ class _HomeState extends State { SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: () { - ideasProvider.add({ - 'userId': user.$id, - 'title': _titleController.text, - 'description': _descriptionController.text, - }, user.$id); - _titleController.clear(); - _descriptionController.clear(); + onPressed: () async { + try { + await ideasProvider.add({ + 'userId': user.$id, + 'title': _titleController.text, + 'description': _descriptionController.text, + }, user.$id); + _titleController.clear(); + _descriptionController.clear(); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); + } }, style: Styles.button, child: Text("Submit"), From 60309e3419cac8844e7e6c57055c65848151c3d8 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 17:46:39 +0530 Subject: [PATCH 09/18] fix ID names for database and table for consistency --- src/routes/docs/tutorials/flutter/step-6/+page.markdoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc index c8e5b74005..e3a8c0d994 100644 --- a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -12,11 +12,12 @@ To store your ideas, you need to create a database first. 1. Go to the Databases section in your Appwrite Console 2. Click *Create Database* -3. Give it a name and ID. For this tutorial, we'll use `Ideas Tracker` as the name and `ideas_tracker` as the ID. +3. Give it a name and ID. For this tutorial, we'll use `Ideas Tracker` as the name and `default` as the ID. 4. You'll need to remember the database ID as you'll need it later. # Create table {% #create-table %} In Appwrite, data is stored as a table of rows. Create a table in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas. +Make sure to the remember the table ID, for this tutorial, we'll use `ideas_tracker` as the table ID. {% only_dark %} ![Create project screen](/images/docs/tutorials/dark/idea-tracker-table.png) From 07b5349607b661d6810715f7cb34e949d87fe71a Mon Sep 17 00:00:00 2001 From: Aditya Prasad Date: Tue, 14 Apr 2026 17:51:26 +0530 Subject: [PATCH 10/18] mounted check before using context after async gap Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- src/routes/docs/tutorials/flutter/step-4/+page.markdoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc index cd8209d136..aac2882281 100644 --- a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -213,6 +213,8 @@ class _LoginState extends State { _passwordController.text, ); } catch (e) { + } catch (e) { + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(e.toString())), ); From 07654cc732be5e91c1e15b7db09db8c8d15d5fd7 Mon Sep 17 00:00:00 2001 From: Aditya Prasad Date: Tue, 14 Apr 2026 17:51:59 +0530 Subject: [PATCH 11/18] mounted check before using context after async gap Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- src/routes/docs/tutorials/flutter/step-7/+page.markdoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc index cebcff6f02..3f0deff779 100644 --- a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -84,6 +84,8 @@ class _HomeState extends State { _titleController.clear(); _descriptionController.clear(); } catch (e) { + } catch (e) { + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(e.toString())), ); From f1f57cda0e054111e7c3a657e82ef6968185c864 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 18:10:59 +0530 Subject: [PATCH 12/18] fix double catch blocks by removing empty catch block --- src/routes/docs/tutorials/flutter/step-4/+page.markdoc | 1 - src/routes/docs/tutorials/flutter/step-7/+page.markdoc | 1 - 2 files changed, 2 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc index aac2882281..721dc94545 100644 --- a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -212,7 +212,6 @@ class _LoginState extends State { _emailController.text, _passwordController.text, ); - } catch (e) { } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc index 3f0deff779..39971a2047 100644 --- a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -83,7 +83,6 @@ class _HomeState extends State { }, user.$id); _titleController.clear(); _descriptionController.clear(); - } catch (e) { } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( From c07eb4e57551642685aa69b9e46e92bd186e000a Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 18:19:02 +0530 Subject: [PATCH 13/18] fix error handling and UI inconsistencies --- .../docs/tutorials/flutter/step-4/+page.markdoc | 1 + .../docs/tutorials/flutter/step-6/+page.markdoc | 3 +-- .../docs/tutorials/flutter/step-7/+page.markdoc | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc index 721dc94545..912867cc44 100644 --- a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -231,6 +231,7 @@ class _LoginState extends State { _passwordController.text, ); } catch (e) { + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(e.toString())), ); diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc index e3a8c0d994..ed8ebf68f2 100644 --- a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -15,7 +15,7 @@ To store your ideas, you need to create a database first. 3. Give it a name and ID. For this tutorial, we'll use `Ideas Tracker` as the name and `default` as the ID. 4. You'll need to remember the database ID as you'll need it later. -# Create table {% #create-table %} +## Create table {% #create-table %} In Appwrite, data is stored as a table of rows. Create a table in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas. Make sure to the remember the table ID, for this tutorial, we'll use `ideas_tracker` as the table ID. @@ -116,7 +116,6 @@ class IdeasProvider extends ChangeNotifier { _ideas.removeWhere((item) => item.$id == id); notifyListeners(); - await init(); debugPrint('Idea removed'); } catch (e) { rethrow; diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc index 39971a2047..732d4036b4 100644 --- a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -132,7 +132,18 @@ class _HomeState extends State { const SizedBox(height: 8), ElevatedButton( - onPressed: () => isOwner? ideasProvider.remove(idea.$id) : null, + onPressed: () async { + if (isOwner) { + try { + await ideasProvider.remove(idea.$id); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); + } + } + }, style: isOwner ? Styles.button : Styles.disabledButton, child: const Text( "Remove", From f72b517ca6f486c09d8924644853e091ff9c44ad Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 18:27:08 +0530 Subject: [PATCH 14/18] change description to match disable remove button if user not owner --- src/routes/docs/tutorials/flutter/step-7/+page.markdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc index 732d4036b4..694eccd0e8 100644 --- a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -7,8 +7,8 @@ step: 7 Using `IdeasProvider` you can now display the ideas on the page and create a form to submit new ideas. -If an idea is submitted by the logged-in user, a remove button will be displayed to remove the idea. -While this check uses the user ID to determine the render logic, permissions set in step 6 will be used to enforce +If an idea is submitted by the logged-in user, remove button will be enabled to remove the idea. +While this check uses the user ID to determine the disable/enable logic, permissions set in step 6 will be used to enforce that only the owner of the idea can remove it. Overwrite the contents of `lib/screens/home.dart` with the following: From f181e6c3c96c71023b7f755148e1d1f6c9729665 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 18:39:38 +0530 Subject: [PATCH 15/18] move isOwner check inside Button function outside to disable button click when null --- .../tutorials/flutter/step-7/+page.markdoc | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc index 694eccd0e8..efe0fd9ff8 100644 --- a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -132,18 +132,16 @@ class _HomeState extends State { const SizedBox(height: 8), ElevatedButton( - onPressed: () async { - if (isOwner) { - try { - await ideasProvider.remove(idea.$id); - } catch (e) { - if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(e.toString())), - ); - } + onPressed: isOwner ? () async { + try { + await ideasProvider.remove(idea.$id); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); } - }, + } : null, style: isOwner ? Styles.button : Styles.disabledButton, child: const Text( "Remove", From 0960f134fe1cfff366a538e03596097117c962c8 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 22:50:56 +0530 Subject: [PATCH 16/18] replace Styles.disabledButton by adding disabledBackgroundColor to Styles.button & remove appwrite version in appwrite flutter sdk installation --- src/routes/docs/tutorials/flutter/step-2/+page.markdoc | 2 +- src/routes/docs/tutorials/flutter/step-4/+page.markdoc | 8 ++------ src/routes/docs/tutorials/flutter/step-7/+page.markdoc | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/routes/docs/tutorials/flutter/step-2/+page.markdoc b/src/routes/docs/tutorials/flutter/step-2/+page.markdoc index 679c7f8d16..c6099f7e50 100644 --- a/src/routes/docs/tutorials/flutter/step-2/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-2/+page.markdoc @@ -19,7 +19,7 @@ cd ideas_tracker Install the Flutter Appwrite SDK and provider. ```sh -flutter pub add appwrite:17.0.0 +flutter pub add appwrite flutter pub add provider ``` diff --git a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc index 912867cc44..cc03fcfa06 100644 --- a/src/routes/docs/tutorials/flutter/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-4/+page.markdoc @@ -134,12 +134,8 @@ class Styles { static ButtonStyle button = ElevatedButton.styleFrom( backgroundColor: Colors.pinkAccent, foregroundColor: Colors.white, - padding: EdgeInsets.symmetric(vertical: 12, horizontal: 24), - ); - - static ButtonStyle disabledButton = ElevatedButton.styleFrom( - backgroundColor: Colors.grey, - foregroundColor: Colors.white, + disabledBackgroundColor: Colors.grey, + disabledForegroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 12, horizontal: 24), ); } diff --git a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc index efe0fd9ff8..548acf3b42 100644 --- a/src/routes/docs/tutorials/flutter/step-7/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-7/+page.markdoc @@ -142,7 +142,7 @@ class _HomeState extends State { ); } } : null, - style: isOwner ? Styles.button : Styles.disabledButton, + style: Styles.button, child: const Text( "Remove", ), From f25924edaf20cc2c0e0cb7f1507c11ecf86f060a Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 22:59:58 +0530 Subject: [PATCH 17/18] fix typo: idea.userId to userId --- src/routes/docs/tutorials/flutter/step-6/+page.markdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc index ed8ebf68f2..72d3c05cf6 100644 --- a/src/routes/docs/tutorials/flutter/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-6/+page.markdoc @@ -124,7 +124,7 @@ class IdeasProvider extends ChangeNotifier { } ``` -Notice that new ideas have the added permission `Permission.write(Role.user(idea.userId))`. +Notice that new ideas have the added permission `Permission.write(Role.user(userId))`. This permission ensures that only the user who created the idea can modify it. Remember to add the `IdeasProvider` to your `main.dart` file. From 5f13426f8ce3c46b7659d1f5c759cd10cd3cea80 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 14 Apr 2026 23:07:07 +0530 Subject: [PATCH 18/18] specify to replace in appwrite endpoint --- src/routes/docs/tutorials/flutter/step-3/+page.markdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/docs/tutorials/flutter/step-3/+page.markdoc b/src/routes/docs/tutorials/flutter/step-3/+page.markdoc index faac3eb739..9414fbff41 100644 --- a/src/routes/docs/tutorials/flutter/step-3/+page.markdoc +++ b/src/routes/docs/tutorials/flutter/step-3/+page.markdoc @@ -43,7 +43,7 @@ Find your project's ID in the **Settings** page. Create a new file `lib/appwrite.dart` to hold our Appwrite related code. Only one instance of the `Client()` should be created per app. -Add the following code to it, replacing `` with your project ID. +Add the following code to it, replacing `` with your project ID and `` with your project region. ```dart import 'package:appwrite/appwrite.dart';