This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Sample apps created on the @platform

Sample @platform apps, from simple to complex

at_hello_world:

Demonstrates some of the most common verbs and methods that can be found on the @platform. If you are new to the @platform, start here!

Learn more

at_chats:

Demonstrates peer-to-peer chatting capabilities and just how easy it is to implement into any project!

Learn more

at_cookbook:

Demonstrates some of the more complex verbs and methods that the @platform has to offer. Similar to at_chats in the sense of sending information to separate @signs. At_cookbook builds upon this allowing you to send objects such as recipes to separate @signs!

Learn more

@mosphere:

Currently our most complex application on the @platform! This app sends entire files from one @sign to another with end-to-end encryption! We highly recommend having a strong understanding of all of the common verbs and methods of the @platform before dissecting @mosphere!

Learn more

1 - at_hello_world

Not sure where to start? Take a look at our at_hello_world app to see how some of the common verbs and methods of the @protocol are applied and implemented.

If you have gone through the steps of setting up the virtual environment and wish to learn how to implement a few common verbs and methods of the @platform, we highly recommend walking through the at_hello_world application.

Below, you can see a small demonstration of how the at_hello_world application works.

at_hello_world

tl;dr

Overview too long for you? Watch the Tyler Time episode that covers this instead!

Overview of the at_hello_world app

“at_hello_world” is a bit of a misnomer. Beyond the fact that this app does a lot more than printing “Hello World!” on the console of your IDE, if you lift its hood, you’ll find a tremendous amount of stuff going on (tracing all the functions called in the server_demo_service.dart file will get you several abstraction layers deep into the @protocol!). By no means do you have to understand everything that is happening behind the scenes in the at_hello_world application, but it’ll definitely help to grasp the basics.

Just like the rest of the @platform, all of our demo applications are open source. Feel free to download the at_hello_world code from our GitHub repository here.

Upon downloading and booting up the app on an emulator, you are met with the Login screen where you can login after selecting a testable @sign to authenticate with from the dropdown menu.

After successfully authenticating, you will be taken to the home screen where you will see three separate boxes which house the three main functons of the at_hello_world app.

The Three Main Functions of the at_hello_world app

Update

The update section, as you see within the code, actually only involves a single if statement (to ensure information is actually passed) and four lines of code within it.

  _update() async {
    if (_key != null && _value != null) {
      AtKey pair = AtKey();
      pair.key = _key;
      pair.sharedWith = atSign;
      await _serverDemoService.put(pair, _value);
    }
  }

Above is the entirety of the update function! The “pair” variable in the first line of the if statement is something that you will see in all @platform applications. The @protocol interprets keys as AtKey objects, which have several attributes like “key”, “metadata”, and “sharedBy” that help the backend understand what to do with them.

In the code snippet above, we are calling the “key” attribute to add a string that will be paired to a value and the “sharedWith” attribute to define with whom we are sharing this AtKey-value pair. If we’d like to store “hello” and “world” on @bob’s secondary server, we would set pair.key to “hello” and pair.sharedWith to “@bob”. This latter detail may be a bit odd: Bob is sharing this AtKey object with himself so that he can access it! You can see use cases of sharing AtKey-value pairs with other @signs in the at_cookbook sample app.

Following up with our “hello” and “world” example, the final step in the _update function is to call the “put” verb from the _serverDemoService object with “pair” as the AtKey instance and “world” as the corresponding value (which should just be a string). When the verb executes successfully, you will have put the “hello” and “world” key-value pair into @bob’s secondary server!

While this is certainly an impressive feat (after all, these few seemingly unassuming lines of code uniquely encrypts the key-value pair to @bob’s secondary server and makes it persist), we’re still limited by the fact that we can’t retrieve key-value pairs from a secondary server. In the @protocol, retrieving key-value pairs takes two steps: scanning the relevant secondary server for AtKey objects, and getting the value associated with a scanned AtKey object.

Scan

Now that we’ve “put” information on our secondary, we’d like to retrieve that key in order to read the information associated with it. In order to display values that we’ve stored, we first need to scan a secondary server for relevant AtKey objects (i.e. those that belong to the “namespace” of the application) and retrieve the values corresponding to those AtKeys.

_scan() async {
    List<AtKey> response = await _serverDemoService.getAtKeys(
      sharedBy: atSign,
    );
    if (response.length > 0) {
      List<String> scanList = response.map((atKey) => atKey.key).toList();
      setState(() => _scanItems = scanList);
    }
  }

The only @protocol verb we’re calling in this snippet is “getAtKeys()”. This verb is an incredibly robust function that can gather and sort AtKeys on a secondary server based on things like who shared those AtKeys (the “sharedBy” optional argument) and regular expressions (e.g. the namespace of the application). Because the at_hello_world app is a special case where we only share AtKey objects with ourselves, we can simply call getAtKeys with the “sharedBy” argument set to our own @sign (i.e. widget.atSign). This will return a List of the AtKey objects we want.

