【发布时间】:2021-04-05 05:21:26
【问题描述】:
我以为我理解RepaintBoundary,但现在我不明白。
背景
我写了this answer 描述了如何在一个必须绘制很多内容的小部件周围添加RepaintBoundary,以防止重绘小部件树的其他部分。这按预期工作。
现在有问题
我现在正在尝试制作一个真实的示例,其中正在根据音频播放器流在 StreamBuilder 内重建小部件。我尝试将整个 StreamBuilder 包装在 RepaintBoundary 中,如下所示:
@override
Widget build(BuildContext context) {
print("building app");
return Scaffold(
body: Column(
children: [
Spacer(),
RepaintBoundary(
child: ProgressBarWidget(
durationState: _durationState, player: _player),
),
RepaintBoundary(
child: PlayPauseButton(player: _player),
),
],
),
);
}
但 UI 的其余部分仍在重新绘制(除了播放/暂停按钮,我也包裹在 RepaintBoundary 中)。
ProgressBarWidget 的构建方法如下所示:
@override
Widget build(BuildContext context) {
print('building progress bar');
return StreamBuilder<DurationState>(
stream: _durationState,
builder: (context, snapshot) {
final durationState = snapshot.data;
final progress = durationState?.progress ?? Duration.zero;
final buffered = durationState?.buffered ?? Duration.zero;
final total = durationState?.total ?? Duration.zero;
return ProgressBar(
progress: progress,
buffered: buffered,
total: total,
onSeek: (duration) {
_player.seek(duration);
},
);
},
);
}
但如果我像这样删除StreamBuilder:
@override
Widget build(BuildContext context) {
print('building progress bar');
return ProgressBar(
progress: Duration.zero,
total: Duration(minutes: 5),
onSeek: (duration) {
_player.seek(duration);
},
);
}
然后当我手动移动拇指时,重绘边界再次起作用。
StreamBuilder 是什么导致RepaintBoundary 不起作用?
完整代码
小部件布局的完整代码在这里:
import 'package:flutter/material.dart';
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:flutter/rendering.dart';
import 'package:just_audio/just_audio.dart';
import 'package:rxdart/rxdart.dart';
void main() {
debugRepaintTextRainbowEnabled = true;
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: HomeWidget(),
);
}
}
class HomeWidget extends StatefulWidget {
@override
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
AudioPlayer _player;
final url = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3';
Stream<DurationState> _durationState;
@override
void initState() {
super.initState();
_player = AudioPlayer();
_durationState = Rx.combineLatest2<Duration, PlaybackEvent, DurationState>(
_player.positionStream,
_player.playbackEventStream,
(position, playbackEvent) => DurationState(
progress: position,
buffered: playbackEvent.bufferedPosition,
total: playbackEvent.duration,
));
_init();
}
Future<void> _init() async {
try {
await _player.setUrl(url);
} catch (e) {
print("An error occured $e");
}
}
@override
Widget build(BuildContext context) {
print("building app");
return Scaffold(
body: Column(
children: [
Spacer(),
RepaintBoundary(
child: ProgressBarWidget(
durationState: _durationState, player: _player),
),
RepaintBoundary(
child: PlayPauseButton(player: _player),
),
],
),
);
}
}
class ProgressBarWidget extends StatelessWidget {
const ProgressBarWidget({
Key key,
@required Stream<DurationState> durationState,
@required AudioPlayer player,
}) : _durationState = durationState,
_player = player,
super(key: key);
final Stream<DurationState> _durationState;
final AudioPlayer _player;
@override
Widget build(BuildContext context) {
print('building progress bar');
return StreamBuilder<DurationState>(
stream: _durationState,
builder: (context, snapshot) {
final durationState = snapshot.data;
final progress = durationState?.progress ?? Duration.zero;
final buffered = durationState?.buffered ?? Duration.zero;
final total = durationState?.total ?? Duration.zero;
return ProgressBar(
progress: progress,
buffered: buffered,
total: total,
onSeek: (duration) {
_player.seek(duration);
},
);
},
);
// ProgressBar(
// progress: Duration.zero,
// total: Duration(minutes: 5),
// onSeek: (duration) {
// _player.seek(duration);
// },
// );
}
}
class PlayPauseButton extends StatelessWidget {
const PlayPauseButton({
Key key,
@required AudioPlayer player,
}) : _player = player,
super(key: key);
final AudioPlayer _player;
@override
Widget build(BuildContext context) {
print('building play/pause button');
return StreamBuilder<PlayerState>(
stream: _player.playerStateStream,
builder: (context, snapshot) {
final playerState = snapshot.data;
final processingState = playerState?.processingState;
final playing = playerState?.playing;
if (processingState == ProcessingState.loading ||
processingState == ProcessingState.buffering) {
return Container(
margin: EdgeInsets.all(8.0),
width: 64.0,
height: 64.0,
child: CircularProgressIndicator(),
);
} else if (playing != true) {
return IconButton(
icon: Icon(Icons.play_arrow),
iconSize: 64.0,
onPressed: _player.play,
);
} else if (processingState != ProcessingState.completed) {
return IconButton(
icon: Icon(Icons.pause),
iconSize: 64.0,
onPressed: _player.pause,
);
} else {
return IconButton(
icon: Icon(Icons.replay),
iconSize: 64.0,
onPressed: () => _player.seek(Duration.zero),
);
}
},
);
}
}
class DurationState {
const DurationState({this.progress, this.buffered, this.total});
final Duration progress;
final Duration buffered;
final Duration total;
}
【问题讨论】:
标签: flutter rendering paint stream-builder