深色模式
Flutter 组件暴露方法
在Flutter中,让StatefulWidget暴露方法给外部使用有几种常见的设计模式,我来介绍几种最实用的方法:
GlobalKey 方式(最直接)
dart
// Widget定义
class VideoPlayer extends StatefulWidget {
const VideoPlayer({Key? key}) : super(key: key);
@override
State<VideoPlayer> createState() => VideoPlayerState();
}
class VideoPlayerState extends State<VideoPlayer> {
bool isPlaying = false;
// 暴露给外部的方法
void play() {
setState(() {
isPlaying = true;
});
}
void pause() {
setState(() {
isPlaying = false;
});
}
void togglePlayPause() {
setState(() {
isPlaying = !isPlaying;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: Text(isPlaying ? '播放中' : '已暂停'),
);
}
}
// 使用方式
class ParentWidget extends StatelessWidget {
final GlobalKey<VideoPlayerState> _videoKey = GlobalKey<VideoPlayerState>();
@override
Widget build(BuildContext context) {
return Column(
children: [
VideoPlayer(key: _videoKey),
ElevatedButton(
onPressed: () => _videoKey.currentState?.play(),
child: Text('播放'),
),
ElevatedButton(
onPressed: () => _videoKey.currentState?.pause(),
child: Text('暂停'),
),
],
);
}
}
Controller 模式(推荐)
dart
// 控制器类
class VideoPlayerController extends ChangeNotifier {
bool _isPlaying = false;
double _volume = 1.0;
bool get isPlaying => _isPlaying;
double get volume => _volume;
void play() {
_isPlaying = true;
notifyListeners();
}
void pause() {
_isPlaying = false;
notifyListeners();
}
void setVolume(double volume) {
_volume = volume;
notifyListeners();
}
void togglePlayPause() {
_isPlaying = !_isPlaying;
notifyListeners();
}
}
// Widget定义
class VideoPlayer extends StatefulWidget {
final VideoPlayerController controller;
const VideoPlayer({
Key? key,
required this.controller,
}) : super(key: key);
@override
State<VideoPlayer> createState() => _VideoPlayerState();
}
class _VideoPlayerState extends State<VideoPlayer> {
@override
void initState() {
super.initState();
widget.controller.addListener(_onControllerChanged);
}
@override
void dispose() {
widget.controller.removeListener(_onControllerChanged);
super.dispose();
}
void _onControllerChanged() {
setState(() {});
}
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Text(widget.controller.isPlaying ? '播放中' : '已暂停'),
Text('音量: ${widget.controller.volume}'),
],
),
);
}
}
// 使用方式
class ParentWidget extends StatefulWidget {
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
late VideoPlayerController _controller;
@override
void initState() {
super.initState();
_controller = VideoPlayerController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
VideoPlayer(controller: _controller),
ElevatedButton(
onPressed: _controller.play,
child: Text('播放'),
),
ElevatedButton(
onPressed: _controller.pause,
child: Text('暂停'),
),
],
);
}
}
回调函数方式
dart
// Widget定义
class VideoPlayer extends StatefulWidget {
final Function(VideoPlayerMethods)? onReady;
const VideoPlayer({
Key? key,
this.onReady,
}) : super(key: key);
@override
State<VideoPlayer> createState() => _VideoPlayerState();
}
class VideoPlayerMethods {
final VoidCallback play;
final VoidCallback pause;
final VoidCallback togglePlayPause;
VideoPlayerMethods({
required this.play,
required this.pause,
required this.togglePlayPause,
});
}
class _VideoPlayerState extends State<VideoPlayer> {
bool isPlaying = false;
@override
void initState() {
super.initState();
// 将方法暴露给父组件
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.onReady?.call(VideoPlayerMethods(
play: _play,
pause: _pause,
togglePlayPause: _togglePlayPause,
));
});
}
void _play() {
setState(() {
isPlaying = true;
});
}
void _pause() {
setState(() {
isPlaying = false;
});
}
void _togglePlayPause() {
setState(() {
isPlaying = !isPlaying;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: Text(isPlaying ? '播放中' : '已暂停'),
);
}
}
// 使用方式
class ParentWidget extends StatefulWidget {
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
VideoPlayerMethods? _videoMethods;
@override
Widget build(BuildContext context) {
return Column(
children: [
VideoPlayer(
onReady: (methods) {
setState(() {
_videoMethods = methods;
});
},
),
ElevatedButton(
onPressed: _videoMethods?.play,
child: Text('播放'),
),
ElevatedButton(
onPressed: _videoMethods?.pause,
child: Text('暂停'),
),
],
);
}
}
混合模式(Controller + GlobalKey)
dart
class VideoPlayerController extends ChangeNotifier {
VideoPlayerState? _state;
void _attach(VideoPlayerState state) {
_state = state;
}
void _detach() {
_state = null;
}
void play() {
_state?._play();
}
void pause() {
_state?._pause();
}
bool get isPlaying => _state?._isPlaying ?? false;
}
class VideoPlayer extends StatefulWidget {
final VideoPlayerController? controller;
const VideoPlayer({Key? key, this.controller}) : super(key: key);
@override
State<VideoPlayer> createState() => VideoPlayerState();
}
class VideoPlayerState extends State<VideoPlayer> {
bool _isPlaying = false;
@override
void initState() {
super.initState();
widget.controller?._attach(this);
}
@override
void dispose() {
widget.controller?._detach();
super.dispose();
}
void _play() {
setState(() {
_isPlaying = true;
});
}
void _pause() {
setState(() {
_isPlaying = false;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: Text(_isPlaying ? '播放中' : '已暂停'),
);
}
}
各种方式的对比
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
GlobalKey | 简单直接,类型安全 | 紧耦合,难以测试 | 简单场景,父子组件直接通信 |
Controller | 解耦,易测试,类似官方API | 需要额外管理Controller生命周期 | 复杂组件,需要状态管理 |
回调函数 | 灵活,解耦 | 回调地狱,方法多时不易管理 | 简单的事件通知 |
混合模式 | 兼具Controller和直接调用的优点 | 复杂度较高 | 复杂场景,需要高度可控性 |
推荐做法
- 简单场景:使用 GlobalKey 方式
- 复杂组件:使用 Controller 模式(类似
TextEditingController
、AnimationController
) - 事件通知:使用回调函数
- 框架级组件:使用混合模式
Controller 模式是最推荐的,因为它:
- 符合Flutter官方设计模式
- 易于测试和维护
- 解耦程度高
- 状态管理清晰