简体   繁体   中英

Show flutter overlay according to different variables

I have a flutter screen called TestMain which has a scaffold and white background. The scaffolds body is supposed to change if certain events happen. The events are stored as a boolean. There is "isLocked" and "isPaused" which get emitted by a Riverpod Stream Provider and "isCheating" which changes when Applifecyle events get triggered. All of the three booleans are stored as Riverpod StateProviders, because of its global accesibility.

This is is my "isCheatingProvider": final isCheatingProvider = StateProvider.autoDispose<bool>((ref) => false); The "isPausedProvider" and "isLockedProvider" are the same.

This is the TestMain screen

class TestMain extends ConsumerStatefulWidget {
  const TestMain({super.key});

  @override
  ConsumerState<TestMain> createState() => _TestMainScreenState();
}

class _TestMainScreenState extends ConsumerState<TestMain>
    with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    super.didChangeAppLifecycleState(state);

    final isCheating = ref.watch(isCheatingProvider.notifier);

    switch (state) {
      case AppLifecycleState.resumed:
      case AppLifecycleState.inactive:
        await sendCheatingAttempt(ref);
        setState(() {
          isCheating.state = true;
        });
        break;
      case AppLifecycleState.paused:
        await sendCheatingAttempt(ref);
        setState(() {
          isCheating.state = true;
        });
        break;
      case AppLifecycleState.detached:
        await sendCheatingAttempt(ref);
        setState(() {
          isCheating.state = true;
        });
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    final List<Item> items = ref.watch(itemsProvider);
    final AsyncValue<dynamic> wsTestListenerMessage =
        ref.watch(testListenerProvider);

    final isLocked = ref.watch(isLockedProvider.notifier);
    final isPaused = ref.watch(isPausedProvider.notifier);
    final isCheating = ref.watch(isCheatingProvider.notifier);

    wsTestListenerMessage.when(
      loading: () => {},
      error: (err, stack) => print('Test State Error: $err'),
      data: (message) async {
        Future.delayed(const Duration(seconds: 0), () {
          if (message["lock"] == true) {
            isLocked.state = true;
          }
          if (message["unlock"] == true) {
            isLocked.state = false;
          }
          if (message["paused"] == true) {
            isPaused.state = true;
          }
          if (message["resumed"] == true) {
            isPaused.state = false;
          }
        });
      },
    );

    return Scaffold(
      backgroundColor: Colors.white,
      body: SafeArea(
          child: isPaused.state
              ? const ErrorOverlay(text: 'paused')
              : isLocked.state || isCheating.state
                  ? const ErrorOverlay(text: 'cheating')
                  : const TestView()),
    );
  }
}

But it doesnt work. No matter what I do. I added the Future.delayed(const Duration(seconds: 0), () {} around the if-statements, because it complained about changing the provider in build method, I use setState() in didChangeAppLifecycleState() , but can't use it in the listener, because the listener would called over and over again. It shouldnt be openend more than once.

(ErrorOverlay is a custom widget that just shows the text in big red letters, in the center)

  • remove the setState , this will do nothing
  • for set a state use ref.read(provider.notifier).state
  • for watch use ref.watch(isCheatingProvider)

By changing all that it is good by testing on my side:

final isCheatingProvider = StateProvider.autoDispose<bool>((ref) => false);

class TestMain extends ConsumerStatefulWidget {
  const TestMain({key});

  @override
  ConsumerState<TestMain> createState() => _TestMainScreenState();
}

class _TestMainScreenState extends ConsumerState<TestMain>
    with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    super.didChangeAppLifecycleState(state);

    final isCheating = ref.read(isCheatingProvider.notifier);

    switch (state) {
      case AppLifecycleState.resumed:
      case AppLifecycleState.inactive:
        isCheating.state = true;
        break;
      case AppLifecycleState.paused:
        isCheating.state = true;
        break;
      case AppLifecycleState.detached:
        isCheating.state = true;
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    final isCheating = ref.watch(isCheatingProvider);

    return Scaffold(
      backgroundColor: isCheating ? Colors.red : Colors.white,
      body: SafeArea(
          child: isCheating ? Text('cheat') : Text(' good')
      )
    );
  }
}

You are incorrectly using StateProvider . To watch StateNotifier you should use

final isCheating = ref.watch(isCheatingProvider);

and to change provider use

ref.read(productSortTypeProvider.notifier).state = value;

So you have to change all provider related code.

@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
  super.didChangeAppLifecycleState(state);

  final isCheatingNotifier = ref.read(isCheatingProvider.notifier);

  switch (state) {
    case AppLifecycleState.resumed:
    case AppLifecycleState.inactive:
      await sendCheatingAttempt(ref);
      isCheatingNotifier.state = true;
      break;
    case AppLifecycleState.paused:
      await sendCheatingAttempt(ref);
      isCheatingNotifier.state = true;
      break;
    case AppLifecycleState.detached:
      await sendCheatingAttempt(ref);
      isCheatingNotifier.state = true;
      break;
  }
}

@override
Widget build(BuildContext context) {
  final List<Item> items = ref.watch(itemsProvider);
  final AsyncValue<dynamic> wsTestListenerMessage =
  ref.watch(testListenerProvider);

  final isLocked = ref.watch(isLockedProvider);
  final isPaused = ref.watch(isPausedProvider);
  final isCheating = ref.watch(isCheatingProvider);

  wsTestListenerMessage.when(
    loading: () => {},
    error: (err, stack) => print('Test State Error: $err'),
    data: (message) async {
      Future.delayed(const Duration(seconds: 0), () {
        final isLockedNotifier = ref.read(isLockedProvider.notifier);
        final isPausedNotifier = ref.read(isPausedProvider.notifier);

        if (message["lock"] == true) {
          isLockedNotifier.state = true;
        }
        if (message["unlock"] == true) {
          isLockedNotifier.state = false;
        }
        if (message["paused"] == true) {
          isPausedNotifier.state = true;
        }
        if (message["resumed"] == true) {
          isPausedNotifier.state = false;
        }
      });
    },
  );

  return Scaffold(
    backgroundColor: Colors.white,
    body: SafeArea(
        child: isPaused
            ? const ErrorOverlay(text: 'paused')
            : isLocked || isCheating
            ? const ErrorOverlay(text: 'cheating')
            : const TestView()),
  );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM