How WebViews Streamline App Development for OY! Indonesia

Indra Septama
OY! Indonesia
Published in
6 min readOct 2, 2023
Source https://unsplash.com/photos/vj_9l20fzj0

OY! Indonesia is a comprehensive and secure business payment solution that is aimed to streamline the payment process for businesses of all sizes. Currently, we have a range of products for payment solutions, including bulk disbursement, payment links, VA aggregator, API biller, virtual cards, e-wallet aggregator, and more, which can be checked at https://www.oyindonesia.com/en/home. All of these products can be managed through our website’s dashboard.

Currently, the website’s dashboard effectively accommodates both desktop and mobile users, ensuring a seamless browsing experience across different devices. However, as user preferences and behaviors continue to evolve, there is an increasingly strong demand for a dedicated mobile app to enhance the user experience. A dedicated mobile app can provide users with convenience, allowing for more efficient use of features (without opening the website from the browser) and greater practicality.

However, the decision to develop a mobile app doesn’t come without challenges. One significant challenge is the limitation of resources both in terms of time and manpower. Balancing the ongoing maintenance and updates of the existing website while simultaneously developing a mobile app presents resource allocation difficulties. This situation necessitates a carefully crafted strategy that optimizes resource utilization and ensures that both platforms receive the attention they require.

Solution

We’ve previously used Flutter for a mobile app. To see why, read Flutter at OY! Indonesia: The Motivation. Drawing from past experiences, when faced with the task of creating the OY! Business application, the decision was made to leverage the efficiency and familiarity of Flutter once again. However, note that using Flutter doesn’t guarantee a quicker process. Creating an app from scratch takes time, especially with limited resources. Still, the OY! Business app will closely match the mobile website’s look and feel.

In mobile app development, webview integration is a common practice. This approach is particularly useful when desired features are already present on the website. Given the hurdles posed by a reduced developer team and a tight timeline, we turned to the Flutter framework, leveraging the flutter_inappwebview plugin. By using the Flutter in-app webview plugin, we combined the mobile website’s features seamlessly into the app. This preserves time and maintains a good user experience.

Using Flutter WebView, we can directly open a web page within our app. However, it should be noted that when using the mobile website feature, user data auth is required. On the other hand, if we directly open the webview through the app, the data auth is empty. Therefore, we need to send data auth from the business app to the webview. Fortunately, when logging in to the business app, the data obtained is roughly the same as the data obtained when logging in to the website. The data auth will be sent directly when the webview is opened. This is useful because it allows us to access the mobile website feature within our app without requiring the user to log in again.

Implementation

Here, we’ll dive deeper into the implementation details that are crucial to the success of the development process. Let’s explore some interesting aspects.

Forwarding Authentication Data to the WebView

The oy website stores auth data in local storage. Therefore, when we open a webview, we need to manipulate the local storage data with the auth data we have previously obtained. On Flutter’s InAppWebView, we can manipulate the local storage by injecting JavaScript through initialUserScripts. Here’s a simple example:

InAppWebView(
initialUserScripts: UnmodifiableListView<UserScript>(
[
UserScript(
source: """
window.localStorage.setItem("auth" , `$dataAuth`);
""",
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START,
),
],
),
...
)

By strategically manipulating local storage through injected JavaScript, we bridge the gap between app and website, enhancing user experience and minimizing the need for redundant logins.

Determining Display Settings for Mobile App Users

To distinguish a website accessed using a webview or a regular browser, we need to add an identifier when opening the webview. The identifier that can be used is the user-agent. On the Flutter side, we need to add the user agent in the in-app webview settings. Here is the code to set the user agent in the in-app webview:

InAppWebView(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
userAgent:"custom-user-agent",
)
),
...
)

From the oy website side, adjustments need to be made regarding the login to distinguish between the website being opened through a regular browser or through a business app via webview. In addition, adjustments on the website side are also needed to determine which display can be displayed through a regular browser and which display can be displayed via webview. Here is the line of code for checking the user agent:

const isFromWebview = window.navigator.userAgent === "custom-user-agent"

Enabling “Copy Text” Functionality

Although we can access an OY website directly through an application (without a browser), there are some limitations when using a webview. For example, we cannot use the copy text feature on the website when we open it through a webview. Therefore, we need a workaround to solve this problem. One thing that can be done is to set up a JavaScript handler when opening a webview in the Flutter code. On the Next.js side, if we want to use the copy text feature when opening it through a webview, we need to call the set up handler.

export const copyTextToClipboard = (text: string) => {
if (window.navigator.userAgent.includes('[Oy]BusinessApp/')) {
window.flutter_inappwebview.callHandler('CopyHandler', text);
return;
}
navigator.clipboard.writeText(text);
};

As you can see, there is a condition that triggers a call to a Javascript handler when the webview is opened. On the Flutter side, we need to add logic to handle the callback when it is called from the next JS side. The following code is an example of how to implement this on the Flutter side.

controller.addJavaScriptHandler(
handlerName: 'CopyHandler',
callback: (args) {
if (args.isNotEmpty) {
Clipboard.setData(ClipboardData(text: args[0]));
}
},
);

The Flutter code executes a method to copy text when the Javascript handler is called. Therefore, although the web view can be embedded directly into the Flutter application, some adjustments are necessary to ensure that not all logic can run in the web view.

Conclusion

Using Flutter and the in-app webview plugin, we can develop a mobile app that offers the same functionalities as the mobile website, ensuring a smooth and uninterrupted user experience. This methodology also accelerates the development process. Without employing this technique, a significant amount of time and resources would be necessary to replicate the precise features found on the mobile website. However, by adopting this strategy, we reallocate these time and resource investments to ensure seamless app functionality when accessing the website, thereby utilizing its full range of features.

With the in-app webview plugin, developers can incorporate web content into the app and access local storage data, enabling users to enjoy the features of the mobile website without having to log in again. However, there may be limitations when using a webview, such as the inability to utilize certain website features, which can be resolved by implementing JavaScript handlers. By carefully addressing these limitations and making necessary modifications, we can deliver a top-quality mobile app that satisfies our users’ expectations and requirements.

Furthermore, when there is a new feature on the mobile website, it is automatically available on the mobile app as well. A real-case example: some time ago, when a new feature (a type of top-up) was added to the mobile dashboard, we didn’t need to develop that feature for the mobile app separately. Instead, we only needed to test the feature in the mobile app to ensure it was functioning properly. This way, it saves developer resources within the company.

--

--