Проверьте, расположен ли виджет без состояния во флаттере

Когда мой виджет без сохранения состояния был создан, я воспроизводил некоторые звуки в последовательности, используя этот код:

await _audioPlayer.play(contentPath1, isLocal: true);
await Future.delayed(Duration(seconds: 4));
await _audioPlayer.play(contentPath2, isLocal: true);
await Future.delayed(Duration(seconds: 4));
await _audioPlayer.play(contentPath3, isLocal: true);

когда пользователь закрывает текущий виджет до окончания воспроизведения звуков, звуки по-прежнему работают даже после закрытия текущего маршрута с помощью этого кода:

Navigator.pop(context);

Мой обходной путь - использовать логическую переменную, чтобы указать, выполнено ли закрывающее действие.

Воспроизведение звукового кода:

await _audioPlayer.play(contentPath1, isLocal: true);
if (closed) return;
await Future.delayed(Duration(seconds: 4));
if (closed) return;
await _audioPlayer.play(contentPath2, isLocal: true);
if (closed) return;
await Future.delayed(Duration(seconds: 4));
if (closed) return;
await _audioPlayer.play(contentPath3, isLocal: true);

Закрытие текущего виджета:

closed = true;
_audioPlayer.stop();

Есть ли лучший способ остановить асинхронные методы, если мой виджет закрыт?

2 ответа

Решение

Если вы измените свой виджет на StatefulWidget, то у вас может быть такая функция:

void _playSounds() {
  await _audioPlayer.play(contentPath1, isLocal: true);
  await Future.delayed(Duration(seconds: 4));
  if (!mounted) return;

  await _audioPlayer.play(contentPath2, isLocal: true);
  await Future.delayed(Duration(seconds: 4));
  if (!mounted) return;

  await _audioPlayer.play(contentPath3, isLocal: true);
}

а затем в методе dispose просто удалите игрока:

@override
void dispose() {
  _audioPlayer?.dipose();
  super.dispose();
}

Геттер в настоящее время объединен в основной канал, но он по-прежнему недоступен в стабильном канале.

https://github.com/флюттер/флаттер/тянуть/111619

А пока у нас есть 2 варианта

  1. Использовать Stateful Widget напрямую (как принятый ответ)
  2. Реализуйте оболочку Stateful Widget с помощью метода Builder для передачиmountedгеттер тоже в обертке.

Виджет-оболочка

      import 'package:flutter/material.dart';

class PrimitiveWrapper<T> {
  T value;

  PrimitiveWrapper(this.value);
}

class MountedWrapper extends StatefulWidget {
  final Widget Function(BuildContext context, PrimitiveWrapper<bool> mounted) builder;

  const MountedWrapper({
    required this.builder,
    super.key,
  });

  @override
  State<MountedWrapper> createState() => _MountedWrapperState();
}

class _MountedWrapperState extends State<MountedWrapper> {
  @override
  Widget build(BuildContext context) {
    return widget.builder.call(context, PrimitiveWrapper(mounted));
  }
}

Применение

      class SubmitBtn extends StatelessWidget {

  ...

  Future<void> onSubmit(WidgetRef ref, BuildContext context, PrimitiveWrapper<bool> mounted) async {
    ...

    await topicRepo.add(topic);

    if (!mounted.value) {
      return;
    }

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('Created topic'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MountedWrapper(
      builder: (context, mounted) {
        return Consumer(
          builder: (context, ref, child) {
            return ElevatedButton(
              onPressed: () {
                onSubmit(ref, context, mounted);
              },
              child: const Text('Create'),
            );
          },
        );
      },
    );
  }
}
Другие вопросы по тегам