flutter-client-sdk icon indicating copy to clipboard operation
flutter-client-sdk copied to clipboard

Lastest version 4.11.2 not working for me

Open amrgetment opened this issue 5 months ago • 10 comments

Describe the bug When I upgrade to the latest version 4.11.2 it won't work at all

To reproduce Use version 4.11.2 and launch on macOS desktop target not iOS, it won't connect at all

Expected behavior It should work on macOS desktop

Logs No errors on console

SDK version 4.11.2

Language version, developer tools Flutter 3.35.3

OS/platform MacOS

Additional context macOS target, if I downgraded to 4.11.1 it will work

amrgetment avatar Sep 05 '25 07:09 amrgetment

Hi @amrgetment , thank you for bringing this to our attention. Is your app intended for other platforms as well? If so, does 4.12.2 work on non-macOS platforms? 4.12.2 includes a change to how we handle invalid contexts. Could you provide a snippet of your configuration/initialization logic including the context building logic.

Thank you.

tanderson-ld avatar Sep 05 '25 13:09 tanderson-ld

I tested with our example app and it works on macOS. Could you give that example app a test? There is a readme with run instructions that lets you pass your API key via command line environment.

Flutter 3.32.8 macOS 15.6.1

tanderson-ld avatar Sep 05 '25 14:09 tanderson-ld

I made debug and this was the error, what is the invalid context I need to check "LDClient was provided an invalid context. The context will be ignored. Existing flags will be used for evaluations until identif…"

class FeatureFlagService {
  FeatureFlagService._privateConstructor();

  static final FeatureFlagService instance =
      FeatureFlagService._privateConstructor();

  late final LDClient _launchDarklyClient;
  bool _started = false;
  final Completer<void> _completer = Completer<void>();

  Future<void> initialize() async {
    if (_completer.isCompleted) {
      return Future<void>.value();
    }

    _started = false;

    try {
      _launchDarklyClient = LDClient(
        LDConfig(
          // for web you need client side id not mobile key
          flavor.launchDarklyMobileKey,
          AutoEnvAttributes.enabled,
          // count of stored flag values for in on-device storage
          persistence: PersistenceConfig(maxCachedContexts: 200),
          logger: LDLogger(
            level: UniversalPlatform.isAndroid && !isProductionFlavor()
                ? LDLogLevel.info
                : LDLogLevel.none,
          ),
        ),
        LDContextBuilder().build(),
      );
    } catch (e) {
      critical('FeatureFlagService $e');
      _completer.completeError(e);
      return Future<void>.value();
    }

    _completer.complete();
    await _completer.future;

    await _listenLaunchDarklyFlags();
  }

  String flagsContext = 'Anonymous';

  Future<void> identify({UserData? user}) async {
    _started = false;
    flagsContext = 'Anonymous';

    final String userId = user?.id ?? '';
    final String userEmail = user?.email ?? '';
    final LDContext userContext = LDContextBuilder()
        .kind('user', userId)
        .setString('email', userEmail)
        .build();

    if (!_completer.isCompleted) {
      await initialize();
      await _completer.onCompleted(
        () async {
          await _launchDarklyClient.identify(
            userId.isNotEmptyOrNull()
                ? userContext
                : LDContextBuilder().build(),
          );

          flagsContext = 'kind:$userId, email:$userEmail';
        },
        onError: (dynamic err) => critical('FeatureFlagService $err'),
      );

      await _listenLaunchDarklyFlags();
      return;
    }

    await _launchDarklyClient.identify(
      userId.isNotEmptyOrNull() ? userContext : LDContextBuilder().build(),
    );
    flagsContext = 'kind:$userId, email:$userEmail';

    await _listenLaunchDarklyFlags();
  }

  void identifyAnonymous() => identify();

  LDClient? getLaunchDarklyClient() {
    if (!_completer.isCompleted) {
      errorLog('FeatureFlagService is not initialized');
      return null;
    }

    _listenLaunchDarklyFlags();

    return _launchDarklyClient;
  }

  Future<void> _listenLaunchDarklyFlags() async {
    if (_started) {
      return;
    }

    await _launchDarklyClient
        .start(waitForNetworkResults: true)
        .then(
          (bool started) {
            _started = started;
            if (!started) {
              critical('FeatureFlagService is not started');
              return;
            }

            FeatureFlags().logAllFlags(flagsContext: flagsContext);
          },
        )
        .onError((Object? error, StackTrace stackTrace) {
          critical('FeatureFlagService $error');
          critical('FeatureFlagService $stackTrace');
        });
  }
}

amrgetment avatar Sep 05 '25 20:09 amrgetment

if it is an easy fix from my side otherwise I will use the example test you mentioned

amrgetment avatar Sep 05 '25 20:09 amrgetment

Hey @amrgetment,

Thank you for getting back to us. There was a bug in the SDK which allowed for an invalid context to accidentally be used to initialize the SDK, which was causing some unpredictable behavior.

It sounds like you were encountering that case. All contexts must have at least 1 kind, so LDContextBuilder().build() likely just needs to be changed to LDContextBuilder().kind('user'),build() if you just want an anonymous user context when you don't have a user ID.

We will discuss if LDContextBuilder().build() should potentially just do that.

Thank you, Ryan

kinyoklion avatar Sep 12 '25 22:09 kinyoklion

@kinyoklion I used version ^4.13.0 and I tried your solution LDContextBuilder().kind('user') but it didn't work for me Image

amrgetment avatar Sep 28 '25 18:09 amrgetment

Hello @amrgetment did you still get the same log message?

kinyoklion avatar Sep 29 '25 15:09 kinyoklion

yes same error, here is the error while I am debugging

Image

amrgetment avatar Sep 29 '25 19:09 amrgetment

yes same error, here is the error while I am debugging

@amrgetment - I have the same exact issue. @kinyoklion the following chnage you suggested did not work.

LDContextBuilder().kind('user').build()

I could get it to work if I did the following stub with any placeholder string. I just need to provide a value.

LDContextBuilder().kind('user','<USER>').build()

Without this stub value in the initial client instance, using the identify method does not work:

client.identify(LDContextBuilder().kind('user', user_id).build())

TLDR, at least in my case, not specifying a value for your context in your initial client instance makes subsequent identify calls meaningless. You can imagine a scenario where the context is not known until some later time.

walsha2 avatar Oct 06 '25 19:10 walsha2

@kinyoklion Yes I confirm, this trick works. Should I close the issue or I keep it open so it will be the default behaviour? Thanks @walsha2

  LDContext getAnonymousUserContext() =>
      LDContextBuilder().kind('user', 'Anonymous').build();

amrgetment avatar Oct 07 '25 08:10 amrgetment