diff --git a/content/docs/tutorials/at-dude/3-app_architecture/index.md b/content/docs/tutorials/at-dude/3-app_architecture/index.md
index a44dd9e07..a09d46201 100644
--- a/content/docs/tutorials/at-dude/3-app_architecture/index.md
+++ b/content/docs/tutorials/at-dude/3-app_architecture/index.md
@@ -52,7 +52,7 @@ The controller files will be saved in this folder.
#### Services
-The services fetch data from the network or local storage and returns it to the controllers.
+The services fetch data from the network or local storage and return it to the commands.
In your terminal type:
@@ -65,6 +65,6 @@ The services files will be saved in this folder.
#### Conclusion
-The controllers will call the services to fetch data from the network and then send the fetched data to the model. The views are bound to the model using provider or an alternative. As the models are updated the views will be updated. Alternatively the controller can update the views directly.
+The commands will call the services to fetch data from the network and then send the fetched data to the model. The views are bound to the model using provider or an alternative. As the models are updated the views will be updated. Alternatively the commands can update the views directly.
In the next step we'll explore this further as we implement onboarding on the atPlatform.
\ No newline at end of file
diff --git a/content/docs/tutorials/at-dude/4-onboarding/first_onboard_screen.png b/content/docs/tutorials/at-dude/4-onboarding/first_onboard_screen.png
new file mode 100644
index 000000000..b3864c4a8
Binary files /dev/null and b/content/docs/tutorials/at-dude/4-onboarding/first_onboard_screen.png differ
diff --git a/content/docs/tutorials/at-dude/4-onboarding/index.md b/content/docs/tutorials/at-dude/4-onboarding/index.md
index 4ef55f821..910697b44 100644
--- a/content/docs/tutorials/at-dude/4-onboarding/index.md
+++ b/content/docs/tutorials/at-dude/4-onboarding/index.md
@@ -8,20 +8,20 @@ draft: true # TODO CHANGE THIS TO FALSE WHEN YOU ARE READY TO PUBLISH THE PAGE
order: 4 # Ordering of the steps
---
-In this tutorial, we will build the onboarding screen for the dude app.
+In this tutorial, we will build the onboarding screen for the dude app and modify the default onboarding function to make it compatible with our app architecture.
-With out further ado, lets get back to building the atDude app.
+With out further ado, let's get back to building the atDude app.
-At the end this step our app will look like this,
+At the end of this step our app will look like this,
-{{< image type="page" src="first_screen.png" >}}
+{{< image type="page" src="first_onboard_screen.png" >}}
{{
}}
{{
}}
-Before we get into the onboarding, lets make the UI changes to our app.
+Before we get into the onboarding, let's make the UI changes to our app.
#### Update AppBar
The first thing we will do is change the App bar title to "atDude" in `main.dart`.
@@ -29,24 +29,25 @@ The first thing we will do is change the App bar title to "atDude" in `main.dar
```
MaterialApp(
// * The onboarding screen (first screen)
+ debugShowCheckedModeBanner: false, // New
home: Scaffold(
appBar: AppBar(
- title: const Text('atDude'), # Changed
+ title: const Text('atDude'), // Changed
),
),
);
```
-The next step is to wrap our `ElevatedButton` widget with a Column widget and center its mainAxisAlignment.
+The next step is to wrap our `ElevatedButton` widget with a `Column` widget and center its mainAxisAlignment.
-```
+```dart
MaterialApp(
// * The onboarding screen (first screen)
home: ...
body: Builder(
builder: (context) => Center(
- child: Column( # new
- mainAxisAlignment: MainAxisAlignment.center, # new
+ child: Column( // new
+ mainAxisAlignment: MainAxisAlignment.center, // new
children: [
ElevatedButton(
onPressed: ...
@@ -61,34 +62,36 @@ MaterialApp(
#### Adding IconButton
-Before add an `IconButton` widget with the dude logo as the icon property. We need to add the logo to our project.
+Before we add an `IconButton` widget with the dude logo as the icon property. We need to add the logo to our project.
-In your editor create a new folder called assets and inside the assets folder create a folder name images or type the following in your terminal:
+Type the following in your terminal:
```
mkdir -p assets/images/
+open pubspec.yaml
```
-Open `pubspec.yaml` and add the location of the image folder as shown below.
+Add the location of the image folder as shown below.
```
flutter:
uses-material-design: true
assets:
- .env
- - assets/images/ # new
+ - assets/images/ // new
```
Let's create the `IconButton` as shown below.
-```
+```dart
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- IconButton( # new
- iconSize: 200, # new
- onPressed: () {}, # new
- icon: Image.asset('assets/images/dude_logo.png'), # new
- ), # new
+ // IconButton New
+ IconButton(
+ iconSize: 200,
+ onPressed: () {},
+ icon: Image.asset('assets/images/dude_logo.png'),
+ ),
ElevatedButton(
onPressed: () async {...},
child: const Text('Onboard an @sign'),
@@ -97,28 +100,26 @@ Column(
),
```
-#### Onboarding
+#### Refactoring the Onboard Function
-Before we get started with this section, lets define a few terms:
+Before we get started with this section, let's define a few terms:
[Onboarding](../../../sdk/flutter/onboarding) - The process of activating an atSign and/or authenticating the atSign into its secondary server.
-[atsign](https://atsign.com/what-is-an-atsign/) - An atsign is your digital identity. It ensures that your data is owned and controlled by you. You can pair your device with any app to access but not store your data.
+[atsign](https://atsign.com/what-is-an-atsign/) - An atsign is your digital identity; it ensures that your data is owned and controlled by you. You can pair your device with any app on the atPlatform to access but not store your data.
-To make the onboarding code reusable, we will remove it form the "onboard an @sign" button and move it into an authentication class.
-
-Copy the code inside the `onPressed` anonymous function of the ElevatedButton
+We will make the onboarding code compatible with our architecture by removing it from the “onboard an @sign” button and moving it into an authentication class.
In your terminal type:
```
-mkdir lib/services
touch lib/services/authentication_service.dart
+open lib/services/authentication_service.dart
```
-Open `authentication_service.dart` file and add the following:
+Add the following:
-```
+```dart
class AuthenticationService {
static final AuthenticationService _singleton =
AuthenticationService._internal();
@@ -132,8 +133,368 @@ class AuthenticationService {
The above code creates a [singleton](https://flutterbyexample.com/lesson/singletons) of our class.
-Now we'll create a method called onboard in our `AuthenticationService` class whose content will be the code we copied a few steps above.
+
+Now we'll create an async method called `onboard` in our `AuthenticationService` class. We will then move the contents of the `onboardingResult` variable inside the `ElevatedButton` `onPressed` anonymous function in `main.dart`, to the this method as shown below.
+
+```dart
+class AuthenticationService {
+ ...
+ Future onboard() async {
+ return await AtOnboarding.onboard(
+ context: context,
+ config: AtOnboardingConfig(
+ atClientPreference: await futurePreference,
+ rootEnvironment: AtEnv.rootEnvironment,
+ domain: AtEnv.rootDomain,
+ appAPIKey: AtEnv.appApiKey,
+ ),
+ );
+ }
+}
+```
+
+#### Fixing Undefined name errors
+
+To fix the `Undefined name` errors we need to provide the `AtOnboarding.onboard()`method with a `BuildContext` and the `AtClientPreference` class as shown below.
+
+
+```dart
+import 'package:at_app_flutter/at_app_flutter.dart';
+import 'package:at_client_mobile/at_client_mobile.dart';
+import 'package:at_onboarding_flutter/at_onboarding_flutter.dart';
+import 'package:path_provider/path_provider.dart'
+ show getApplicationSupportDirectory;
+
+class AuthenticationService {
+ ...
+
+ Future onboard() async {
+ var dir = await getApplicationSupportDirectory(); // new
+
+ var atClientPreference = AtClientPreference() // new
+ ..rootDomain = AtEnv.rootDomain //new
+ ..namespace = AtEnv.appNamespace //new
+ ..hiveStoragePath = dir.path //new
+ ..commitLogPath = dir.path //new
+ ..isLocalStoreRequired = true; //new
+
+ return AtOnboarding.onboard(
+ context: context,
+ config: AtOnboardingConfig(
+ atClientPreference: atClientPreference, // new
+ rootEnvironment: AtEnv.rootEnvironment,
+ domain: AtEnv.rootDomain,
+ appAPIKey: AtEnv.appApiKey,
+ ),
+ );
+ }
+}
+```
+
+We Now need access to the BuildContext, We'll create a separate class for this since we'll be reusing our BuildContext outside of the stateful and stateless widgets.
+
+In your terminal type:
+
+```
+touch lib/services/navigation_service.dart
+```
+Add the below code snippet in that file.
+```dart
+import 'package:flutter/material.dart';
+
+class NavigationService {
+ static GlobalKey navKey = GlobalKey();
+
+ static GlobalKey nestedNavKey = GlobalKey();
+}
+```
+```
+touch lib/services/services.dart
+open lib/services/services.dart
+```
+
+Add the below code to use one import statement to import all export files:
+```dart
+export 'authentication_service.dart';
+export 'navigation_service.dart';
+```
+
+In `main.dart` add the navigatorKey to the materialApp as shown below;
+
+```dart
+import 'package:at_dude/services/services.dart'; // new
+...
+Widget build(BuildContext context) {
+ return MaterialApp(
+ // * The onboarding screen (first screen)
+ navigatorKey: NavigationService.navKey, // new
+ home: ...
+ );
+ }
+```
+
+In `navigation_service.dart` add the below code to the `onboard()` method.
+
+```dart
+Future onboard() async {
+ ...
+ return await AtOnboarding.onboard(
+ context: NavigationService.navKey.currentContext!, // new
+ config: ...
+ );
+ }
+```
+
+That's it, our `AuthenticatinService` can reach out to the atPlatform to return our `AtOnboardingResult`. We now need our command to call this service and decide what action to take depending on the returned `AtOnboardingResult`.
+
+#### Base Command
+
+ Remember, our commands contain our application logic; we’ll first create a base command that all other commands will extend from before creating our onboard command In your terminal type:
+
+```
+touch lib/commands/base_command.dart
+open lib/commands/base_command.dart
+```
+
+Create the `BaseCommand` class with the needed imports as shown below:
+
+```dart
+import 'package:provider/provider.dart';
+
+import '../services/services.dart';
+
+abstract class BaseCommand {
+ // Services
+ AuthenticationService authenticationService =
+ NavigationService.navKey.currentContext!.read();
+}
+```
+
+ We need to provide the services to the widget tree in order for `BaseCommand` to successfully read the services from the BuildContext. To achieve this we'll make use of the [provider package](https://pub.dev/packages/provider) as shown below:
+
+```
+flutter pub add provider
+open lib/main.dart
+```
+```dart
+import 'package:provider/provider.dart'; //new
+...
+class _MyAppState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return MultiProvider( //new
+ providers: [Provider(create: (c) => AuthenticationService.getInstance())] // new
+ child: MaterialApp(),
+ )
+ }
+}
+```
+
+To learn more about state management using provider check out this [flutter article](https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple)
+
+#### Onboard Command
+
+Now that we're all set, let's create our Onboard Command. This class method will contain the instructions required to onboard on the atPlatform. In your terminal type:
+
+```
+touch lib/commands/onboard_command.dart
+open lib/commands/onboard_command.dart
+```
+
+Add the below code:
+
+```dart
+import 'package:at_dude/commands/base_command.dart';
+class OnboardCommand extends BaseCommand {
+ Future run() async {
+ var onboardingResult = await authenticationService.onboard();
+
+ }
+}
+```
+Our Commands will only have one method called `run()`. This command return a `Future`.
+
+Move the remaining code inside the `ElevatedButton onPressed` anonymous function in `main.dart`, to the `run()` method of the `OnboardCommand()` class as show below:
+
+```dart
+import 'package:at_dude/commands/base_command.dart';
+import 'package:at_dude/services/navigation_service.dart'; // new
+import 'package:at_onboarding_flutter/at_onboarding_result.dart'; // new
+import 'package:flutter/material.dart'; // new
+
+import '../home_screen.dart'; // new
+
+class OnboardCommand extends BaseCommand {
+ Future run() async {
+ var onboardingResult = await authenticationService.onboard();
+
+ // Everything Below New
+ var context = NavigationService.navKey.currentContext!;
+ switch (onboardingResult.status) {
+ case AtOnboardingResultStatus.success:
+ Navigator.push(
+ context, MaterialPageRoute(builder: (_) => const HomeScreen()));
+ break;
+ case AtOnboardingResultStatus.error:
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ backgroundColor: Colors.red,
+ content: Text('An error has occurred'),
+ ),
+ );
+ break;
+ case AtOnboardingResultStatus.cancel:
+ break;
+ }
+ }
+}
+```
+
+If `authenticationService.onboard()` return `AtOnboardingResultStatus.success` we navigate to the HomeScreen, if it returns `AtOnboardingResultStatus.error` we display a `Snackbar` on the screen.
+
+We will be using the `Snackbar` widget often so let's extract it into its own method.
+
+#### Creating Snackbar Method
+
+In your terminal type
+
+```
+touch lib/views/widgets/snackbars.dart
+open lib/views/widgets/snackbars.dart
+```
+
+Add the below code
+
+```dart
+import 'package:at_dude/services/navigation_service.dart';
+import 'package:flutter/material.dart';
+
+class Snackbars extends StatelessWidget {
+ const Snackbars({Key? key}) : super(key: key);
+
+ static void errorSnackBar({
+ required String errorMessage,
+ }) {
+ ScaffoldMessenger.of(NavigationService.navKey.currentContext!)
+ .showSnackBar(SnackBar(
+ content: Text(
+ errorMessage,
+ textAlign: TextAlign.center,
+ ),
+ backgroundColor:
+ Theme.of(NavigationService.navKey.currentContext!).errorColor,
+ ));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+```
+
+We created a class that extends `StatelessWidget`. This class contains a static void method named `errorSnackBar` that accepts an `errorMessage`.
+
+This method will save us from calling `ScaffoldMessenger.of(context).showSnackBar()` every time we want so show a snackbar.
+
+Let's replace our snackbar part of `OnboardCommand.run()` method as shown below:
+
+```dart
+...
+
+import 'package:at_dude/views/widgets/snackbars.dart'; // new
+
+class OnboardCommand extends BaseCommand {
+ Future run() async {
+ ...
+ switch (onboardingResult.status) {
+ ...
+ case AtOnboardingResultStatus.error:
+ Snackbars.errorSnackBar(errorMessage: 'An error has occurred'); // new
+ break;
+ case AtOnboardingResultStatus.cancel:
+ break;
+ }
+ }
+}
+```
+
+All done!
+
+#### Cleaning up main.dart
+
+Now we just have to clean up `main.dart`.
+Remove the code below from main.dart:
+
+
+```dart
+import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; // remove
+
+import 'package:path_provider/path_provider.dart' // remove
+ show getApplicationSupportDirectory; // remove
+
+Future loadAtClientPreference() async { // remove
+ var dir = await getApplicationSupportDirectory(); //remove
+
+ return AtClientPreference() // remove
+ ..rootDomain = AtEnv.rootDomain // remove
+ ..namespace = AtEnv.appNamespace // remove
+ ..hiveStoragePath = dir.path // remove
+ ..commitLogPath = dir.path // remove
+ ..isLocalStoreRequired = true; // remove
+ // TODO
+ // * By default, this configuration is suitable for most applications
+ // * In advanced cases you may need to modify [AtClientPreference]
+ // * Read more here: https://pub.dev/documentation/at_client/latest/at_client/AtClientPreference-class.html
+}
+
+class _MyAppState extends State {
+ // * load the AtClientPreference in the background // remove
+ Future futurePreference = loadAtClientPreference(); // Remove
+ ...
+}
+```
+
+Add the below code to `main.dart`:
+
+```dart
+@override
+ Widget build(BuildContext context) {
+ return MultiProvider(
+ ...
+ child: MaterialApp(
+ ...
+ home: Scaffold(
+ appBar: ...,
+ body: Builder(
+ builder: (context) => Center(
+ child: Column(
+ ...
+ children: [
+ ...,
+ ElevatedButton(
+ onPressed: () async {
+ await OnboardCommand().run(); // new
+ },
+ child: const Text('Onboard an @sign'),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+```
+
+Run your flutter app and everything should work as before.
+
+```
+flutter run
```
+#### Conclusion
+Following an architecture is like having an organized room, it takes extra effort but it makes navigating and understand your codebase a whole lot easier and cleaner.
-```
\ No newline at end of file
+Now that we've completed the onboarding process, in the next step we'll complete the first screen by adding a reset atsign button and it's functionalities.
diff --git a/content/docs/tutorials/at-dude/5-reset-atsign/final_onboard_screen.png b/content/docs/tutorials/at-dude/5-reset-atsign/final_onboard_screen.png
new file mode 100644
index 000000000..7aefe6af5
Binary files /dev/null and b/content/docs/tutorials/at-dude/5-reset-atsign/final_onboard_screen.png differ
diff --git a/content/docs/tutorials/at-dude/5-reset-atsign/index.md b/content/docs/tutorials/at-dude/5-reset-atsign/index.md
new file mode 100644
index 000000000..1fc5db41b
--- /dev/null
+++ b/content/docs/tutorials/at-dude/5-reset-atsign/index.md
@@ -0,0 +1,281 @@
+---
+layout: codelab
+
+title: "Reset atsign" # Step Name
+description: How to reset any app built on the atPlatform # SEO Description for this step Documentation
+
+draft: true # TODO CHANGE THIS TO FALSE WHEN YOU ARE READY TO PUBLISH THE PAGE
+order: 5 # Ordering of the steps
+---
+
+In this tutorial, we will complete the onboarding screen for the dude app and implement the reset app functionality.
+
+At the end of this step our app will look like this,
+
+{{< image type="page" src="final_onboard_screen.png" >}}
+
+{{
}}
+{{
}}
+
+#### Creating the Texts util class
+
+The first thing we will do is create a utility class that will store our texts. This will make it easy to update our texts across out app without having to search and replace all occurrence of the string.
+
+ Follow the steps below:
+
+```
+mkdir lib/utils
+touch lib/utils/texts.dart
+open lib/utils/texts.dart
+```
+
+```dart
+class Texts {
+ static const String atDude = 'atDude';
+ static const String onboardAtsign = 'Onboard an atsign';
+ static const String resetApp = 'Reset App';
+}
+```
+
+Replace the string 'Reset App' with it's equivalent `static const` for the `ResetAppButton` widget as shown below
+
+```dart
+...
+@override
+ Widget build(BuildContext context) {
+ return ElevatedButton(
+ onPressed: () {},
+ child: const Text(Texts.resetApp), // Changed
+ );
+ }
+```
+
+Let us make similar changes in `main.dart` as shown below:
+```dart
+...
+MaterialApp(
+ ...
+ home: Scaffold(
+ appBar: AppBar(
+ title: const Text(Texts.atDude), // Changed
+ ),
+ body: Builder(
+ builder: (context) => Center(
+ child: Column(
+ ...
+ children: [
+ ...
+ ElevatedButton(
+ ...
+ child: const Text(Texts.onboardAtsign), // changed
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+```
+
+#### Adding Reset Functionality to Authentication Service
+
+We'll repeat the same pattern of the onboarding functionality for the reset app functionality. In your terminal type:
+
+```
+open lib/services/authentication_service.dart
+```
+
+Now we'll create a method called `reset` and `getAtOnboardingConfig` in our `AuthenticationService` class and refactor the `onboard` method.
+
+```dart
+class AuthenticationService {
+ ...
+ // new method
+ AtOnboardingConfig getAtOnboardingConfig({
+ required AtClientPreference atClientPreference,
+ }) =>
+ AtOnboardingConfig(
+ atClientPreference: atClientPreference,
+ rootEnvironment: AtEnv.rootEnvironment,
+ domain: AtEnv.rootDomain,
+ appAPIKey: AtEnv.appApiKey,
+ ); // new
+
+ Future onboard() async {
+ return await AtOnboarding.onboard(
+ context: context,
+ config: getAtOnboardingConfig(atClientPreference: atClientPreference), // changed
+ );
+ }
+}
+```
+
+Here we simply moved `AtOnboardingConfig` into it's own method so we can reuse it on our reset method we're going to create below:
+
+```dart
+import 'package:at_onboarding_flutter/screen/at_onboarding_reset_screen.dart';
+...
+Future reset() async {
+ var dir = await getApplicationSupportDirectory();
+
+ var atClientPreference = AtClientPreference()
+ ..rootDomain = AtEnv.rootDomain
+ ..namespace = AtEnv.appNamespace
+ ..hiveStoragePath = dir.path
+ ..commitLogPath = dir.path
+ ..isLocalStoreRequired = true;
+
+ return AtOnboarding.reset(
+ context: NavigationService.navKey.currentContext!,
+ config: getAtOnboardingConfig(atClientPreference: atClientPreference),
+ );
+ }
+```
+
+`AtOnboarding.reset` allows the user to remove any atsign that onboard on the app before. This allows the user to onboarding with another atsign.
+
+#### Reset Command
+
+Now that we're all set, lets create our Reset Command. This class method will contain the instructions required to remove any atsign associated with our app.
+
+In your terminal type:
+
+```
+touch lib/commands/reset_command.dart
+open lib/commands/reset_command.dart
+```
+
+Add the below code:
+
+```dart
+import 'package:at_dude/commands/base_command.dart';
+class ResetCommand extends BaseCommand {
+ Future run() async {
+ var resetResult = await authenticationService.reset();
+
+ }
+}
+```
+Now that we have our variable `resetResult`. Let's decide what we'll do depending on the `resetResult`.
+
+```dart
+import 'package:at_dude/commands/base_command.dart';
+import 'package:at_dude/commands/onboard_command.dart';
+
+import 'package:at_onboarding_flutter/screen/at_onboarding_reset_screen.dart'; //new
+import 'package:flutter/material.dart'; // new
+
+
+
+class ResetCommand extends BaseCommand {
+ Future run() async {
+ var resetResult = await authenticationService.reset();
+
+
+ // Everything Below New
+
+ switch (resetResult) {
+ case AtOnboardingResetResult.success:
+ OnboardCommand().run();
+ break;
+
+ case AtOnboardingResetResult.cancelled:
+ break;
+ }
+ }
+}
+```
+
+If `authenticationService.reset()` return `AtOnboardingResetResultStatus.success` we call `'OnboardCommand().run()` to initiate the onboarding process, if it returns `AtOnboardingResetResultStatus.cancelled` we do nothing.
+
+
+#### Completing the first screen
+
+Now we just have to update the UI in `main.dart` to allow the user to reset the app.
+
+Edit `main.dart` as shown below:
+
+```dart
+@override
+ Widget build(BuildContext context) {
+ return MultiProvider(
+ ...
+ child: MaterialApp(
+ ...
+ home: Scaffold(...),
+ body: Builder(
+ builder: (context) => Center(
+ child: Column(
+ ...
+ children: [
+ IconButton(...),
+ ElevatedButton(
+ ...
+ child: const Text(Texts.onboardAtsign),
+ ),
+ // new
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 12,
+ vertical: 8,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: const [
+ Expanded(
+ child: Divider(
+ color: Colors.black,
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.symmetric(horizontal: 12.0),
+ child: Text(
+ 'Or',
+ textAlign: TextAlign.center,
+ ),
+ ),
+ Expanded(
+ child: Divider(
+ color: Colors.black,
+ ),
+ ),
+ ],
+ ),
+ ), // new end
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+```
+
+We add a divider to create separation between the two buttons.
+
+lets add the reset atsign button as shown below:
+
+```dart
+import 'package:at_dude/commands/reset_command.dart'; // new
+...
+Padding(...)
+// new
+ElevatedButton(
+ onPressed: () async {
+ await ResetCommand().run();
+ },
+ child: Text(Texts.resetApp),
+) // new end
+```
+
+Run your flutter app and everything should work perfectly.
+
+Go ahead and reset the app
+
+```
+flutter run
+```
+#### Conclusion
+
+Well done, you've made it this far. In the next step we will start building our Send Dude Screen.
diff --git a/content/docs/tutorials/at-dude/6-send_dude-screen-app-bar/index.md b/content/docs/tutorials/at-dude/6-send_dude-screen-app-bar/index.md
new file mode 100644
index 000000000..ef487af6b
--- /dev/null
+++ b/content/docs/tutorials/at-dude/6-send_dude-screen-app-bar/index.md
@@ -0,0 +1,372 @@
+---
+layout: codelab
+
+title: "Send Dude Screen AppBar" # Step Name
+description: Creating the UI of the send dude screen # SEO Description for this step Documentation
+
+draft: true # TODO CHANGE THIS TO FALSE WHEN YOU ARE READY TO PUBLISH THE PAGE
+order: 5 # Ordering of the steps
+---
+
+In this tutorial, we will build the AppBar of send dude screen.
+
+
+
+
+At the end of this step our app will look like this,
+
+{{< image type="page" src="send_dude_screen_app_bar.png" >}}
+
+{{
}}
+{{
}}
+
+
+#### Creating the AppBar
+The first thing we will do is create our send dude screen dart file with the AppBar and the widgets and properties it needs.
+
+ Follow the steps below:
+
+```
+
+touch lib/views/screens/send_dude_screen.dart
+open lib/views/screens/send_dude_screen.dart
+```
+
+```dart
+import 'package:flutter/material.dart';
+import '../../utils/texts.dart';
+
+class SendDudeScreen extends StatefulWidget {
+ SendDudeScreen({Key? key}) : super(key: key);
+ static String routeName = 'sendDudeScreen';
+
+ @override
+ State createState() => _SendDudeScreenState();
+}
+
+class _SendDudeScreenState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ backgroundColor: Colors.transparent,
+ foregroundColor: Colors.transparent,
+ shadowColor: Colors.transparent,
+ title: const Text(
+ Texts.sendDude,
+ style: TextStyle(color: Colors.black),
+ ),
+ actions: const [AtsignAvatar()],
+
+ ),
+ );
+ }
+}
+```
+
+We have our stateful widget with an `appBar` but we neither have a `Texts.sendDude` constant nor the `AtsignAvatar()`. Lets create them:
+
+```
+open lib/utils/texts.dart
+```
+
+```dart
+...
+class Texts {
+ ...
+ static const String sendDude = 'Send Dude';
+}
+```
+
+#### AtsignAvatar
+
+Let us create `AtsignAvatar` as shown below:
+
+```
+touch lib/views/widgets/atsign_avatar.dart
+open lib/views/widgets/atsign_avatar.dart
+```
+```dart
+import 'dart:typed_data' show Uint8List;
+import 'package:flutter/material.dart';
+
+class AtsignAvatar extends StatefulWidget {
+ const AtsignAvatar({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _AtsignAvatarState();
+}
+
+class _AtsignAvatarState extends State {
+ Uint8List? image;
+ String? profileName;
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ child: CircleAvatar(
+ backgroundColor: Colors.transparent,
+ child: image == null
+ ? const Icon(
+ Icons.person_outline,
+ color: Colors.black,
+ )
+ : ClipOval(child: Image.memory(image!)),
+ ),
+ onTap: () {},
+ );
+ }
+}
+```
+
+Basically we have a `CircleAvatar` whose child is a profile image or an person_outline icon if no image is available. We need to add the functionality that will check for the atsign contact details.
+
+##### Profile Data
+```
+mkdir lib/data
+touch lib/data/profile_data.dart
+open lib/data/profile_data.dart
+```
+
+```dart
+import 'dart:typed_data' show Uint8List;
+
+class ProfileData {
+ ProfileData({required this.name, required this.profileImage});
+
+ final String? name;
+ final Uint8List? profileImage;
+}
+```
+This class will contain the name and profile image data we'll get from the ContactService class provided to us for free from the [at_contacts_flutter package](https://pub.dev/packages/at_contacts_flutter).
+
+##### Contacts Model
+We'll now create our contacts model that will store all our contacts information needed in our app.
+
+```
+touch lib/models/contacts_model.dart
+open lib/models/contacts_model.dart
+```
+
+```dart
+import 'package:flutter/material.dart';
+
+import '../data/profile_data.dart';
+
+class ContactsModel extends ChangeNotifier {
+ late ProfileData _profileData;
+
+ ProfileData get profileData => _profileData;
+
+ set profileData(ProfileData profileData) {
+ _profileData = profileData;
+ notifyListeners();
+ }
+}
+```
+Our profile data extends `ChangeNotifier`, this will allow us to `notifyListeners()` of changes made to `profileData`.
+
+We now have to add our `ContactModel` as a `ChangeNotifierProvider` and then add it to `BaseCommand`.
+
+```
+open lib/main.dart
+```
+
+```dart
+...
+@override
+ Widget build(BuildContext context) {
+ return MultiProvider(
+ providers: [
+ Provider(create: (c) => AuthenticationService.getInstance()),
+ ChangeNotifierProvider(create: (c) => ContactsModel()), // new
+ ],
+ child: MaterialApp(...),
+ );
+```
+
+```
+open lib/commands/base_command.dart
+```
+```dart
+...
+import '../models/contacts_model.dart';
+
+abstract class BaseCommand {
+ // Services
+ AuthenticationService authenticationService =
+ NavigationService.navKey.currentContext!.read();
+
+ // Models
+ ContactsModel contactsModel = NavigationService.navKey.currentContext!.read();
+```
+
+#### Contact Details Command
+We'll now create our Contact Details Command. We don't have to create our `ContactService` since this is provided to us from the [at_contacts_flutter package](https://pub.dev/packages/at_contacts_flutter).
+
+
+In your terminal type:
+
+```
+flutter pub add at_contacts_flutter
+touch lib/commands/contact_details_command.dart
+open lib/commands/contact_details_command.dart
+```
+
+```dart
+import 'package:at_client_mobile/at_client_mobile.dart';
+import 'package:at_contacts_flutter/services/contact_service.dart';
+import 'package:at_dude/commands/base_command.dart';
+import 'package:at_dude/data/profile_data.dart';
+
+class ContactDetailsCommand extends BaseCommand {
+ Future run() async {
+ final contactService = ContactService();
+
+ ContactService()
+ .getContactDetails(
+ AtClientManager.getInstance().atClient.getCurrentAtSign(), null)
+ .then(
+ (value) {
+ contactsModel.profileData =
+ ProfileData(name: value['name'], profileImage: value['image']);
+ return null;
+ },
+ );
+ }
+}
+```
+
+We use the `getCurrentAtSign()` method to get the current atsign, then use the `getContactDetails()` to get the name and profile image of the atsign. We then return the `profileData` to our `contactsModel`.
+
+
+
+#### Completing the AtsignAvatar widget
+
+Now we just have what we need to complete the AtsignAvatar Widget.
+
+```
+open lib/views/widgets/atsign_avatar.dart
+```
+
+```dart
+class _AtsignAvatarState extends State {
+...
+@override
+ void initState() {
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
+ await ContactDetailsCommand().run();
+ });
+ super.initState();
+ }
+}
+```
+
+To run an async method inside `initState` we need to call the method inside `WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {});`
+
+We can now delete the `image` and `profileName` variables since the data is now inside our `ContactsModel.profileData` property. Let's use the power of provider to access this property.
+
+```dart
+class _AtsignAvatarState extends State {
+ Uint8List? image; // Delete this
+ String? profileName; // Delete this
+
+ @override
+ void initState() {
+ ...
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ child: CircleAvatar(
+ backgroundColor: Colors.transparent,
+ child: context.watch().profileData.profileImage == null
+ ? const Icon(
+ Icons.person_outline,
+ color: Colors.black,
+ )
+ : ClipOval(
+ child: Image.memory(
+ context.watch().profileData.profileImage!)),
+ ),
+ onTap: () {},
+ );
+ }
+}
+```
+
+We accessed the `profileData` generated by calling `ContactDetailsCommand().run()` through `context.watch().profileData`
+
+#### Cleaning up our SendDudeScreen
+
+Let's fix our "The method 'AtsignAvatar' isn't defined" error by simply importing `AtsignAvatar` widget:
+
+```
+open lib/views/screens/send_dude_screen.dart
+```
+
+```dart
+import '../widgets/atsign_avatar.dart'; // new
+
+class SendDudeScreen extends StatefulWidget {
+ ...
+}
+
+class _SendDudeScreenState extends State {
+ ...
+}
+```
+
+#### Navigating to the SendDudeScreen
+
+We can now navigate to our SendDudeScreen now that our send Dude Screen AppBar is completed.
+
+```
+open lib/commands/onboard_command.dart
+```
+
+```dart
+...
+class OnboardCommand extends BaseCommand {
+ Future run() async {
+ ...
+ switch (onboardingResult.status) {
+ case AtOnboardingResultStatus.success:
+ Navigator.popAndPushNamed(context, SendDudeScreen.routeName); // new
+ break;
+ ...
+ }
+ }
+}
+```
+
+Instead of navigating to the `HomeScreen` we now navigate to `SendDudeScreen` but we also need to add this screen as route.
+```
+open lib/main.dart
+```
+
+```dart
+import 'package:at_dude/views/screens/send_dude_screen.dart';
+
+...
+
+class _MyAppState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return MultiProvider(
+ providers: ...,
+ child: MaterialApp(
+ ...
+ routes: {
+ SendDudeScreen.routeName: ((context) => SendDudeScreen()), // new
+ },
+ home: Scaffold(...),
+ ),
+ );
+ }
+}
+```
+#### Conclusion
+
+We're all done. In the next step we will start working on the bottom navigation bar.
diff --git a/content/docs/tutorials/at-dude/6-send_dude-screen-app-bar/send_dude_screen_app_bar.png b/content/docs/tutorials/at-dude/6-send_dude-screen-app-bar/send_dude_screen_app_bar.png
new file mode 100644
index 000000000..c7af21707
Binary files /dev/null and b/content/docs/tutorials/at-dude/6-send_dude-screen-app-bar/send_dude_screen_app_bar.png differ
diff --git a/content/docs/tutorials/at-dude/_index.md b/content/docs/tutorials/at-dude/_index.md
index e5c59b531..dd5f397d3 100644
--- a/content/docs/tutorials/at-dude/_index.md
+++ b/content/docs/tutorials/at-dude/_index.md
@@ -2,8 +2,8 @@
layout: codelab-list # The layout for a codelab list (think of this as a title page for the code lab)
title: "atDude Tutorial" # Title of the codelab
-lead: Learn how to build on the atPlatform # Description of the codelab
-description: Learn how to build on the atPlatform
+lead: Learn how to build production app on the atPlatform # Description of the codelab
+description: Learn how to build production app on the atPlatform
doneLink: /tutorials # Where to send them if they press "Done" at the end of the Codelab
exitLink: /tutorials # Where to send them if they press "Exit Codelab"
diff --git a/hugo_stats.json b/hugo_stats.json
index e7fb2b111..75725e069 100644
--- a/hugo_stats.json
+++ b/hugo_stats.json
@@ -289,6 +289,8 @@
"a-assignment-of-static-ip",
"a-register-domain-name-with-aws",
"a-register-domain-name-with-gcp",
+ "adding-iconbutton",
+ "adding-reset-functionality-to-authentication-service",
"anchor-tag-a",
"arrow-back",
"assets",
@@ -301,10 +303,12 @@
"atonboardingresult",
"atplatform",
"atprotocol",
+ "atsignavatar",
"atserver",
"b-assignment-of-domain-name-to-your-static-ip",
"b-create-cloud-dns-zone",
"b-setting-up-billing",
+ "base-command",
"block-list",
"building-layouts",
"buttonlink",
@@ -316,14 +320,24 @@
"cardgroup",
"cardshowcase",
"cardsocial",
+ "cleaning-up-maindart",
+ "cleaning-up-our-senddudescreen",
"cloning-the-client",
"commands",
"compile-jar",
+ "completing-the-atsignavatar-widget",
+ "completing-the-first-screen",
"conclusion",
"configuration-parameters",
"contact",
+ "contact-details-command",
"contact-us",
+ "contacts-model",
"content",
+ "contributing-to-the-developer-site",
+ "creating-snackbar-method",
+ "creating-the-appbar",
+ "creating-the-texts-util-class",
"definition",
"deleting-a-publickey-example",
"deleting-a-selfkey-example",
@@ -337,6 +351,7 @@
"example-4",
"example-5",
"featured-tutorials",
+ "fixing-undefined-name-errors",
"free-atsigns",
"frontmatter",
"getting-a-publickey-example",
@@ -366,6 +381,7 @@
"monitor",
"monitor-verb",
"need-more-information-read-the-following",
+ "navigating-to-the-senddudescreen",
"next-step",
"notification",
"notify-list",
@@ -376,6 +392,8 @@
"notifyremove-verb",
"objects",
"offcanvasDoks",
+ "offcanvasDoksLabel",
+ "onboard-command",
"open-source-contributions",
"other-services",
"overview",
@@ -385,13 +403,17 @@
"polymorphic-data",
"prerequisite",
"previous-step",
+ "profile-data",
+ "putting-a-privatehiddenkey-example",
"putting-a-publickey-example",
"putting-a-selfkey-example",
"putting-sharedkey-example",
+ "refactoring-the-onboard-function",
"reference",
"registration-cli",
"related-resources",
"requirements",
+ "reset-command",
"root-secondary",
"root-server",
"search",