Commit 36ac8d24 authored by DatHV's avatar DatHV
Browse files

cập nhật build os

parent 873fba2d
class AppConfig {
final String name;
final String baseUrl;
final String t3Token;
final bool enableLogging;
const AppConfig({
required this.name,
required this.baseUrl,
required this.t3Token,
required this.enableLogging,
});
static late AppConfig current;
}
class APIPaths {//sandbox class APIPaths {//sandbox
static const String baseUrl = "https://api.mypoint.com.vn/8854/gup2start/rest"; static const String baseUrl = "https://api.sandbox.mypoint.com.vn/8854/gup2start/rest";
static const String checkUpdate = "/version-management-service/api/v1.0/check-customer-software-update"; static const String checkUpdate = "/version-management-service/api/v1.0/check-customer-software-update";
static const String getOnboardingInfo = "/resource/api/v2.0/intro-screen"; static const String getOnboardingInfo = "/resource/api/v2.0/intro-screen";
static const String checkPhoneNumber = "/user/api/v2.0/account/users/checkPhoneNumber"; static const String checkPhoneNumber = "/user/api/v2.0/account/users/checkPhoneNumber";
......
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle, MethodChannel;
import 'package:flutter/foundation.dart';
import 'app_config.dart';
Future<void> loadEnv() async {
String flavor = 'dev'; // default flavor
String baseUrl = 'https://api.sandbox.mypoint.com.vn/8854/gup2start/rest';
String t3Token = 'dev-xxx';
bool enableLogging = true;
// Try to get config from BuildConfig if available
try {
const MethodChannel channel = MethodChannel('com.icom.mypoint/config');
final Map<dynamic, dynamic> config = await channel.invokeMethod('getConfig');
flavor = config['flavor'] ?? 'dev';
baseUrl = config['baseUrl'] ?? baseUrl;
t3Token = config['libToken'] ?? t3Token;
enableLogging = config['enableLogging'] ?? enableLogging;
} catch (e) {
if (kDebugMode) {
print('Could not get config from BuildConfig, using default: $e');
}
}
AppConfig.current = AppConfig(
name: flavor,
baseUrl: baseUrl,
t3Token: t3Token,
enableLogging: enableLogging,
);
}
...@@ -2,20 +2,23 @@ import 'package:flutter/material.dart'; ...@@ -2,20 +2,23 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/app_navigator.dart'; import 'package:mypoint_flutter_app/networking/app_navigator.dart';
import 'package:mypoint_flutter_app/configs/api_paths.dart';
import 'package:mypoint_flutter_app/preference/data_preference.dart'; import 'package:mypoint_flutter_app/preference/data_preference.dart';
import 'package:mypoint_flutter_app/preference/point/point_manager.dart'; import 'package:mypoint_flutter_app/preference/point/point_manager.dart';
import 'package:mypoint_flutter_app/resources/base_color.dart'; import 'package:mypoint_flutter_app/resources/base_color.dart';
import 'package:mypoint_flutter_app/screen/home/header_home_viewmodel.dart'; import 'package:mypoint_flutter_app/screen/home/header_home_viewmodel.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart'; import 'package:mypoint_flutter_app/shared/router_gage.dart';
import 'base/app_loading.dart'; import 'base/app_loading.dart';
import 'env_loader.dart';
import 'networking/dio_http_service.dart'; import 'networking/dio_http_service.dart';
import 'push_setup.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await loadEnv();
await initFirebaseAndFcm();
DioHttpService();
Get.put(HeaderThemeController(), permanent: true); Get.put(HeaderThemeController(), permanent: true);
await DataPreference.instance.init(); await DataPreference.instance.init();
DioHttpService().setBaseUrl(APIPaths.baseUrl);
await UserPointManager().fetchUserPoint(); await UserPointManager().fetchUserPoint();
runApp(const MyApp()); runApp(const MyApp());
AppLoading().attach(); AppLoading().attach();
......
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'dart:io';
import '../app_config.dart';
import 'interceptor/auth_interceptor.dart'; import 'interceptor/auth_interceptor.dart';
import 'interceptor/exception_interceptor.dart'; import 'interceptor/exception_interceptor.dart';
import 'interceptor/logger_interceptor.dart'; import 'interceptor/logger_interceptor.dart';
...@@ -12,13 +14,12 @@ class DioHttpService { ...@@ -12,13 +14,12 @@ class DioHttpService {
DioHttpService._internal(); DioHttpService._internal();
static final DioHttpService _instance = DioHttpService._internal(); static final DioHttpService _instance = DioHttpService._internal();
factory DioHttpService() => _instance; factory DioHttpService() => _instance;
String _baseUrl = '';
Dio get dio => _dio; Dio get dio => _dio;
late final Dio _dio = late final Dio _dio =
Dio( Dio(
BaseOptions( BaseOptions(
baseUrl: _baseUrl, baseUrl: AppConfig.current.baseUrl,
connectTimeout: const Duration(seconds: connectTimeout), connectTimeout: const Duration(seconds: connectTimeout),
receiveTimeout: const Duration(seconds: receiveTimeout), receiveTimeout: const Duration(seconds: receiveTimeout),
contentType: 'application/json', contentType: 'application/json',
...@@ -40,11 +41,6 @@ class DioHttpService { ...@@ -40,11 +41,6 @@ class DioHttpService {
// ), // ),
// ); // );
void setBaseUrl(String newUrl) {
_baseUrl = newUrl;
_dio.options.baseUrl = newUrl;
}
void setDefaultHeaders(Map<String, dynamic> headers) { void setDefaultHeaders(Map<String, dynamic> headers) {
dio.options.headers.addAll(headers); dio.options.headers.addAll(headers);
} }
......
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'firebase_options.dart';
@pragma('vm:entry-point') // bắt buộc cho background isolate
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
// TODO: xử lý dữ liệu background nếu cần
}
final _flnp = FlutterLocalNotificationsPlugin();
Future<void> _initLocalNotifications() async {
const androidInit = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosInit = DarwinInitializationSettings();
const init = InitializationSettings(android: androidInit, iOS: iosInit);
await _flnp.initialize(init);
const channel = AndroidNotificationChannel(
'default_channel', 'General',
description: 'Default notifications',
importance: Importance.high,
);
await _flnp
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
Future<void> initFirebaseAndFcm() async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
final messaging = FirebaseMessaging.instance;
// Quyền iOS / Android 13+
if (Platform.isIOS) {
await messaging.requestPermission(alert: true, badge: true, sound: true);
} else {
await messaging.requestPermission(); // Android 13+ POST_NOTIFICATIONS
}
await _initLocalNotifications();
// Foreground: tự hiện local notification
FirebaseMessaging.onMessage.listen((message) {
final n = message.notification;
if (n != null) {
_flnp.show(
n.hashCode,
n.title,
n.body,
const NotificationDetails(
android: AndroidNotificationDetails('default_channel', 'General',
importance: Importance.high, priority: Priority.high),
iOS: DarwinNotificationDetails(),
),
payload: message.data.isNotEmpty ? message.data.toString() : null,
);
}
});
// User click notification mở app
FirebaseMessaging.onMessageOpenedApp.listen((message) {
// TODO: điều hướng theo message.data['screen'] (nếu có)
});
// Nếu app mở từ trạng thái terminated do user bấm notify
final initial = await FirebaseMessaging.instance.getInitialMessage();
if (initial != null) {
// TODO: xử lý điều hướng
}
// Lấy token để test gửi
final token = await messaging.getToken();
print('FCM token: $token');
}
...@@ -133,7 +133,7 @@ class MonthlyPointsChart extends StatelessWidget { ...@@ -133,7 +133,7 @@ class MonthlyPointsChart extends StatelessWidget {
enabled: true, enabled: true,
handleBuiltInTouches: true, handleBuiltInTouches: true,
touchTooltipData: BarTouchTooltipData( touchTooltipData: BarTouchTooltipData(
tooltipBgColor: Colors.black87, // tooltipBgColor: Colors.black87,
fitInsideHorizontally: true, fitInsideHorizontally: true,
fitInsideVertically: true, fitInsideVertically: true,
getTooltipItem: (group, groupIndex, rod, rodIndex) { getTooltipItem: (group, groupIndex, rod, rodIndex) {
......
...@@ -121,10 +121,10 @@ class _ScanTabViewState extends State<ScanTabView> { ...@@ -121,10 +121,10 @@ class _ScanTabViewState extends State<ScanTabView> {
left: 0, left: 0,
right: 0, right: 0,
child: Center( child: Center(
child: ValueListenableBuilder<TorchState>( child: ValueListenableBuilder<MobileScannerState>(
valueListenable: _cam.torchState, valueListenable: _cam,
builder: (_, state, __) { builder: (_, state, __) {
final on = state == TorchState.on; final on = state.torchState == TorchState.on;
return InkWell( return InkWell(
onTap: () => _cam.toggleTorch(), onTap: () => _cam.toggleTorch(),
borderRadius: BorderRadius.circular(32), borderRadius: BorderRadius.circular(32),
......
...@@ -101,11 +101,18 @@ class _MyMobileCardListScreenState extends State<MyMobileCardListScreen> { ...@@ -101,11 +101,18 @@ class _MyMobileCardListScreenState extends State<MyMobileCardListScreen> {
margin: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
child: DottedBorder( child: DottedBorder(
color: Colors.redAccent.withOpacity(0.3), options: RoundedRectDottedBorderOptions(
borderType: BorderType.RRect, // borderType: BorderType.RRect,
radius: const Radius.circular(12), color: Colors.redAccent.withOpacity(0.3),
dashPattern: const [3, 3], radius: const Radius.circular(12),
strokeWidth: 1, dashPattern: const [3, 3],
strokeWidth: 1,
),
// color: Colors.redAccent.withOpacity(0.3),
// borderType: BorderType.RRect,
// radius: const Radius.circular(12),
// dashPattern: const [3, 3],
// strokeWidth: 1,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
......
...@@ -102,11 +102,17 @@ class _MyVoucherListScreenState extends State<MyVoucherListScreen> { ...@@ -102,11 +102,17 @@ class _MyVoucherListScreenState extends State<MyVoucherListScreen> {
margin: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
child: DottedBorder( child: DottedBorder(
color: Colors.redAccent.withOpacity(0.3), options: RoundedRectDottedBorderOptions(
borderType: BorderType.RRect, color: Colors.redAccent.withOpacity(0.3),
radius: const Radius.circular(12), radius: const Radius.circular(12),
dashPattern: const [3, 3], dashPattern: const [3, 3],
strokeWidth: 1, strokeWidth: 1,
),
// color: Colors.redAccent.withOpacity(0.3),
// borderType: BorderType.RRect,
// radius: const Radius.circular(12),
// dashPattern: const [3, 3],
// strokeWidth: 1,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
......
...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev ...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1 version: 1.21.11+2025101801
environment: environment:
sdk: ^3.7.0 sdk: ^3.7.0
...@@ -38,35 +38,39 @@ dependencies: ...@@ -38,35 +38,39 @@ dependencies:
json_annotation: ^4.9.0 json_annotation: ^4.9.0
retrofit: retrofit:
get: ^4.7.2 get: ^4.7.2
fluttertoast: 8.2.12 fluttertoast: ^8.2.13
logger: logger:
url_launcher: ^6.3.1 url_launcher: ^6.3.1
flutter_widget_from_html: ^0.16.0 flutter_widget_from_html: ^0.17.1
device_info_plus: ^9.0.3 device_info_plus: ^12.1.0
uuid: ^4.3.3 uuid: ^4.3.3
flutter_svg: flutter_svg:
local_auth: local_auth:
pin_code_fields: pin_code_fields:
intl: ^0.19.0 intl: ^0.20.2
webview_flutter: ^4.2.2 webview_flutter: ^4.2.2
webview_flutter_wkwebview: ^3.9.4 webview_flutter_wkwebview: ^3.9.4
qr_flutter: ^4.0.0 qr_flutter: ^4.0.0
barcode_widget: ^2.0.1 barcode_widget: ^2.0.1
infinite_carousel: ^1.0.3 infinite_carousel: ^1.0.3
package_info_plus: ^4.1.0 package_info_plus: ^9.0.0
dotted_border: ^2.0.0 dotted_border: ^3.1.0
flutter_contacts: ^1.1.6 flutter_contacts: ^1.1.6
permission_handler: ^11.0.0 permission_handler: ^12.0.1
share_plus: ^7.2.1 share_plus: ^12.0.0
file_saver: ^0.2.2 file_saver: ^0.3.1
month_picker_dialog: month_picker_dialog:
marquee: ^2.2.3 marquee: ^2.2.3
fl_chart: ^0.66.2 fl_chart: ^1.1.0
mobile_scanner: ^3.5.7 mobile_scanner: ^7.0.1
encrypt: ^5.0.1 encrypt: ^5.0.1
connectivity_plus: connectivity_plus:
flutter_launcher_icons: ^0.14.4
game_miniapp: game_miniapp:
path: ../mini_app/game_miniapp path: ../mini_app/game_miniapp
firebase_core: ^4.1.0
firebase_messaging: ^16.0.1
flutter_local_notifications: ^19.4.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
...@@ -80,7 +84,7 @@ dev_dependencies: ...@@ -80,7 +84,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^5.0.0 flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
...@@ -98,6 +102,7 @@ flutter: ...@@ -98,6 +102,7 @@ flutter:
- assets/images/ - assets/images/
- assets/data/ - assets/data/
- assets/images/cashback/ - assets/images/cashback/
- assets/config/env.json
# - images/a_dot_burr.jpeg # - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg
...@@ -126,3 +131,29 @@ flutter: ...@@ -126,3 +131,29 @@ flutter:
# #
# For details regarding fonts from package dependencies, # For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package # see https://flutter.dev/to/font-from-package
flutter_launcher_icons:
android: true
ios: false
image_path: assets/icons/dev/app_icon.png
adaptive_icon_foreground: assets/icons/dev/ic_foreground.png
adaptive_icon_background: "#FEE440"
adaptive_icon_monochrome: assets/icons/dev/ic_monochrome.png
flavors:
dev:
android: true
image_path: assets/icons/dev/app_icon.png
adaptive_icon_foreground: assets/icons/dev/ic_foreground.png
adaptive_icon_background: "#FEE440"
adaptive_icon_monochrome: assets/icons/dev/ic_monochrome.png
stg:
android: true
image_path: assets/icons/stg/app_icon.png
adaptive_icon_foreground: assets/icons/stg/ic_foreground.png
adaptive_icon_background: "#E0E0E0"
adaptive_icon_monochrome: assets/icons/stg/ic_monochrome.png
pro:
android: true
image_path: assets/icons/pro/app_icon.png
adaptive_icon_foreground: assets/icons/pro/ic_foreground.png
adaptive_icon_background: "#FFFFFF"
adaptive_icon_monochrome: assets/icons/pro/ic_monochrome.png
\ No newline at end of file
#!/bin/bash
# Script to copy icons for different flavors
# This script copies the appropriate icon files to the Android res directories
echo "Copying icons for different flavors..."
# Function to copy icons for a specific flavor
copy_icons_for_flavor() {
local flavor=$1
local source_dir="assets/icons/$flavor"
local target_dir="android/app/src/$flavor/res"
echo "Copying icons for flavor: $flavor"
# Create target directory if it doesn't exist
mkdir -p "$target_dir/mipmap-hdpi"
mkdir -p "$target_dir/mipmap-mdpi"
mkdir -p "$target_dir/mipmap-xhdpi"
mkdir -p "$target_dir/mipmap-xxhdpi"
mkdir -p "$target_dir/mipmap-xxxhdpi"
mkdir -p "$target_dir/drawable-hdpi"
mkdir -p "$target_dir/drawable-mdpi"
mkdir -p "$target_dir/drawable-xhdpi"
mkdir -p "$target_dir/drawable-xxhdpi"
mkdir -p "$target_dir/drawable-xxxhdpi"
mkdir -p "$target_dir/values"
# Copy app icon (you'll need to create different sizes)
if [ -f "$source_dir/app_icon.png" ]; then
cp "$source_dir/app_icon.png" "$target_dir/mipmap-hdpi/ic_launcher.png"
cp "$source_dir/app_icon.png" "$target_dir/mipmap-mdpi/ic_launcher.png"
cp "$source_dir/app_icon.png" "$target_dir/mipmap-xhdpi/ic_launcher.png"
cp "$source_dir/app_icon.png" "$target_dir/mipmap-xxhdpi/ic_launcher.png"
cp "$source_dir/app_icon.png" "$target_dir/mipmap-xxxhdpi/ic_launcher.png"
fi
# Copy foreground icon
if [ -f "$source_dir/ic_foreground.png" ]; then
cp "$source_dir/ic_foreground.png" "$target_dir/drawable-hdpi/ic_launcher_foreground.png"
cp "$source_dir/ic_foreground.png" "$target_dir/drawable-mdpi/ic_launcher_foreground.png"
cp "$source_dir/ic_foreground.png" "$target_dir/drawable-xhdpi/ic_launcher_foreground.png"
cp "$source_dir/ic_foreground.png" "$target_dir/drawable-xxhdpi/ic_launcher_foreground.png"
cp "$source_dir/ic_foreground.png" "$target_dir/drawable-xxxhdpi/ic_launcher_foreground.png"
fi
# Create colors.xml for this flavor
case $flavor in
"dev")
echo '<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FEE440</color>
</resources>' > "$target_dir/values/colors.xml"
;;
"stg")
echo '<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#E0E0E0</color>
</resources>' > "$target_dir/values/colors.xml"
;;
"pro")
echo '<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>' > "$target_dir/values/colors.xml"
;;
esac
echo "Icons copied for $flavor"
}
# Copy icons for each flavor
copy_icons_for_flavor "dev"
copy_icons_for_flavor "stg"
copy_icons_for_flavor "pro"
echo "Icon copying completed!"
#!/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!"
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