Commit b7cceccb authored by DatHV's avatar DatHV
Browse files

update config web + fix bug

parent 42a99a61
......@@ -289,7 +289,7 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
);
}
_buildVersionInfo(String? version, String? buildNumber) {
Container _buildVersionInfo(String? version, String? buildNumber) {
return Container(
color: Colors.grey[200],
padding: const EdgeInsets.symmetric(vertical: 12),
......@@ -301,7 +301,7 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
);
}
_showAlertConfirmLogout() {
void _showAlertConfirmLogout() {
final dataAlert = DataAlertModel(
title: "Xác nhận",
description: "Bạn có chắc muốn đăng xuất?",
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/restful_api_viewmodel.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart';
import '../../base/base_response_model.dart';
import '../../configs/url_params.dart';
import '../../model/auth/login_token_response_model.dart';
import '../../model/auth/profile_response_model.dart';
import 'models/update_response_model.dart';
import '../../preference/data_preference.dart';
......@@ -35,21 +38,38 @@ class SplashScreenViewModel extends RestfulApiViewModel {
}
Future<void> getUserProfile() async {
if (!(DataPreference.instance.logged)) {
// Không cần loadFromStorage nữa vì token đã được set từ main.dart
// await UrlParams.loadFromStorage();
if (DataPreference.instance.logged) {
_freshUserProfile();
return;
}
final token = UrlParams.getTokenForApi() ?? "";
print('🔍 SplashScreen - Token from URL: $token');
if (!kIsWeb || token.isEmpty) {
print('❌ No token found, going to onboarding');
Get.toNamed(onboardingScreen);
return;
}
print('✅ Token found, proceeding with login');
LoginTokenResponseModel tokenModel = LoginTokenResponseModel(accessToken: token);
await DataPreference.instance.saveLoginToken(tokenModel);
_freshUserProfile();
return;
}
Future<void> _freshUserProfile() async {
showLoading();
client.getUserProfile().then((value) async {
final response = await client.getUserProfile();
hideLoading();
final userProfile = value.data;
if (value.isSuccess && userProfile != null) {
final userProfile = response.data;
if (response.isSuccess && userProfile != null) {
_freshDataAndToMainScreen(userProfile);
} else {
DataPreference.instance.clearLoginToken();
Get.toNamed(onboardingScreen);
}
});
}
void _freshDataAndToMainScreen(ProfileResponseModel userProfile) async {
......
......@@ -28,7 +28,7 @@ class BaseWebViewScreen extends BaseScreen {
class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicState {
late final BaseWebViewInput input;
late final WebViewController _controller;
WebViewController? _controller; // Nullable cho web platform
String? _dynamicTitle;
@override
......@@ -43,16 +43,31 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta
});
return;
}
// Web platform: mở URL trong tab mới và đóng màn hình ngay
if (kIsWeb) {
AppLoading().hide();
Future.microtask(() async {
await _openUrlInBrowser();
if (mounted) {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
}
});
return;
}
// Mobile platform: khởi tạo WebView
AppLoading().show();
_controller =
WebViewController()
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(Colors.transparent)
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (_) async {
AppLoading().hide();
final title = await _controller.getTitle();
final title = await _controller!.getTitle();
setState(() {
_dynamicTitle = title;
});
......@@ -92,9 +107,14 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta
@override
Widget createBody() {
// Web platform: không hiển thị màn hình trung gian (đã auto mở tab mới ở initState)
if (kIsWeb) {
return const SizedBox.shrink();
}
// Mobile platform: sử dụng WebView
return Scaffold(
appBar:
input.isFullScreen
appBar: input.isFullScreen
? null
: CustomNavigationBar(
title: input.title ?? _dynamicTitle ?? Uri.parse(input.url).host,
......@@ -106,7 +126,7 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta
children: [
SafeArea(
child: WebViewWidget(
controller: _controller,
controller: _controller!,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(VerticalDragGestureRecognizer.new),
},
......@@ -120,18 +140,126 @@ class _BaseWebViewScreenState extends BaseState<BaseWebViewScreen> with BasicSta
}
Future<void> _clearCookies() async {
if (kIsWeb) return; // Web không cần clear cookies
final cookieManager = WebViewCookieManager();
await cookieManager.clearCookies();
}
void _handleBack() async {
if (await _controller.canGoBack()) {
_controller.goBack();
if (kIsWeb) {
// Web: chỉ đơn giản quay lại
if (context.mounted) Navigator.of(context).pop();
return;
}
// Mobile: kiểm tra WebView có thể go back không
if (_controller != null && await _controller!.canGoBack()) {
_controller!.goBack();
} else {
if (context.mounted) Navigator.of(context).pop();
}
}
Widget _buildWebViewForWeb() {
if (!kIsWeb) return const SizedBox.shrink();
return Container(
width: double.infinity,
height: double.infinity,
child: Column(
children: [
// Header với URL
Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[100],
child: Row(
children: [
Icon(Icons.language, color: Colors.blue),
const SizedBox(width: 8),
Expanded(
child: Text(
input.url,
style: const TextStyle(fontSize: 14),
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: () => _openUrlInBrowser(),
icon: const Icon(Icons.open_in_new, size: 16),
label: const Text('Mở tab mới'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
),
],
),
),
// iframe area
Expanded(
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.web, size: 64, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'WebView không hỗ trợ trực tiếp trên web',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: Colors.grey[600],
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Vui lòng click "Mở tab mới" để xem nội dung',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey[500],
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () => _openUrlInBrowser(),
icon: const Icon(Icons.open_in_new),
label: const Text('Mở trong tab mới'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
],
),
),
),
),
],
),
);
}
Future<void> _openUrlInBrowser() async {
try {
final uri = Uri.parse(formatUrl(input.url));
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
if (kDebugMode) {
print('Could not launch URL: ${input.url}');
}
}
} catch (e) {
if (kDebugMode) {
print('Error launching URL: $e');
}
}
}
NavigationDecision _handleNavigation(NavigationRequest request) {
final url = request.url;
debugPrint("➡️ Navigating: $url");
......
export 'web_helper_stub.dart'
if (dart.library.html) 'web_helper_web.dart';
// Stub implementations for non-web platforms
void webReplaceUrl(String path) {
// no-op on non-web
}
void webClearStorage() {
// no-op on non-web
}
// Web-specific implementations
// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html;
void webReplaceUrl(String path) {
try {
final origin = html.window.location.origin;
final newUrl = '$origin$path';
html.window.history.replaceState(null, html.document.title ?? '', newUrl);
} catch (_) {}
}
void webClearStorage() {
try {
html.window.localStorage.clear();
html.window.sessionStorage.clear();
// Optionally clear caches if any
} catch (_) {}
}
......@@ -44,6 +44,7 @@ dependencies:
flutter_widget_from_html: ^0.17.1
device_info_plus: ^12.1.0
uuid: ^4.3.3
universal_html: ^2.2.4
flutter_svg:
local_auth:
pin_code_fields:
......
#!/bin/bash
echo "Testing build with different flavors..."
echo "Building dev flavor..."
flutter build apk --flavor dev --debug
echo "Building stg flavor..."
flutter build apk --flavor stg --debug
echo "Building pro flavor..."
flutter build apk --flavor pro --debug
echo "Build test completed!"
// Firebase Messaging Service Worker (stub for web)
// We disable messaging on web, this file avoids 404 registration errors.
self.addEventListener('install', (event) => {
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});
// No push/message handlers; messaging is disabled on web.
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="mypoint_flutter_app">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>mypoint_flutter_app</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>
{
"name": "mypoint_flutter_app",
"short_name": "mypoint_flutter_app",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment