Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
C28130B8ACDE2425E043E8EF /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -348,23 +347,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
C28130B8ACDE2425E043E8EF /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
Expand Down
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterSkin.init(
apiKey:
"fsk_b4331e99326b000434d601a80284abf89b8425e5f246f3b76a924c9d04486012",
"fsk_9a8ea7f3a0deaf89470bdcfdd762b3d3c0fd63b086a08a24bcdd46c82755cd36",
Comment thread
koukibadr marked this conversation as resolved.
);
runApp(const MyApp());
}
Expand Down
10 changes: 10 additions & 0 deletions lib/flutter_skin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart' show ThemeData, ColorScheme;
import 'package:flutter/widgets.dart';
import 'package:flutter_skin/models/project_config.dart';
import 'package:flutter_skin/remote/fskin_remote_config.dart';
import 'package:flutter_skin/services/fskin_logger.dart';
import 'package:flutter_skin/services/fskin_subscriber.dart';

class FlutterSkin with WidgetsBindingObserver {
Expand All @@ -10,12 +11,14 @@ class FlutterSkin with WidgetsBindingObserver {
late String apiKey;
static final FskinSubscriber _sse = FskinSubscriber();
static Stream<ThemeData> get onSkinChanged => FskinRemoteConfig.onSkinChanged;
static final FskinLogger _logger = FskinLogger();

// Private constructor
FlutterSkin._();

static Future<FlutterSkin> init({required String apiKey}) async {
if (apiKey.trim().isEmpty) {
_logger.logError('apiKey must not be empty');
throw ArgumentError.value(apiKey, 'apiKey', 'apiKey must not be empty');
}
if (_instance == null) {
Expand All @@ -40,6 +43,9 @@ class FlutterSkin with WidgetsBindingObserver {

/// Start listening to the backend stream for skin and projects updates
static void _startStream() {
_logger.logMessage(
'Starting stream connection to listen for skin updates.',
);
_sse.listen(
apiKey: _instance!.apiKey,
onSkinUpdated: FskinRemoteConfig.singleton.fetchConfig,
Expand All @@ -51,13 +57,15 @@ class FlutterSkin with WidgetsBindingObserver {
// When the app is resumed, fetch the latest config and restart the stream.
if (state == AppLifecycleState.resumed) {
try {
_logger.logMessage('Fetching latest config and restarting stream.');
await FskinRemoteConfig.singleton.fetchConfig();
} finally {
_startStream();
}

// When the app is paused, dispose the stream to save resources.
} else if (state == AppLifecycleState.paused) {
_logger.logMessage('Disposing stream to save resources.');
_sse.dispose();
}
}
Expand All @@ -70,9 +78,11 @@ class FlutterSkin with WidgetsBindingObserver {
ThemeData remoteTheme = ThemeData(colorScheme: colors);
if (colors == null) {
if (fallbackTheme != null) {
_logger.logWarning('No active theme found. Returning fallback theme.');
return fallbackTheme;
}
} else {
_logger.logMessage('Active theme found. Returning remote theme.');
return remoteTheme;
}
return null;
Expand Down
40 changes: 40 additions & 0 deletions lib/services/fskin_logger.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'dart:developer';

class FskinLogger {
static final FskinLogger _instance = FskinLogger._();

FskinLogger._();

factory FskinLogger() {
return _instance;
}

void logMessage(String message) {
log(
'Fskin: $message',
level: 800,
time: DateTime.now(),
name: 'FskinLogger',
);
}

void logError(String error, {Object? errorObject}) {
log(
'Fskin Error: $error',
level: 1000,
time: DateTime.now(),
name: 'FskinLogger',
error: errorObject,
stackTrace: StackTrace.current,
);
}
Comment thread
koukibadr marked this conversation as resolved.

void logWarning(String warning) {
log(
'Fskin Warning: $warning',
level: 900,
time: DateTime.now(),
name: 'FskinLogger',
);
}
}
19 changes: 17 additions & 2 deletions lib/services/fskin_subscriber.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter_skin/constants/fskin_constants.dart';
import 'package:flutter_skin/services/fskin_logger.dart';
import 'package:http/http.dart' as http;

class FskinSubscriber {
StreamSubscription? _subscription;
late http.Client _client;
int _retryCount = 0;
bool _disposed = false;
final FskinLogger _logger = FskinLogger();

void listen({required String apiKey, required VoidCallback onSkinUpdated}) {
_disposed = false;
Expand All @@ -35,18 +37,28 @@ class FskinSubscriber {
(line) {
if (line.startsWith('event: skin_updated') ||
line.startsWith('event: project_updated')) {
_logger.logMessage('Skin update event received.');
onSkinUpdated();
}
},
onDone: () {
_logger.logMessage('Stream connection closed. Retrying...');
_retry(apiKey, baseUrl, onSkinUpdated);
},
onError: (_) {
onError: (e) {
_logger.logError(
'Stream connection error. Retrying...',
errorObject: e,
);
_retry(apiKey, baseUrl, onSkinUpdated);
},
);
})
.catchError((_) {
.catchError((e) {
_logger.logError(
'Stream connection error. Retrying...',
errorObject: e,
);
_retry(apiKey, baseUrl, onSkinUpdated);
});
Comment thread
koukibadr marked this conversation as resolved.
}
Expand All @@ -62,6 +74,9 @@ class FskinSubscriber {
}

void dispose() {
_logger.logMessage(
'Disposing FskinSubscriber and closing stream connection.',
);
_disposed = true;
_subscription?.cancel();
_client.close();
Expand Down
8 changes: 8 additions & 0 deletions lib/services/skin_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import 'dart:convert';

import 'package:flutter_skin/constants/fskin_constants.dart';
import 'package:flutter_skin/models/project_config.dart';
import 'package:flutter_skin/services/fskin_logger.dart';
import 'package:http/http.dart' as http;

/// Singleton service class responsible for fetching the skin configuration from the remote server.
/// This class abstracts the network communication and provides a method to retrieve the ProjectConfig
class SkinService {
static final SkinService _instance = SkinService._();
final FskinLogger _logger = FskinLogger();

SkinService._();

Expand All @@ -16,6 +18,7 @@ class SkinService {
}

Future<ProjectConfig?> fetchData(String apiKey) async {
_logger.logMessage('Fetching skin configuration for the provided apiKey.');
var client = http.Client();
try {
var response = await client
Expand All @@ -27,13 +30,18 @@ class SkinService {
.timeout(const Duration(seconds: 5));

if (response.statusCode != 200) {
_logger.logError(
'Error fetching skin configuration: ${response.statusCode}',
errorObject: response,
);
return null;
}

var decodedResponse =
jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
return ProjectConfig.fromMap(decodedResponse);
} catch (e) {
_logger.logError('Error fetching skin configuration: $e', errorObject: e);
return null;
} finally {
client.close();
Expand Down
Loading