Проверьте, расположен ли виджет без состояния во флаттере
Когда мой виджет без сохранения состояния был создан, я воспроизводил некоторые звуки в последовательности, используя этот код:
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 варианта
- Использовать Stateful Widget напрямую (как принятый ответ)
- Реализуйте оболочку 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'),
);
},
);
},
);
}
}