feat(client_app): open privacy policy in in-app webview
Add a reusable WebPageScreen (webview_flutter host with close button + progress bar, no nav interception) and wire the profile 'kebijakan privasi' menu item to open https://mybestieindonesia.com/privacy in it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
96
client_app/lib/core/widgets/web_page_screen.dart
Normal file
96
client_app/lib/core/widgets/web_page_screen.dart
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
|
||||||
|
import '../theme/halo_tokens.dart';
|
||||||
|
|
||||||
|
/// Generic in-app WebView host for static external pages (privacy policy,
|
||||||
|
/// terms & conditions, etc).
|
||||||
|
///
|
||||||
|
/// Unlike [XenditCheckoutScreen] this carries no navigation-interception logic
|
||||||
|
/// — it just loads [url] and lets the user read it, with a close button and a
|
||||||
|
/// progress bar. Push it with a plain `Navigator.push(MaterialPageRoute(...))`.
|
||||||
|
class WebPageScreen extends StatefulWidget {
|
||||||
|
final String url;
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
const WebPageScreen({
|
||||||
|
super.key,
|
||||||
|
required this.url,
|
||||||
|
required this.title,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WebPageScreen> createState() => _WebPageScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebPageScreenState extends State<WebPageScreen> {
|
||||||
|
late final WebViewController _controller;
|
||||||
|
int _progress = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = WebViewController()
|
||||||
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
|
..setBackgroundColor(HaloTokens.surface)
|
||||||
|
..setNavigationDelegate(
|
||||||
|
NavigationDelegate(
|
||||||
|
onProgress: (p) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _progress = p);
|
||||||
|
},
|
||||||
|
onWebResourceError: (error) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint(
|
||||||
|
'[WebPageScreen] WebResourceError '
|
||||||
|
'code=${error.errorCode} type=${error.errorType} '
|
||||||
|
'desc=${error.description} url=${error.url}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..loadRequest(Uri.parse(widget.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: HaloTokens.surface,
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: HaloTokens.surface,
|
||||||
|
elevation: 0,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.close, color: HaloTokens.brandDark),
|
||||||
|
tooltip: 'Tutup',
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
widget.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontFamily: HaloTokens.fontDisplay,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: HaloTokens.brandDark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
bottom: _progress < 100
|
||||||
|
? PreferredSize(
|
||||||
|
preferredSize: const Size.fromHeight(2),
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value: _progress / 100.0,
|
||||||
|
minHeight: 2,
|
||||||
|
backgroundColor: HaloTokens.brandSofter,
|
||||||
|
valueColor: const AlwaysStoppedAnimation<Color>(
|
||||||
|
HaloTokens.brand,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
body: WebViewWidget(controller: _controller),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../core/auth/auth_notifier.dart';
|
import '../../core/auth/auth_notifier.dart';
|
||||||
import '../../core/theme/halo_tokens.dart';
|
import '../../core/theme/halo_tokens.dart';
|
||||||
|
import '../../core/widgets/web_page_screen.dart';
|
||||||
import '../home/widgets/halo_tab_bar.dart';
|
import '../home/widgets/halo_tab_bar.dart';
|
||||||
|
|
||||||
/// "Kamu" tab — profile screen.
|
/// "Kamu" tab — profile screen.
|
||||||
@@ -78,7 +79,14 @@ class ProfileScreen extends ConsumerWidget {
|
|||||||
_MenuItemData(
|
_MenuItemData(
|
||||||
icon: Icons.lock_outline,
|
icon: Icons.lock_outline,
|
||||||
label: 'kebijakan privasi',
|
label: 'kebijakan privasi',
|
||||||
onTap: () {},
|
onTap: () => Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => const WebPageScreen(
|
||||||
|
url: 'https://mybestieindonesia.com/privacy',
|
||||||
|
title: 'kebijakan privasi',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|||||||
Reference in New Issue
Block a user