Once you get that List of AtKeys, you’re pretty much finished! In the at_hello_world app, because we want to display keys as strings, we call the “map” method on the List of AtKeys to create a new list that just contains the “key” attribute of each AtKey object. In the last line of code, we call “setState” so that the app loads the newly populated list of keys in the DropdownButton widget.

Lookup

An individual on the at_hello_world app is given a list of keys, and they select one (_lookupKey) to find its corresponding value. How do we do this? The answer lies in the _lookup function of the HomeScreen class:

 _lookup() async {
    if (_lookupKey != null) {
      AtKey lookup = AtKey();
      lookup.key = _lookupKey;
      lookup.sharedWith = atSign;
      String response = await _serverDemoService.get(lookup);
      if (response != null) {
        setState(() => _lookupValue = response);
      }
    }
  }

In the first line of the if statement, we are creating a new AtKey object called “lookup.” The reason for this is we need a dummy AtKey object that can be passed into the @protocol for looking up the correct value. For the at_hello_world app, this dummy AtKey just needs its “key” and “sharedWith” attributes populated before it can be passed into the “get” verb.

By the way, if you think creating a copy of an AtKey object is a hassle, that’s totally valid! In a typical @platform application, you have the _scan and _lookup functions merged to some degree so that you can just pass in the AtKey objects we retrieved with the “getAtKeys” verb to the “get” verb. The point of having two separate functions in the at_hello_world project is to define the “scanning” and “getting” steps more concretely.

“Get” is a very straightforward verb: it gets the value corresponding to a specified AtKey.

Once we retrieve the value paired with the “lookup” AtKey, all that’s left is calling “setState” to display the “_lookupValue” on screen.

2 - at_chats

Demonstrates the peer to peer chatting capabilities and just how easy it is to implement into any project!

The at_chat_flutter widget offers a messaging experience that is unique to the @platform. In a traditional messaging application, your texts are stored in a remote database and the person you’re pinging pulls the texts from there (a bit unsettling if you think about it). Of course, there is no such thing as a remote database in the @platform, so we had to be a bit clever coming up with a messaging scheme. In a nutshell, your text messages are not “sent” but rather “shared” with another @sign. All your texts are stored securely in your secondary server and never leave; if you decide to send a message to someone, that person is given permission to view that text via the notify verb. You can see use cases of the notify verb in the at_cookbook sample app.

Below, you can see a small demonstration of how the at_chats application works.

at_chats

tl;dr

Overview too long for you? Watch the Tyler Time episode that covers this instead!

Overview of the at_chats app

Although this messaging dynamic might sound a bit involved, set up is quite easy! To get a feel for using the “Chatting” widget, it’s best to follow along with the at_chats demo application.

The general flow of all @platform widgets is onboarding an @sign => initializing the service object => creating the actual widgets. Assuming we’ve already onboarded an @sign, let’s look at the steps to initialize our chat service:

(The following code snippets are taken directly from at_chats. While there will be explanations, don’t worry too much about all the variables!)

getAtSignAndInitializeChat() async {
 /// In the at_chats app, the onboarded @sign is displayed at the top
 /// of the Second Screen. We set that @sign to [currentAtSign].
 String currentAtSign = await clientSdkService.getAtSign();
 /// Set [activeAtSign], which is the variable that gets displayed, to
 /// [currentAtSign] using setState(() {}).
 setState(() {
   activeAtSign = currentAtSign;
 });
 /// Initialize a List of Strings called [allAtSigns] that we will
 /// eventually display in the dropdown on the Second Screen. Here, we
 /// simply pull an existing List from the at_demo_data dependency.
 List<String> allAtSigns = at_demo_data.allAtsigns;
 /// We want to remove the [activeAtSign] from this List because we
 /// can't chat with ourselves!
 allAtSigns.remove(activeAtSign);
 /// Again, call setState(() {}) to assign [allAtSigns] to the
 /// variable [atSigns] that will be used in the dropdown widget.
 setState(() {
   atSigns = allAtSigns;
 });
 /// This is the only at_chat_flutter related function!
 /// initializeChatService takes in an AtClientImpl instance, the
 /// currently onboarded @sign, and the root domain for this project.
 /// As its name suggest, this function will prepare the chat service
 /// for us.
 initializeChatService(
     clientSdkService.atClientServiceInstance.atClient, activeAtSign,
     rootDomain: MixedConstants.ROOT_DOMAIN);
}

Because getAtSignandInitializeChat() is an initialization function, it is best to call it in the initState() function at the top of the _SecondScreenState class. The only other thing we need to do before calling the “Chatting” widget is deciding who we’d like to chat with.

setAtsignToChatWith() {
 /// This function is as simple as calling the setChatWithAtSign()
 /// function from the at_chat_flutter dependency with
 /// [chatWithAtSign] passed in! [chatWithAtSign] is simply the @sign
 /// that a user selects from the dropdown on the screen.
 setChatWithAtSign(chatWithAtSign);
}

