App Development: User Guide
Introduction to ZOAPIIO Apps
Apps are end-user web and mobile apps built over ZOAPIIO. App development process is optimized over what you may be used to if you have developed front-end or full-stack apps before.
Let us start by looking at the high-level process.
Conventional approach ...
proceeds roughly as follows -
- The project effort and hence teams are divided into one each for the admin app, backend, web app, and mobile app. Besides, there are support teams like devops and QA. If you are using native development for Android and iOS, then you are one more team in your project.
- The backend team designs and builds the functional APIs using a suitable server-side language or framework.
- The front-end design generally starts with UI wireframes using tools like figma.
- The front-end development starts by getting a UI structure generated from the wireframe tool and then proceeds to programming of the interactivity and server integration.
- The backend and front-end development proceeds simultaneously as each of these feedback changes into the other.
- After a few sprint-cycles the final app starts to take shape and then refined over a period using the same process.
... is sub-optimal
There are several inefficiencies in the conventional approach that ZOAPIIO addresses, such as-- Conventional approach requires multiple and advanced skill sets. These are expensive and suffer for manpower turnover problems.
- Multiple teams require coordination - additional time and effort is wasted in managing the touch points.
- Larger teams tend to lower the overall productivity.
- There is limited opportunity for leveraging code generation. It is mostly limited to CRUD on the server-side and application skeleton on the client side.
- The generation benefit is available only once at the start of the project. Enhancements must be programmed manually.
- Functionality enhancements impact all teams and turnaround time is high because of interdependencies. Further, the overall timelines are determined by the slowest team.
The ZOAPIIO Way
As we will see in the rest of this document, architecture allows you to break away from convention and adopt a fresh approach addressing most of the pain points. The following are the key characteristics of building apps with -
- approach is intent based. Most of the time you specify your intent, and the system provides the implementation.
- If you have already worked on server-side APIs with , you would observe that the abstraction principle of is nothing but intent based programming. The same principle is extended to front-end Apps.
- follows integrated and code-generation driven approach. This means that impedance issues between backend and front-end are eliminated, generation coverage is much higher, and advantages of code generation are available throughout the life of the project - not just at the beginning.
- fully supports "Server-driven" or "backend-driven" mobile UI - a hot new approach that is gaining traction where the app UI can be updated from the server without having to compile and push your app with every change.
- follows the function first approach. This means that you can initiate and UI/UX design process, but the development is not dependent on it. The development focuses on functionality building, the UX design is planted over it later.
- The admin app is almost entirely generated.
- front-end app structure is same for web and mobile (Android as well as iOS, through flutter). Effectively, it is write-once and deploy anywhere.
- Skill requirements and efforts are dramatically reduced. The developers only need to learn one IDE. In terms of programming, only JavaScript, jQuery, and entry-level Flutter/dart skills are needed, which can be easily acquired on the project.
- is a build and host platform - effectively eliminating the need for a devops team.
Getting Started
The process of building web and mobile apps in is the same. This is because the build process relies heavily on code generation. The difference comes when you start to add custom interactivity - for mobile app, you will use the dart programming language and JavaScript for the web. The programming model is identical for web and mobile and only the underlying language is different - you can replicate the same code with only minor changes to syntax.
Creating the app design is easy because uses function first approach. Your design is simply a skeleton for page layout, the appearance is addressed later. To address the web and mobile deployment targes of your app -
- You can create the complete design, clone it and then build interactivity for each app independently, or
- If your app for web and mobile is identical, then you can build a single app and add Web/mobile interactivity programming separately in the same app.
Before you proceed
Every technology has its strengths and use case scenario where it shines. While is a generic build and runtime environment for internet based solutions for web and mobile, its strengths are best leveraged in a commercial business type of application. Before proceeding, do see the comments below-
- A business application is one that has a significant server-side role; there are user transactions, which require monitoring, and perhaps fulfilment and customer service. is ideally suited for this, because it takes an integrated view of the problem and can deliver maximum value.
- An application which is heavy on user-interactivity, and has a smaller server-side role, will have limited benefit from the platform.
- For applications which have significant server-side role but are also heavy on user interactivity, we advise to use for building the server and server APIs. The front-end should be built using conventional methods (while still using ZOAPIIO services).
- The platform offers no advantage to building gaming apps.
Mobile app strategy
The mobile app development landscape today, unfortunately, is fragmented and non-standard. In an ideal world, the web technology, which is already standardized, would work on mobile platforms as well. Bridging solutions exist, but none solves the problem completely - there are trade-offs and choices involved. Before you get down to building the mobile app on ZOAPIIO, take a moment and think about your mobile app needs.
- A web-app (written using HTML/JS/CSS) can be compiled for mobile platforms using technologies like Cordova and PhoneGap. However, the resulting application has inferior UI performance compared to the native apps. If your app users are unlikely to be affected by slight drop in performance, this may be the best option. ZOAPIIO Web app can be compiled using Cordova.
- PWA is gaining popularity as an alternative to native apps. See if you can drop the mobile app in favor of the PWA. ZOAPIIO supports PWA as well.
- Lastly, ZOAPIIO lets you build your mobile app in platform neutral Flutter SDK. This will require more effort and time compared to other options, but you will get native apps for Android and iOS.
- You can also consider starting off with a Cordova wrapped mobile app and delaying full graduation to native.
The admin application
A web or mobile app, without an admin control, is of minimal utility. A large part of business functionality consists of provisioning content and policies for the proper functioning of the app. Some common situations are-
- Inventory of goods and services sold for an eCommerce app.
- Content and advertisements for social media apps.
- Management of third-party contracts for content insourced from suppliers.
- CRM - managing user complaints and feedback.
- Policies - management control on application behaviour.
Think about and document the admin control requirements of your app. You will build the admin application using the Wizard and your database design will be a part of it.
More on Mobile
There are some parts of the mobile app development that need to be addressed outside of ZOAPIIO, in your Cordova or Flutter project.
App environment
Before the app is put on the user's screens, it is required to have properly configured icons for different screen resolutions and configurations. The app may also have a splash screen, a visual to fill the short amount of time it takes before your app opens. You must create these as per the recommended procedure for your environment.
Signing and publishing
Both Google and Apple have a process for listing the app in their on-line stores. They will require you to have an account with them, digitally sign the app and have a verification/review process before the app gets listed on the store. You must go through these steps with the respective vendor.
Versions and dependencies
When building your Cordova or Flutter you may encounter errors depending upon the configuration you have used. You could encounter problem related to versions of your libraries, SDKs and dependencies. You will find solutions to these problems from the Internet search. Most are simple to fix, and you will wise up after some experience.
Accessing device ecosystem
Having an app is a great opportunity to interact with the users' devices and other apps on their device. Both Cordova and Flutter provide means of doing this. You should explore such opportunities and incorporate such features in your app.
Design your app
Consider your application as composed of three functional parts-
- The pages in the application, their layout, composition, data source and data binding. WebIDE is an integrated IDE for backend and frontend. This allows you to assign data source APIs and individual data-element bindings at design time. System manages the data retrieval and rendering transparently.
- Navigation - a central control to launch pages. System manages the navigation bars and basic navigation in your application. You can, additionally, control the navigation programmatically as well.
- Sign-up and registration functionality. You do not treat sign-up and sign-in pages as application pages - this functionality is managed by the system.
app is a collection of pages. | Each page is a hierachical composition of views . |
Get started by documenting, in plain English or by using a diagramming tool, the list of pages in your application and the layout of each.
The programming model
will create a functioning flutter or HTML/JS application from the app abstraction that you define. The app is started and managed by the runtime. You will complement the basic app with your custom program stubs for-
- User Interactivity. You will need to respond to user actions in the app beyond the standard functionality of the input controls. All of the interactivity handling in the form of responding to events, which you configure with the controls when building the app.
- Custom Controls. There may be certain type of input controls that doesn't support out of box. You can provide your own custom controls programmed in HTML/JS and Flutter respectively. Almost all input types are supported natively, so you will require this is advanced cases only.
- Styling Overrides. The basic app comes with minimum styling and is designed to support the function. You may want to override the styling of the controls to match your UI style standard.
- Other overrides. You can override the entire rendering of a view with your custom HTML/JS or Flutter code respectively. Default rendering is sufficient for most cases, this is an advanced scenario and is rarely needed.
- Computational support. If your app requires specialized computational support, the corresponding JS or Flutter libraries can be included in your project.
The global $.app Object
In both web and mobile app programming, a global object is available to your program stubs named $.app. This is your handle to access anything in the application. Every action you take in the application programmatically will be a method call on this object.
The app Context
The app context is a global object maintained in $.app which can be used to communicate between your application and the runtime. The object itself is a JavaScript object for Web and of the generic object type Map<String,dynamic> in flutter.
Pages, Views, and Elements
hides the details of internal objects and classes; and exposes the abstracted view of the application. The contents of the pages, views, and elements in the app are accessed using their names. When building the apps, remember to give each view/element a simple and recognizable name.
Building your application
From a high-level view, you will build the app following these steps.
- Build the admin application. This is the first step because your database is created in this step. This does not require any programming because the Wizard will generate it for you, besides creating the APIs needed for the app registration and sign-in functions in your app. Refer to the Wizard User Guide for details.
- Write the business APIs. The data that your app uses will be stored on the server and the app will use the APIs to access it. Refer to the WebIDE User Guide for details. This is not mandatory, the data for your app can also be served from canned JSON files stored with the app itself.
- Build your app. This is the focus of this guide - the front-end apps.
Steps 2 and 3 above are not strictly separate steps. You will work on these simultaneously and switch back and forth as necessary. Both are a part of the same project and built using the same WebIDE - working on these two steps together is quite natural.
- Create a folder on your desktop to hold all project files. The admin app requires a configuration JSON and supporting customization files. Save local copies in this folder.
- Your working project is directly deployed on the server, without need for a local save - but it is recommended to locally save the project periodically. Use the same project folder to save this.
- It is a good practice to name the second project by adding "-X" suffix (for extension) to the name of the base project.
- For ease of database management, define all your tables in the base app configuration - even the ones that do not require admin functionality (entities with type "transaction" are not managed by the admin app).
The WEB-APPLICATION Node
The front-end applications are added to the extension project. Multiple front-end applications can be created in the same project. However, multiple extension projects can be created for each front-end application.
A related node in your schema - APP-COMMONS - contains those parts of your application which are reused across applications. Examples are styles, components and assets.
Start by adding the WEB-APPLICATION node to your schema. Even though the name suggests it is for web apps, but it is used for both web and mobile apps. If your web and mobile apps have similar structure, you can first define a structure and then clone it - this will save you the effort of defining the structure again. Once the basic structure is in place, you can develop the two independently. The structure, for our purpose, consists of-
- The pages (APP-PAGE node) and their layout.
- The layout is specified as a top-down breakdown of the viewable space. The broken-down components in the layout are called views (APP-VIEW node).
- The lowest level element in the layout, that holds an elementary piece of information is called an Element (APP-ELEMENT node).
- The high-level navigation - the one controlled by actions on the navigation bars.
- The data binding for each view and element.
- The data binding also follows a hierarchical breakdown closely mapped with the data structure that feeds data to the view.
- each view can have its own data source (API) or it can inherit data from its parent view.
Spend some time closely examining the WEB-APPLICATION properties screenshot below. You will define the application-level behaviour and controls here. This also gives you an idea of the amount of work automatically handles for you.
![]() |
![]() |
WEB-APPLICATION properties . |
Notes:
- Your app will have a URL even though it may be a mobile app.
- During the development stage, the app pages, layout, and navigation can be tested using this URL.
- This URL serves as the gateway to serve other APIs used in the application or data management.
- Having a single URL for the entire application helps in hosting the application behind a firewall and the external URL (for web-apps) can be a different convenient name.
- You can configure the details of the sign-in module - this is managed by the system. The associated foundation project contains the server-side database and APIs to support this functionality.
- The web application can be optionally rendered as PWA, which can also be configured.
Image, Data and HTML assets
Add the image assets in your application using APP-ASSETS under APP-COMMONS, and then APP-IMAGE nodes under that. Make sure the images have appropriate size and resolution for the purpose you intend to use them for. Note that the mobile app (flutter) IDE requires certain app icon images to be provided separately for Android and iOS - these are not a part of the image assets here.
Your project may also require static data files - these should be added as APP-DATA nodes under APP-ASSETS. Only JSON formatted data files are supported, make sure the data is valid JSON - use a validation tool if required.
Your front-end apps can use locally served HTML content (rendered as WebView in Mobile app). Create your HTML content under APP-ASSETS as APP-CONTENT node. The APP-CONTENT can be hierarchical allowing entire web applications to served locally from the app. The WebIDE also allows you a short-cut to import the web app from a ZIP file.
Pages and Layout
You add APP-PAGE nodes under the WEB-APPLICATION, for each page in your app. Leave the sign-in and sign-up pages out because system manages it for you. Include all functional pages, whether launched from the menu nav bars or invoked from app interaction.
Create the layout of each by hierarchically adding APP-VIEW and APP-ELEMENT nodes inside the page. The intermediate views in the page will segment the area into rows, columns or grids and the lowest level views will consist of elements - where you will display the data and accept user inputs. provides a wide selection of element types for different needs.
As you define the page layout, build and then run it on the phone emulator, you may encounter errors from the Flutter layout engine. We have some tips-
- Start with a layout that only focusses on dividing the page into views and content for each view. Do not worry about sizing, alignments and scrolling.
- Gradually use view options like 'padding', 'detailed style', 'scroll', 'expand', 'center' etc. to improve the presentation.
- On a page, use the Scroll option in only one view. The easiest is to use it on the top view only. If you decide to use scroll on an inside view, do not set scrolling for the top view.
- For complex scrolling control, get the page to work correctly first, then you can try with adding 'scroll' to multiple views. Revert if something breaks.
- Use of 'expand' option is most likely to throw your layout into errors because there are restrictions on its use by Flutter. When you first build your layout, do not use 'expand' at all.
- Gradually, add 'expand' option to views and revert if something breaks.
- Sometimes, a few trial-and-error cycles are needed in getting the layout right.
- In extreme cases, if you cannot get a layout of your desire, you can resort to native Flutter implementation of the view.
Data binding
Your application pages will include data that is supplied from outside - from the server API, for instance. You incorporate this in your pages using Data binding. Server APIs are not the only source of data for your application, it can be canned data provided as a data asset file (JSON) - see APP-DATA, or the app context (in-memory app data).
The page rendering is fundamentally differently for HTML (Web) apps and Flutter (Mobile) apps. tries to create a common model for both, but you should be aware of differences because the underlying implementations are very different. In HTML, an element on the page can be directly referenced and changed and the program has full flexibility in doing so.
Flutter, however, only allows views (Widgets) to be updated from their state data. Data binding is used to implement this behaviour. For your mobile app, any view that you want to dynamically change at run-time (user interactivity), must be bound to a data element (it can be structure). If the data to be bound does not originate from a server API, it can be bound to a local data source.
Data Source, Root and Binding
Routing data to your app pages is controlled through these properties. Data source and root properties are applicable only for a view and binding applies to both views and elements. The rules are as follows:
- All data sources (API, canned, or app context) used in your app must declare their structure in the project schema.
- When defining the data source a view, you must identify the node in the schema that contains the definition of the source structure. This is essential, otherwise cannot manage the data inside the page.
- A view can have its own data source or it can be bound to a sub-structure from its parent.
- Multiple data sources can be a part of the same page.
- Data source or binding is not mandatory for a view. For instance, a view may not require any external data.
- If a view does not explicitly binds itself to a source or parent's data sub-structure, it inherits the same binding as its parent.
- If bound, a view must be bound to an object in the source - it cannot be an elementary data item.
- Elements can have bindings for its value, label, help and herotag properties. Each can be a fixed literal value or a binding to an elementary item in its parent's view.
As noted before - the app context is a simple hash, mapping names to values (possibly objects). Data source property can reference data from the app context. Please note that you must programmatically populate the app context with correct values before pages containing the referenced data is called. Data source can take one of the following forms-
- A name in format appcontext:name. The name is looked up in the app context and that value (which should an object) is used as source.
- A name starting with http:, or https:. It is taken to an external API which is called to fetch the source.
- A name starting with /. It is taken to a service API and is called to fetch the source.
- Any other name. It is taken to be the name of a data asset (See APP-DATA) in the project, the contents of which are used as the source.
Additionally, with an API URL, app contenxt substitutions can be used to include parameters to the API using the {{}} notation. E.g. http://host.com/products/{{productid}}, where productid is picked up from the app context and substituted for {{productid}}, before calling the API.
App navigation
In app, you can have a default navigation plan for the app and you can optionally override it for each page. The navigation plan consists of buttons that will appear on the navigation bars located at the top and/or bottom of the user screen.
You add "APP-NAVIGATION" tag to your application and on pages where you need a different navigation. Within that you add the list of commands and the location of the command button. The button can be positioned on the navigation bar itself (left or right) or on the dropdown menu that is launched from one of the navigation bars. There is great deal of flexibility on how you decide to structure the navigation controls. A command can be placed on both the top and bottom bar, if required. The dropdown menus themselves are available on both the nav bars.
You can attach one of the following actions with each command in the navigation -
- Transition to another named page.
- Launch the named page as a modal popup with a close button. This can be used to display alerts and information messages.
- Login/Logout function. The label for this button is automatically set based on the current logged in status of the user. The command then allows the user to login or logout.
- Programmed handling. In this case, the system does not take any action and you must handle the event programmatically. See events.
Data routing
manages data inputs from the user and automatically makes it available to the API services that are bound to the views. System also automatically renders the views from the API output based on the data bindings.
- All input fields on a page are captured as a flat set of "Name-Value" tuples.
- These values are passed as standard POST HTTP parameters to the API calls in the page that is invoked from the page.
- This works when the page invocation is automatic (in response to a Submit) or programmatic from custom code provided by you.
Server-driven UI
This is a relatively new trend in mobile app development to deal with the challenges associated with pushing your app to your existing users.
- With every push to the play store, there is a publishing process that can take a few days.
- Even after the app is in the play store, it can take many days - even weeks - before the app is updated for all users. You will have to deal with your users running different versions of your app.
- The problem gets further compounded with bugs in your app. If a buggy update gets into circulation, it could take many days before it can be flushed out via an update. This could even affect your business.
In Server-driven mobile UI, you push the description of the page composition via an API from the server, and the app uses that description to render itself.
Building Server-driven mobile UI is a breeze with . No extra steps are required, you simply flag parts of your application that you want server-driven and manages the rest. Pages, views, assets, and even navigation commands can be flagged for server rendering. There are some rules, though-
- You can flag an entire page as Server-driven. This allows you to add completely new pages to your app on-the-fly. There is a caveat here, though. The page cannot have any Flutter/dart code in it, for instance, to handle custom interactivity. The reason is obvious - the flutter code needs to be compiled into the app and cannot be pushed dynamically.
- If a page was static when it was pushed to the play store, you can still override it by changing to Server-driven.
- In pages that are not Server-driven, you can flag specific views to be Server-driven. The advantage of this is that you can have Flutter/dart code that is pushed along with the page to the app, but the content, layout, rendering can be altered on-the-fly.
- App Assets can also be flagged as Server-driven - such assets are not baked into the app, are fetched by the app dynamically.
- App Navigation commands can also be flagged as Server-driven. You can add additional commands to the navbars, and override existing ones using this. Please note that adding a command of type "Programmed" requires a handler to be present in your app.
User interactivity
Besides the App navigation, the users will interact with the app by clicking/tapping on elements on the page. In HTML and JavaScript, event handlers can be attached to elements even after they are created. In Flutter, however, the event handler must be provided at the time of creating the element. provides a common model for Web and Mobile apps for letting users interact with your app. This is done through events. The $.app object that we have seen before, manages these events.
Event routing
The 'fire' and 'listen' methods are used for this. When you define an element on the page (WebIDE), you also mention the events you are interested in, from that element. Then you can take action when these events occur.
- For elements of type 'button', the event 'pressed' is implied and is same for both web and mobile apps (for consistency) - you do not need to mention it in your list.
- For element of type 'submit', the event 'submit' is fired and handled within the system. The default submit behaviour is to validate the form and if there are no validation errors, the control is passed to the next page along with all the form fields.
- If you want a different behaviour - such as additional validations - use the type 'button' and listen to the 'pressed' event.
The data that is passed at the time of 'fire' is passed to handler mentioned in the 'listen'. Normally, you will not need to fire the events in your program because the system does it automatically based on the events that you have mentioned in the element definition. The event names are the ones that HTML uses - E.g., click, doubleclick, mousever etc. These names are also same for both web and mobile for consistency - internal implementation will use the appropriate equivalent.
You may use the 'fire' function if you require custom events. There is no restriction on the names of the events, you can use your own custom event names and trigger/handle them programmatically.
Function | Web App (JS/jQuery) | Mobile App (Dart/Flutter) | ||
---|---|---|---|---|
The global UI object | $.app | $.app | ||
Listen to an event | $.app.listen(eventname, pagename, elementname, handler) | $.app.listen(eventname, pagename, elementname, handler); | ||
Trigger (fire) an event | $.app.fire(eventname, pagename, elementname, handler, data) | $.app.fire(eventname, pagename, elementname, handler, data); | ||
Event processing in front-end. |
Event handling
As already mentioned, you handle events though the listen function of the $.app object. An event is uniquely identified as the combination of event name, the page name, and the element name. It is important to note that only one listener can be attached to an event. If you attempt to attach another listener (I.e. call the listen on the same event), the previous one is removed.
So, where should be listening code be placed? For web apps, there is only one place, which is the PAGE-SCRIPT attached to the page. For web apps, although you can override the view layout with HTML code, no JavaScript can be placed there. You must put all listen commands in the PAGE-SCRIPT, there is one for every page.
For mobile apps, you can override the view layout using Flutter code. Besides overriding the _buildWidget method, you can also add initState method to the widget. You can add other widget lifecycle methods there too. The body of the initState method is the right place to listen to events and handle them.
Updating the user view
Updating the user view is simple and straight forward in HTML/JavaScript/jQuery. You can reference any element by its Id, Class or Name attribute and modify its content.
For Mobile apps, rebuild method of $.app object is used. You will recall from our earlier discussion that any view that you need to programmatically update must be bound to some data (server API or local data asset). This is because Flutter requires that data to be a part of the Widget state.
Dart is a strongly typed language, although you can use untyped variables and structures. The use of strong typing allows for error detection at the time of compilation and leaves you more confident that the code will not blow up at runtime - though the chances are that it still will, for other reasons. takes advantage of the strong typing feature and generates data classes for you based on the data schema you have provided. When a view is rendered, an object of the mapped data class is passed to it - it is not a generic Dart map, but object of a class.
If you are updating a view, you must update this object before the rebuild, otherwise the view will remain unchanged because the underlying data will remain same. The state data for the Widget corresponding to the view can be updated by providing an object of Dart Map<String,dynamic> type. This object should only contain the changes to the original object, which itself was created from the JSON data from its source.
The last parameter to the fire method is this object. The changes from this object are first applied to the state object and then the rebuild of the view is performed. You should examine the generated data classes to know what fields are present in the object.
An example of the rebuild usage is given below -
$.app.listen('click', 'CALCULATOR', 'CBUTTON', (var w) { print('CBUTTON Click'); Text t=w as Text; Calculator calc=$.app.state['calc'] as Calculator; String val=t.data ?? ''; var result=calc.key(val); $.app.fire('rebuild', 'CALCULATOR', 'CALCTOP', {'value': result}); });
Manipulating the page view
The views and elements on your app pages get their behaviour and looks from the configuration you provide in the WebIDE. Certain default behaviour is applied to the input elements.
- When user interacts with these elements, events are dispatched for you to action them in your program.
- When navigating between pages, the values entered by the user on a page is made available to the API services on the next page.
At times you may want to modify the configuration of the input element when handing user interactivity. E.g. you may want to disable input on a field based on some condition, or may want to alter the look (display properties like style, size, label etc.) at run time. You may also want to change the data displayed on the page in response of some event.
You will use the rebuild method of the $.app object for this. See the $.app Object reference for details.
Mobile app examples-
// Disable input on Element $.app.rebuild('page-name', 'element-name', {'inputdisabled': true}); // Set counter (property in the bound object) to 2. $.app.rebuild('page-name', 'view-name', {'counter': 2}); // Increment counter by 1 - by passing patchdata as a function. $.app.rebuild('page-name', 'view-name', (viewdata) { viewdata['counter']++; });
Web app example-
// Disable input on Element. element is the DOM node. $.app.rebuild(element, {'inputdisabled': true});
Styling the app
uses a function first development approach. This means that when developing the app, you will focus on getting the functionality and page content hierarchy right. The final rendering for web and mobile will require a complex set of functions provided by jQuery/Bulma CSS and Flutter respectively. does not attempt to abstract the fine nuances of application aesthetics. Chances are that the application that you get as the first output will be only nice looking. The formatting controls in WebIDE are designed to make the application look presentable, which may or may not be the final desired presentation.
There is where you will use the custom view layout feature.
allows you to override the underlying HTML/Flutter code for rendering at view level. You must source the skills of professional app designers to get your app to look good. Entry-level Flutter/HTML skills are needed to incorporate the Custom layouts in your app.
- Typically, you will override the rendering at the lowest (leaf) level view.
- You should look at the HTML/Flutter code that has been generated by the system first. Then clone it and apply changes.
- The generated code will also show you what structure is bound to the view and how to access its sub-elements when creating your custom rendition.
$.app Object reference
Web app (JS) WIP
$.app method | Return Type | Parameters | Description | |
---|---|---|---|---|
fire | Fire an event | |||
getAppContext | ||||
getData | Fetch the data from an URL | |||
listen | Register a listener for an event | |||
loadPage | Transition to the named page | |||
patchAppContext | Update the app context | |||
popupPage | Open the named page as a popup (dialog) | |||
$.app JS reference (INCOMPLETE) |
Mobile app (Flutter)
$.app method | Return Type | Parameters | Description | |
---|---|---|---|---|
fire | void |
String eventtype, String pagename, String target, dynamic data |
Fire an event. Event names are generic and the apps can use any arbitrary name. System fires some fixed event (E.g. nav, pressed, click, rebuild). Apps can fire system events also. The target of the event is identified by the pagename and the target (View or Element). Finally, with the event arbitrary data can be passed which is delivered to the listen handler. | |
formdata | String? |
String pagename, String target, String value |
Set/Get the values in form data. Form data is data that will be sent to the server when the page is submitted. When an input element on the page is changed, this value is updated immediately. If you are writing a custom component, you should make this call to update the form data when its value changes. target is the name of the element and value is the new value. To query the value, pass null for the value. |
|
getAppContext | dynamic |
String name |
Get an entry from the app context | |
getData | Future |
String url, Map<String,dynamic>? postdata, Map<String,String>? headers, |
Fetch the data from an URL. postdata and headers are optional and named parameters. The method returns a Future<dynamic> object. When the future completes, it resolves to the data received from the API. | |
listen | void |
String type, String pagename, String target, void Function(dynamic) handler |
Register a listener for an event. You must specify the event name and identify the sending element/view through the pagename and target (which can be view name or element name). For 'nav' events, the pagename is always blank and the target name is the name of the nav command. The handler receives as parameter an object whose type depends on the event being handled. If the event target is a view, the parameter is of type UIView and if the target is an element, the parameter is of type UIElement. (See Helper classes below). |
|
loadPage | void |
UIPage? currentpage, String pagename, bool replace, Map<String,dynamic>? patchcontext, Map<String,dynamic>? patchformdata |
Transition to the named page. If 'replace' is true, the loaded page replaces the top of page stack (See Flutter documentation). Otherwise, it is added on top of the page stack. This call requires a handle to current page from where you are making this call. This is available to your programs as 'widget.page'. 'pagename' is the name of the page to load. Last two parameters are optional and named. They respectively contain any changes you want to make to the app context and the formdata being submitted, before loading the page. |
|
patchAppContext | void |
Map<String,dynamic>? patchdata |
Update the app context. App context is a global Hash map, preserved across pages. This allows you to pass information between pages. The data bound to app views can also reference elements from it. Updated are applied while retaining the unchanged entries. | |
popupPage | void |
UIPage? currentpage, String pagename, Map<String,dynamic>? patchcontext, Map<String,dynamic>? patchformdata |
Works like 'loadPage', except the page is opened as a popup. | |
rebuild | void | Rebuild a view or an element with changes. The rebuild method works differently for views and elements, described separately below. |
||
rebuild (View) | void |
String pagename, String viewname, dynamic patchdata |
For the view, the patchdata contains updates to the data object that is bound with the view. It can be a Function which receives the current bound object as parameter and it returns the updated object. Alternatively, it can be an object containing values to be updated. Only values present in the patchdata are updated, others remain unchanged. Also for the view, the nested views are also automatically updated to reflect the data changes. |
|
rebuild (Element) | void |
String pagename, String elementname, dynamic patchdata |
For the element, the patchdata contains updates to the element properties - it cannot be a Function. The properties that can be updated are the following and the new property values are in the same format that you provide in the WebIDE, when configuring the element. See Helper Class UIElement for the list of properties. Example (Page Flutter code)- void init() { // CBUTTON and CALCVAL are elements. $.app.state['calc'] = Calculator(); $.app.listen('pressed', 'CALCULATOR', 'CBUTTON', (var w) { print('CBUTTON Click'); UIElement t=w as UIElement; Calculator calc=$.app.state['calc'] as Calculator; String val=t.prop('label') ?? ''; var result=calc.key(val); $.app.rebuild('CALCULATOR', 'CALCVAL', {'value': result}); }); } |
|
$.app Flutter reference |
Helper Class UIView
UIView is the internal implementation of the View. A reference of the object is passed to the handler listening to events on the View. The handler can query the properties of the view using the prop method.
Method | Return Type | Parameters | Description |
---|---|---|---|
prop | dynamic |
String name |
Query a property of the view. The following is the list of properties and their respective data types.
|
Helper Class UIElement
UIElement is the internal implementation of the Element. A reference of the object is passed to the handler listening to events on the Element. The handler can query the properties of the view using the prop method.
Method | Return Type | Parameters | Description |
---|---|---|---|
prop | dynamic |
String name |
Query a property of the view. The following is the list of properties and their respective data types.
|
Helper Class UIData
UIData is the internal implementation of Data structure associated with a View. A reference to the view data can be obtained by querying the data property.
Method | Return Type | Parameters | Description |
---|---|---|---|
resolveValue | dynamic |
String? name |
Query a value from the data. If a name is provided the vaue of the named property in data is returned, and if the name is null, the entire data associated with the view is returned as a dynamic hash. |
Push notifications
apps have pre-configured push messaging capability using Firebase Cloud Messaging (FCM) - the messaging platform from Google. You will need to -
- Create a project on Firebase,
- Add apps to the project (for each mobile app),
- Acquire the keys and configuration files, and
- Finally, add the keys/configuration to your ZOAPIIO project.
- The Server key should be copied to your Web App properties in WebIDE.
- The Configuration files should be added to your Mobile project - the configuration for Cordova and Flutter are different.
Please refer to the documentation on Firebase and Cordova/Flutter configuration for the same.
Once your app is compiled and ready, you can send push notifications to the logged in B2C users, from the Admin console of the Base (foundation) Project. To send push notifications from your application (Server APIs), compose a JSON object with following structure -
{ "to": "<the Id of the user from the b2cuser entity in your base project>", "notification": { "title": "The title of the notification message", "body": "The text of the notification message" } }
Then, serialize it into a string, and send to the provided MQ destination as text. The following Biz rule in your WebIDE will accomplish this-
JmsSend to Destination FCMPUSH-QUEUE /Path/to/MessageNode@_JSON
Launching the Web App
The web app is served from the container. This is the most efficient way to render the web app because the instance also holds all your services and data.
It is easy to launch the web app. Simply right click on the WEB-APPLICATION node in your WebIDE, and click on "Launch Application". You can also launch it by directly typing its URL in the browser. The app URL is constructed from your instance URL, the app URL and finally appending '/index' to it.
Native Mobile (Flutter) App
Running and testing your mobile app requires you to setup the Flutter development environment on your machine. Flutter is supported on multiple IDEs - most popular being the Android Studio, and Visual Code. You will need to acquire some basic Flutter knowledge - there are several Internet resources available that will get you started very quickly. Pick an IDE and that you are comfortable with and install the flutter environment. WebIDE will generate the Flutter source code, which you must copy into an empty Flutter project shell.
- Install the Flutter environment and create a new project.
- From the ZOAPIIO WebIDE, under the Tools menu, select the "Generate Flutter Project" command.
- Select the application (WEB-APPLICATION) that you want to generate and pick a server URL. When generating the Flutter app that will run with a different ZOAPIIO server, you should change the server URL - default value points to your development server.
- If all goes well, a ZIP file will be downloaded to your machine. Open it and you will find three folders - assets, images and lib. Copy these folders to your Flutter project folder (use the File Explorer to copy the files).
- Use your IDEs build, test, run and package commands to work with your mobile app.
- Use can use either a Mac or a Windows machine, but to be able to test the iOS build of your app, you are required to have a Mac for your development machine.
- Also, all mobile development environments (even before Flutter) are resource hungry. The same is true of Flutter - you need a development machine of a recent vintage.
- In the Flutter project, there is a special file called 'pubspec.yaml' which controls the project environment and external dependencies.
- Make sure the images and assets folders are exposed as project assets.
- Also add the dependencies. The following is an example, your requirements may differ-
- For Android build of the Flutter project, another file called 'AndriodManifest.xml' comes into the picture.
- Make sure the AndriodManifest.xml file grants access to the app to networking and other services by adding the following.
- iOS build will have a similar environment specific control file.
assets: - images/ - assets/
dependencies: cupertino_icons: ^1.0.3 event_bus: ^2.0.0 google_fonts: ^3.0.1 dev_dependencies: flutter_test: sdk: flutter http: ^0.13.3 flutter_spinkit: ^5.1.0 event_bus: ^2.0.0 device_info_plus: ^3.2.3 shared_preferences: any animated_text_kit: ^4.2.1 geolocator: ^8.0.5 google_sign_in: ^5.3.2 flutter_facebook_auth: ^4.3.4 firebase_core: ^1.17.1 firebase_messaging: ^11.4.1 material_design_icons_flutter: ^5.0.6295 flutter_form_builder: 7.2.1 expressions: ^0.2.3 table_sticky_headers: ^2.0.0 flutter_inappwebview: ^5.4.3+7 flutter_titled_container: ^1.0.7 video_player: ^2.4.7 flick_video_player: ^0.5.0 visibility_detector: ^0.3.3 url_launcher: ^6.1.6
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.XXXX.YYY"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <application...
Cordova Mobile App
Cordova is an open-source cross-platform mobile app development platform. Biggest plus in its favour is that you can take an existing web application and compile it for mobile - both Android and iOS. Only requirement is that your web app must be responsive, so that it renders well on the mobile screen.
Cordova mobile apps are not native apps, however. makes building Flutter apps very easy, but it is still not as direct as using Cordova. Depending upon the complexity of user interaction in your app, your target user profile and your development budget, you can decide your entry to the mobile app market. If you are not sure, we suggest you start with the Cordova app because it is faster to build and progressively advance to the native flutter app. Both are cross-platform.
Running and testing your Cordova mobile app requires you to setup the Cordova development environment on your machine. No IDE is required - it is mostly command line, but you can use an IDE, if you already have one. WebIDE will download the web app source code (HTML/CSS/JS) as bundle, which you must copy into an empty Cordova project shell.
- Install the Cordova environment and create a new project.
- From the ZOAPIIO WebIDE, under the Tools menu, select the "Generate Cordova Project" command.
- Select the application (WEB-APPLICATION) that you want to generate and pick a server URL. When generating the Flutter app that will run with a different ZOAPIIO server, you should change the server URL - default value points to your development server.
- If all goes well, a ZIP file will be downloaded to your machine. Open it and you will find the www folder. Copy the contents of this folder to the www folder of your Cordova app.
- That is all you need, just build your Cordova project and run.
- If you need to change the URL of the server for the app, you will find this in the index.html file, simply change the URL and rebuild. You can have different build for testing and production.