Appearance
Integrating LIQA into Flutter App
Below is the guide for integrating LIQA into a Flutter application using Dart language.
WebView with LIQA Integrated
Here is a ready-to-use implementation of a WebView with integrated LIQA functionality.
Production Recommendation:
For simplicity, the WebView loads the HTML page from a local string containing the HTML content.
In a real-world application, you may consider loading the page from a CDN managed by your organization. Check the Hosting the HTML Page section for more details.
IMPORTANT
The WebView should be opened over HTTPS. Without HTTPS, the camera access will not be requested, and the camera cannot be used. When hosting locally, you can open the WebView using https://localhost/
as the baseUrl
.
dart
liqaWebViewController.loadHtmlString(
_getHtmlPage(),
baseUrl: "https://localhost/"
);
liqaWebViewController.loadHtmlString(
_getHtmlPage(),
baseUrl: "https://localhost/"
);
dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
// ignore: depend_on_referenced_packages
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
class LiqaWebView extends StatefulWidget {
const LiqaWebView({super.key, required this.onLiqaEvent});
final Function(String, Map<String, dynamic>?) onLiqaEvent;
@override
State<LiqaWebView> createState() => _LiqaWebViewState();
}
class _LiqaWebViewState extends State<LiqaWebView> {
WebViewController? liqaWebViewController;
@override
void initState() {
super.initState();
_initializeLiqaWebView();
}
@override
Widget build(BuildContext context) {
return liqaWebViewController != null
? WebViewWidget(
controller: liqaWebViewController!,
)
: const Center(
child: CircularProgressIndicator(
color: Colors.blueGrey,
),
);
}
Future<void> _initializeLiqaWebView() async {
// Request camera permission before loading the WebView
await _requestCameraPermission();
late final PlatformWebViewControllerCreationParams params;
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
params = WebKitWebViewControllerCreationParams(
allowsInlineMediaPlayback: true,
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
);
} else {
params = const PlatformWebViewControllerCreationParams();
}
setState(() {
liqaWebViewController = WebViewController.fromPlatformCreationParams(
params,
onPermissionRequest: (request) async {
// Handle camera permission specifically
if (request.types.first.name == 'camera') {
final granted = await _requestCameraPermission();
if (granted) {
request.grant();
} else {
request.deny();
}
}
},
)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'FlutterWebView',
onMessageReceived: (JavaScriptMessage message) {
try {
print("Received message from LIQA: ${message.message}");
final messageObject =
jsonDecode(message.message) as Map<String, dynamic>;
final name = messageObject['name'] as String;
final payload = messageObject['payload'] as Map<String, dynamic>?;
// Pass the event to the external handler
widget.onLiqaEvent(name, payload);
} catch (e) {
print('Failed to parse message: $e');
}
},
)
..loadHtmlString(_getHtmlPage(), baseUrl: "https://localhost/");
});
}
Future<bool> _requestCameraPermission() async {
var status = await Permission.camera.status;
if (!status.isGranted) {
await Permission.camera.request();
}
return status.isGranted;
}
String _getHtmlPage() {
return """
PUT THE HTML CONTENT HERE
""";
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
// ignore: depend_on_referenced_packages
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
class LiqaWebView extends StatefulWidget {
const LiqaWebView({super.key, required this.onLiqaEvent});
final Function(String, Map<String, dynamic>?) onLiqaEvent;
@override
State<LiqaWebView> createState() => _LiqaWebViewState();
}
class _LiqaWebViewState extends State<LiqaWebView> {
WebViewController? liqaWebViewController;
@override
void initState() {
super.initState();
_initializeLiqaWebView();
}
@override
Widget build(BuildContext context) {
return liqaWebViewController != null
? WebViewWidget(
controller: liqaWebViewController!,
)
: const Center(
child: CircularProgressIndicator(
color: Colors.blueGrey,
),
);
}
Future<void> _initializeLiqaWebView() async {
// Request camera permission before loading the WebView
await _requestCameraPermission();
late final PlatformWebViewControllerCreationParams params;
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
params = WebKitWebViewControllerCreationParams(
allowsInlineMediaPlayback: true,
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
);
} else {
params = const PlatformWebViewControllerCreationParams();
}
setState(() {
liqaWebViewController = WebViewController.fromPlatformCreationParams(
params,
onPermissionRequest: (request) async {
// Handle camera permission specifically
if (request.types.first.name == 'camera') {
final granted = await _requestCameraPermission();
if (granted) {
request.grant();
} else {
request.deny();
}
}
},
)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'FlutterWebView',
onMessageReceived: (JavaScriptMessage message) {
try {
print("Received message from LIQA: ${message.message}");
final messageObject =
jsonDecode(message.message) as Map<String, dynamic>;
final name = messageObject['name'] as String;
final payload = messageObject['payload'] as Map<String, dynamic>?;
// Pass the event to the external handler
widget.onLiqaEvent(name, payload);
} catch (e) {
print('Failed to parse message: $e');
}
},
)
..loadHtmlString(_getHtmlPage(), baseUrl: "https://localhost/");
});
}
Future<bool> _requestCameraPermission() async {
var status = await Permission.camera.status;
if (!status.isGranted) {
await Permission.camera.request();
}
return status.isGranted;
}
String _getHtmlPage() {
return """
PUT THE HTML CONTENT HERE
""";
}
}
Before integrating the snippet into your app, please ensure that:
- Camera permissions are requested and granted before loading the
WebView
. - JavaScript is enabled in the WebView (
setJavaScriptMode(JavaScriptMode.unrestricted)
). WebView
loads the page over HTTPS.- You have installed the necessary dependencies:
bash
flutter pub add webview_flutter permission_handler
flutter pub add webview_flutter permission_handler
Application Integration Example
Here is an app’s entry point, showcasing a sample integration of LiqaWebView
:
dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'liqa_web_view.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: HomeScreen());
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String? capturedImage;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('LIQA Flutter Integration'),
actions: [
if (capturedImage != null)
IconButton(
onPressed: showCapturedImage, icon: const Icon(Icons.image))
],
),
body: LiqaWebView(
onLiqaEvent: handleLiqaEvent,
),
);
}
void handleLiqaEvent(String name, Map<String, dynamic>? payload) {
print('LIQA Event: $name');
if (payload != null) {
print('Payload: ${jsonEncode(payload)}');
}
if (name == 'capture') {
setState(() {
capturedImage = payload?['data'];
});
}
}
void showCapturedImage() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LiqaImageScreen(
base64String: capturedImage!
.replaceFirst('data:image/jpeg;base64,', ''),
),
),
);
}
}
class LiqaImageScreen extends StatelessWidget {
const LiqaImageScreen({super.key, required this.base64String});
final String base64String;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Captured Image'),
),
body: Image.memory(base64Decode(base64String)),
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'liqa_web_view.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: HomeScreen());
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String? capturedImage;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('LIQA Flutter Integration'),
actions: [
if (capturedImage != null)
IconButton(
onPressed: showCapturedImage, icon: const Icon(Icons.image))
],
),
body: LiqaWebView(
onLiqaEvent: handleLiqaEvent,
),
);
}
void handleLiqaEvent(String name, Map<String, dynamic>? payload) {
print('LIQA Event: $name');
if (payload != null) {
print('Payload: ${jsonEncode(payload)}');
}
if (name == 'capture') {
setState(() {
capturedImage = payload?['data'];
});
}
}
void showCapturedImage() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LiqaImageScreen(
base64String: capturedImage!
.replaceFirst('data:image/jpeg;base64,', ''),
),
),
);
}
}
class LiqaImageScreen extends StatelessWidget {
const LiqaImageScreen({super.key, required this.base64String});
final String base64String;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Captured Image'),
),
body: Image.memory(base64Decode(base64String)),
);
}
}
Application Permissions Configuration
For LiqaWebView
to function correctly, you’ll need to configure camera permissions in your configuration files.
Android
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- Camera feature -->
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<!-- Application Configuration -->
<application android:label="LIQA WebView Integration">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- Camera feature -->
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<!-- Application Configuration -->
<application android:label="LIQA WebView Integration">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
iOS
xml
<key>NSCameraUsageDescription</key>
<string>We need camera access to analyze skin quality.</string>
<key>NSCameraUsageDescription</key>
<string>We need camera access to analyze skin quality.</string>
Podfile
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
]
end
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
]
end
end
end
Further Considerations
- Secure the
WebView
: Ensure that theWebView
is secured against potential vulnerabilities, such as cross-site scripting (XSS). - Test Permissions Thoroughly: Verify that the app requests and handles permissions correctly on both iOS and Android.
- For more details, refer to the Flutter WebView Documentation.
Need Assistance?
If you have any questions regarding the integration or need further assistance, please contact Haut.AI Support Team with this request via the Support Desk.