We won’t want to call setAtsignToChatWith() in initState() because the function won’t know which @sign we’re communicating with until the individual selects it from the dropdown widget. Instead, it makes the most sense to place this function in the button (FlatButton for the at_chats app) that determines the navigation to the next screen. For at_chats, clicking the “Chat options” FlatButton will check to make sure that the “chatWithAtSign” variable is populated before it calls setAtsignToChatWith() and switches the “showOptions” variable to true, which allows the individual to see the two options for viewing the chatbox.

Now, for the moment of truth: once we’ve initialized the chat service, how do we create the actual chat screen? In most tutorials, you’ll probably be guided through a UI-heavy demo of different chatbox components and pairing a backend service. With the @platform, however, it’s really just one line of code:

(This snippet is directly from third_screen.dart in the at_chats project!)

class _ThirdScreenState extends State<ThirdScreen> {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: Text('Chat')),
     /// You can simply set the body parameter of Scaffold widget
     /// to the ChatScreen widget from the at_chat_flutter dependency!
     body: ChatScreen(
       /// Optional parameters to customize your ChatScreen widget.
       /// You can find the full list of parameters in our Github
       /// under the at_widgets repository.
       height: MediaQuery.of(context).size.height,
       incomingMessageColor: Colors.blue[100],
       outgoingMessageColor: Colors.green[100],
       isScreen: true,
     ),
   );
 }
}

By initializing the chat service and calling the ChatScreen() widget, you can make a fully-functioning one-to-one messaging application! While the ChatScreen widget offers a number of ways to customize your chatbox, if you’d like to build your own widget from scratch, you can use the at_chat_flutter dependency as a basis for creating your personal chat library that works with the @platform.

3 - at_cookbook

This is an intermediate-level @platform based application that uses the verbs we learned in the at_hello_world and at_chats applications to make a working cookbook for the chef inside of us all!

at_cookbook

tl;dr

Overview too long for you? Watch the Tyler Time episode that covers this instead!

Overview of the at_cookbook app

The at_cookbook app allows a testable @sign to create a recipe with a name, description, list of ingredients, and a picture. After doing this, the @sign is capable of sharing this entire recipe with another testable @sign. A dishWidget class is created which encapsulates an individual recipe with each instance. Its parameters are defined below:

class DishWidget extends StatelessWiatdget {
 final String title;
 final String ingredients;
 final String description;
 final String imageURL;
 final String prevScreen;

 DishWidget({
   @required this.title,
   @required this.ingredients,
   @required this.description,
   @required this.imageURL,
   @required this.prevScreen,
});
...

After we pull dishWidgets that exist in a secondary server, we display them on the home screen, where the list of recipes are located.

Now that you have the recipe itself, you’re able to pass this through the notify verb. When an @sign wishes to share a recipe, the _share function will be called. This function is structured as so:

_share(BuildContext context, String sharedWith) async {
 if (sharedWith != null) {
   AtKey lookup = AtKey()
     ..key = widget.dishWidget.title
     ..sharedWith = atSign;

   String value = await _serverDemoService.get(lookup);

   var metadata = Metadata()..ttr = -1;
   AtKey atKey = AtKey()
     ..key = widget.dishWidget.title
     ..metadata = metadata
     ..sharedBy = atSign
     ..sharedWith = _otherAtSign;

   var operation = OperationEnum.update;
   await _serverDemoService.notify(atKey, value, operation);
   Navigator.pop(context);
 }
}

Here, we’re initializing a variable called lookup as an AtKey object (lookup will be our recipe). You’ll notice that we’re defining a couple of the attributes of the AtKey object (mainly the name of the recipe and what @sign is this recipe being shared with).

We would like to “get” the values of our recipe, so we use the get verb from the serverDemoService file of the application. ttr (Time To Refresh), a metadata attribute, is called with a value of -1, which means that we’re confident that the values of our recipe won’t change. Once the recipe has been cached on the secondary server that has received the recipe, it will not need to worry about updating its values at any point.

After passing all of the necessary values such as the metadata, the appropriate @signs, and the type of notification we’d like to send, we simply pass the values through the notify verb!

4 - @mosphere

Makes peer-to-peer encrypted file sharing possible.

In real-time, you can send files across any device regardless of your location — with the added benefit of total privacy. You can fearlessly share contracts, tax information, or other confidential information without worrying about your data being stored on a server in the cloud.

To see a demonstration of @mosphere, refer to the GIF below:

atmosphere-walkthrough

Creating your own @platform data-streaming application

Just like any Flutter app, an @platform application requires a little bit of setup before you can get started. Here are those steps:

  1. Add the service file to your app: You can simply copy this service file from our demo at_hello_world application. These files contain helper methods that allow you to implement @protocol functionality with just a couple lines of code.
  2. Add the configuration file to your app: Again, feel free to copy this from the at_hello_world and @mosphere application. This file contains variables that allow you to use the virtual environment. Make sure that the ROOT_DOMAIN string is set to vip.ve.atsign.zone and you have a unique name for the NAMESPACE of your @pp!
  3. Copy the dependencies from the at_hello_world and @mosphere pubspec.yaml files and put them into your project.

We are super glad that you are beginning your journey as an @dev. We highly recommend that you join our discord dev community for troubleshooting, dev updates, and much more!