Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Flutterで動画を再生する

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

 Flutterで動画を再生する

Avatar for Kentaro Kawakami

Kentaro Kawakami

February 24, 2021
Tweet

More Decks by Kentaro Kawakami

Other Decks in Programming

Transcript

  1. ύοέʔδͷ঺հ • video_player • pub.dev • GitHub • flutter͕ग़͍ͯ͠Δ •

    iOS/Android/WebʹରԠ • iOS͸Objective-CɺAndroid͸Java
  2. ϩʔΧϧϑΝΠϧɾΞηοτͷ࠶ੜ class LocalVideo extends StatefulWidget { @override _LocalVideoState createState() =>

    _LocalVideoState(); } class _LocalVideoState extends State<LocalVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); // _controller = VideoPlayerController.file(File('file path')); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  3. ϩʔΧϧϑΝΠϧɾΞηοτͷ࠶ੜ class LocalVideo extends StatefulWidget { @override _LocalVideoState createState() =>

    _LocalVideoState(); } class _LocalVideoState extends State<LocalVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); // _controller = VideoPlayerController.file(File('file path')); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  4. ϩʔΧϧϑΝΠϧɾΞηοτͷ࠶ੜ class LocalVideo extends StatefulWidget { @override _LocalVideoState createState() =>

    _LocalVideoState(); } class _LocalVideoState extends State<LocalVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); // _controller = VideoPlayerController.file(File('file path')); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  5. ϩʔΧϧϑΝΠϧɾΞηοτͷ࠶ੜ class LocalVideo extends StatefulWidget { @override _LocalVideoState createState() =>

    _LocalVideoState(); } class _LocalVideoState extends State<LocalVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); // _controller = VideoPlayerController.file(File('file path')); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  6. ϩʔΧϧϑΝΠϧɾΞηοτͷ࠶ੜ class LocalVideo extends StatefulWidget { @override _LocalVideoState createState() =>

    _LocalVideoState(); } class _LocalVideoState extends State<LocalVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); // _controller = VideoPlayerController.file(File('file path')); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  7. ϩʔΧϧϑΝΠϧɾΞηοτͷ࠶ੜ class LocalVideo extends StatefulWidget { @override _LocalVideoState createState() =>

    _LocalVideoState(); } class _LocalVideoState extends State<LocalVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); // _controller = VideoPlayerController.file(File('file path')); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  8. ϦϞʔτϑΝΠϧͷ࠶ੜ class RemoteVideo extends StatefulWidget { @override _RemoteVideoState createState() =>

    _RemoteVideoState(); } class _RemoteVideoState extends State<RemoteVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.network('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  9. ϦϞʔτϑΝΠϧͷ࠶ੜ class RemoteVideo extends StatefulWidget { @override _RemoteVideoState createState() =>

    _RemoteVideoState(); } class _RemoteVideoState extends State<RemoteVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.network('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  10. ϦϞʔτϑΝΠϧͷ࠶ੜ class RemoteVideo extends StatefulWidget { @override _RemoteVideoState createState() =>

    _RemoteVideoState(); } class _RemoteVideoState extends State<RemoteVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.network('https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8'); _controller.initialize(); _controller.play(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return … VideoPlayer(_controller); … } }
  11. ࣈນ class RemoteVideo extends StatefulWidget { … } class _RemoteVideoState

    extends State<RemoteVideo> { late VideoPlayerController _controller; Future<ClosedCaptionFile> _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context).loadString('assets/bumble_bee_captions.srt'); return SubRipCaptionFile(fileContents); } @override void initState() { super.initState(); _controller = VideoPlayerController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', closedCaptionFile: _loadCaptions(), ); _controller.initialize(); _controller.play(); } … @override Widget build(BuildContext context) { return … Stack( children: <Widget>[ VideoPlayer(_controller), ClosedCaption(text: _controller.value.caption.text), ], ); … } }
  12. ࣈນ class RemoteVideo extends StatefulWidget { … } class _RemoteVideoState

    extends State<RemoteVideo> { late VideoPlayerController _controller; Future<ClosedCaptionFile> _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context).loadString('assets/bumble_bee_captions.srt'); return SubRipCaptionFile(fileContents); } @override void initState() { super.initState(); _controller = VideoPlayerController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', closedCaptionFile: _loadCaptions(), ); _controller.initialize(); _controller.play(); } … @override Widget build(BuildContext context) { return … Stack( children: <Widget>[ VideoPlayer(_controller), ClosedCaption(text: _controller.value.caption.text), ], ); … } }
  13. ࣈນ class RemoteVideo extends StatefulWidget { … } class _RemoteVideoState

    extends State<RemoteVideo> { late VideoPlayerController _controller; Future<ClosedCaptionFile> _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context).loadString('assets/bumble_bee_captions.srt'); return SubRipCaptionFile(fileContents); } @override void initState() { super.initState(); _controller = VideoPlayerController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', closedCaptionFile: _loadCaptions(), ); _controller.initialize(); _controller.play(); } … @override Widget build(BuildContext context) { return … Stack( children: <Widget>[ VideoPlayer(_controller), ClosedCaption(text: _controller.value.caption.text), ], ); … } }
  14. ࣈນ class RemoteVideo extends StatefulWidget { … } class _RemoteVideoState

    extends State<RemoteVideo> { late VideoPlayerController _controller; Future<ClosedCaptionFile> _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context).loadString('assets/bumble_bee_captions.srt'); return SubRipCaptionFile(fileContents); } @override void initState() { super.initState(); _controller = VideoPlayerController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', closedCaptionFile: _loadCaptions(), ); _controller.initialize(); _controller.play(); } … @override Widget build(BuildContext context) { return … Stack( children: <Widget>[ VideoPlayer(_controller), ClosedCaption(text: _controller.value.caption.text), ], ); … } }
  15. γʔΫόʔ class RemoteVideo extends StatefulWidget { … } class _RemoteVideoState

    extends State<RemoteVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.network('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'); _controller.initialize(); _controller.play(); } … @override Widget build(BuildContext context) { return … Stack( alignment: Alignment.bottomCenter, children: <Widget>[ VideoPlayer(_controller), VideoProgressIndicator(_controller, allowScrubbing: true), ], ); … } }
  16. γʔΫόʔ class RemoteVideo extends StatefulWidget { … } class _RemoteVideoState

    extends State<RemoteVideo> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.network('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'); _controller.initialize(); _controller.play(); } … @override Widget build(BuildContext context) { return … Stack( alignment: Alignment.bottomCenter, children: <Widget>[ VideoPlayer(_controller), VideoProgressIndicator(_controller, allowScrubbing: true), ], ); … } }
  17. ϥΠϑαΠΫϧʹ߹Θͤͯ࠶ੜʗఀࢭ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { _VideoAppLifeCycleObserver(this._controller); bool

    _wasPlayingBeforePause = false; final VideoPlayerController _controller; void initialize() { WidgetsBinding.instance!.addObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.paused: _wasPlayingBeforePause = _controller.value.isPlaying; _controller.pause(); break; case AppLifecycleState.resumed: if (_wasPlayingBeforePause) { _controller.play(); } break; default: } } void dispose() { WidgetsBinding.instance!.removeObserver(this); } }
  18. ϥΠϑαΠΫϧʹ߹Θͤͯ࠶ੜʗఀࢭ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { _VideoAppLifeCycleObserver(this._controller); bool

    _wasPlayingBeforePause = false; final VideoPlayerController _controller; void initialize() { WidgetsBinding.instance!.addObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.paused: _wasPlayingBeforePause = _controller.value.isPlaying; _controller.pause(); break; case AppLifecycleState.resumed: if (_wasPlayingBeforePause) { _controller.play(); } break; default: } } void dispose() { WidgetsBinding.instance!.removeObserver(this); } }
  19. ग़དྷͳ͍͜ͱ • ը࣭ઃఆ • ϏοτϨʔτΛઃఆͰ͖ͳ͍ • ࣈນɾ෭Ի੠ • ετϦʔϜʹ৐ͬͯΔࣈນɾԻ੠ͷऔಘɺબ୒͕Ͱ͖ͳ͍ •

    UI • ࣗ෼Ͱ࣮૷͢Δඞཁ͕͋Δ • DRM flutter#24923 • Cookieͷઃఆ • ʢΤϥʔϋϯυϦϯάʣ
  20. ग़དྷͳ͍͜ͱ • ը࣭ઃఆ • ϏοτϨʔτΛઃఆͰ͖ͳ͍ • ࣈນɾ෭Ի੠ • ετϦʔϜʹ৐ͬͯΔࣈນɾԻ੠ͷऔಘɺબ୒͕Ͱ͖ͳ͍ •

    UI • ࣗ෼Ͱ࣮૷͢Δඞཁ͕͋Δ • DRM flutter#24923 • Cookieͷઃఆ • ʢΤϥʔϋϯυϦϯάʣ
  21. ग़དྷͳ͍͜ͱ • ը࣭ઃఆ • ϏοτϨʔτΛઃఆͰ͖ͳ͍ • ࣈນɾ෭Ի੠ • ετϦʔϜʹ৐ͬͯΔࣈນɾԻ੠ͷऔಘɺબ୒͕Ͱ͖ͳ͍ •

    UI • ࣗ෼Ͱ࣮૷͢Δඞཁ͕͋Δ • DRM flutter#24923 • Cookieͷઃఆ • ʢΤϥʔϋϯυϦϯάʣ
  22. ग़དྷͳ͍͜ͱ • ը࣭ઃఆ • ϏοτϨʔτΛઃఆͰ͖ͳ͍ • ࣈນɾ෭Ի੠ • ετϦʔϜʹ৐ͬͯΔࣈນɾԻ੠ͷऔಘɺબ୒͕Ͱ͖ͳ͍ •

    UI • ࣗ෼Ͱ࣮૷͢Δඞཁ͕͋Δ • DRM flutter#24923 • Cookieͷઃఆ • ʢΤϥʔϋϯυϦϯάʣ
  23. ग़དྷͳ͍͜ͱ • ը࣭ઃఆ • ϏοτϨʔτΛઃఆͰ͖ͳ͍ • ࣈນɾ෭Ի੠ • ετϦʔϜʹ৐ͬͯΔࣈນɾԻ੠ͷऔಘɺબ୒͕Ͱ͖ͳ͍ •

    UI • ࣗ෼Ͱ࣮૷͢Δඞཁ͕͋Δ • DRM flutter#24923 • Cookieͷઃఆ • ʢΤϥʔϋϯυϦϯάʣ
  24. ग़དྷͳ͍͜ͱ • ը࣭ઃఆ • ϏοτϨʔτΛઃఆͰ͖ͳ͍ • ࣈນɾ෭Ի੠ • ετϦʔϜʹ৐ͬͯΔࣈນɾԻ੠ͷऔಘɺબ୒͕Ͱ͖ͳ͍ •

    UI • ࣗ෼Ͱ࣮૷͢Δඞཁ͕͋Δ • DRM flutter#24923 • Cookieͷઃఆ • ʢΤϥʔϋϯυϦϯάʣ
  25. ग़དྷͳ͍͜ͱ • ը࣭ઃఆ • ϏοτϨʔτΛઃఆͰ͖ͳ͍ • ࣈນɾ෭Ի੠ • ετϦʔϜʹ৐ͬͯΔࣈນɾԻ੠ͷऔಘɺબ୒͕Ͱ͖ͳ͍ •

    UI • ࣗ෼Ͱ࣮૷͢Δඞཁ͕͋Δ • DRM flutter#24923 • Cookieͷઃఆ • ʢΤϥʔϋϯυϦϯάʣ
  26. ΤϥʔϋϯυϦϯά class VideoPlayerController extends ValueNotifier<VideoPlayerValue> { … } … class

    VideoPlayerValue { … final Duration duration; final Duration position; final Caption caption; final List<DurationRange> buffered; final bool isPlaying; final bool isLooping; final bool isBuffering; final double volume; final double playbackSpeed; final String? errorDescription; final Size size; final bool isInitialized; bool get hasError => errorDescription != null; … }
  27. ΤϥʔϋϯυϦϯά class VideoPlayerController extends ValueNotifier<VideoPlayerValue> { … } … class

    VideoPlayerValue { … final Duration duration; final Duration position; final Caption caption; final List<DurationRange> buffered; final bool isPlaying; final bool isLooping; final bool isBuffering; final double volume; final double playbackSpeed; final String? errorDescription; final Size size; final bool isInitialized; bool get hasError => errorDescription != null; … }
  28. ΤϥʔϋϯυϦϯά class VideoPlayerController extends ValueNotifier<VideoPlayerValue> { … } … class

    VideoPlayerValue { … final Duration duration; final Duration position; final Caption caption; final List<DurationRange> buffered; final bool isPlaying; final bool isLooping; final bool isBuffering; final double volume; final double playbackSpeed; final String? errorDescription; final Size size; final bool isInitialized; bool get hasError => errorDescription != null; … }
  29. ΤϥʔϋϯυϦϯά // iOS _eventSink([FlutterError errorWithCode:@"VideoError" message:@"Video cannot be fast-forwarded beyond

    2.0x" details:nil]); _eventSink([FlutterError errorWithCode:@"VideoError" message:[@"Failed to load video: “ stringByAppendingString:[item.error localizedDescription]] details:nil]); // Android eventSink.error("VideoError", "Video player had error " + error, null);
  30. ΤϥʔϋϯυϦϯά // iOS … AVPlayerItem* item = (AVPlayerItem*)object; switch (item.status)

    { case AVPlayerItemStatusFailed: if (_eventSink != nil) { _eventSink([FlutterError errorWithCode:@"VideoError" message:[@"Failed to load video: " stringByAppendingString:[item.error localizedDescription]] details:nil]); } break; case AVPlayerItemStatusUnknown: break; case AVPlayerItemStatusReadyToPlay: [item addOutput:_videoOutput]; [self sendInitialized]; [self updatePlayingState]; break; } …
  31. ΤϥʔϋϯυϦϯά // iOS … AVPlayerItem* item = (AVPlayerItem*)object; switch (item.status)

    { case AVPlayerItemStatusFailed: if (_eventSink != nil) { _eventSink([FlutterError errorWithCode:@"VideoError" message:[@"Failed to load video: " stringByAppendingString:[item.error localizedDescription]] details:nil]); } break; case AVPlayerItemStatusUnknown: break; case AVPlayerItemStatusReadyToPlay: [item addOutput:_videoOutput]; [self sendInitialized]; [self updatePlayingState]; break; } …
  32. ΤϥʔϋϯυϦϯά // Android exoPlayer.addListener( new EventListener() { … @Override public

    void onPlayerError(final ExoPlaybackException error) { setBuffering(false); if (eventSink != null) { eventSink.error("VideoError", "Video player had error " + error, null); } } });
  33. ΤϥʔϋϯυϦϯά // Android exoPlayer.addListener( new EventListener() { … @Override public

    void onPlayerError(final ExoPlaybackException error) { setBuffering(false); if (eventSink != null) { eventSink.error("VideoError", "Video player had error " + error, null); } } });
  34. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } }
  35. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } } class TextureMessage { int? textureId; Object encode() { final Map<Object?, Object?> pigeonMap = <Object?, Object?>{}; pigeonMap['textureId'] = textureId; return pigeonMap; } static TextureMessage decode(Object message) { final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>; return TextureMessage()..textureId = pigeonMap['textureId'] as int; } }
  36. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } } class TextureMessage { int? textureId; Object encode() { final Map<Object?, Object?> pigeonMap = <Object?, Object?>{}; pigeonMap['textureId'] = textureId; return pigeonMap; } static TextureMessage decode(Object message) { final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>; return TextureMessage()..textureId = pigeonMap['textureId'] as int; } }
  37. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } } class TextureMessage { int? textureId; Object encode() { final Map<Object?, Object?> pigeonMap = <Object?, Object?>{}; pigeonMap['textureId'] = textureId; return pigeonMap; } static TextureMessage decode(Object message) { final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>; return TextureMessage()..textureId = pigeonMap['textureId'] as int; } }
  38. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } } class TextureMessage { int? textureId; Object encode() { final Map<Object?, Object?> pigeonMap = <Object?, Object?>{}; pigeonMap['textureId'] = textureId; return pigeonMap; } static TextureMessage decode(Object message) { final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>; return TextureMessage()..textureId = pigeonMap['textureId'] as int; } }
  39. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } }
  40. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } } Channel͸APIຖʹ࡞੒
  41. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } }
  42. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } }
  43. BasicMessageChannel Future<PositionMessage> position(TextureMessage arg) async { final Object encoded =

    arg.encode(); const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); final Map<Object?, Object?>? replyMap = await channel.send(encoded) as Map<Object?, Object?>?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', details: null, ); } else if (replyMap['error'] != null) { final Map<Object?, Object?> error = replyMap['error'] as Map<Object?, Object?>; throw PlatformException( code: error['code'] as String, message: error['message'] as String?, details: error['details'], ); } else { return PositionMessage.decode(replyMap['result']!); } }
  44. Dart // VideoPlayerController @override Widget build(BuildContext context) { return _textureId

    == VideoPlayerController.kUninitializedTextureId ? Container() : _videoPlayerPlatform.buildView(_textureId); } // video_player_platform_interface @override Widget buildView(int textureId) { return Texture(textureId: textureId); }
  45. Dart // VideoPlayerController @override Widget build(BuildContext context) { return _textureId

    == VideoPlayerController.kUninitializedTextureId ? Container() : _videoPlayerPlatform.buildView(_textureId); } // video_player_platform_interface @override Widget buildView(int textureId) { return Texture(textureId: textureId); }
  46. Dart // VideoPlayerController @override Widget build(BuildContext context) { return _textureId

    == VideoPlayerController.kUninitializedTextureId ? Container() : _videoPlayerPlatform.buildView(_textureId); } // video_player_platform_interface @override Widget buildView(int textureId) { return Texture(textureId: textureId); }
  47. iOS @protocol FlutterTexture <NSObject> /** Copy the contents of the

    texture into a `CVPixelBuffer`. */ - (CVPixelBufferRef _Nullable)copyPixelBuffer; /** * Called when the texture is unregistered. * * Called on the raster thread. */ @optional - (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture; @end FlutterTexture
  48. iOS @protocol FlutterTexture <NSObject> /** Copy the contents of the

    texture into a `CVPixelBuffer`. */ - (CVPixelBufferRef _Nullable)copyPixelBuffer; /** * Called when the texture is unregistered. * * Called on the raster thread. */ @optional - (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture; @end FlutterTexture
  49. iOS @protocol FlutterTexture <NSObject> /** Copy the contents of the

    texture into a `CVPixelBuffer`. */ - (CVPixelBufferRef _Nullable)copyPixelBuffer; /** * Called when the texture is unregistered. * * Called on the raster thread. */ @optional - (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture; @end FlutterTexture - (CVPixelBufferRef)copyPixelBuffer { CMTime outputItemTime = [_videoOutput itemTimeForHostTime:CACurrentMediaTime()]; if ([_videoOutput hasNewPixelBufferForItemTime:outputItemTime]) { return [_videoOutput copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL]; } else { return NULL; } }
  50. iOS @protocol FlutterTexture <NSObject> /** Copy the contents of the

    texture into a `CVPixelBuffer`. */ - (CVPixelBufferRef _Nullable)copyPixelBuffer; /** * Called when the texture is unregistered. * * Called on the raster thread. */ @optional - (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture; @end FlutterTexture - (CVPixelBufferRef)copyPixelBuffer { CMTime outputItemTime = [_videoOutput itemTimeForHostTime:CACurrentMediaTime()]; if ([_videoOutput hasNewPixelBufferForItemTime:outputItemTime]) { return [_videoOutput copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL]; } else { return NULL; } }
  51. iOS @protocol FlutterTexture <NSObject> /** Copy the contents of the

    texture into a `CVPixelBuffer`. */ - (CVPixelBufferRef _Nullable)copyPixelBuffer; /** * Called when the texture is unregistered. * * Called on the raster thread. */ @optional - (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture; @end FlutterTexture
  52. iOS @protocol FlutterTexture <NSObject> /** Copy the contents of the

    texture into a `CVPixelBuffer`. */ - (CVPixelBufferRef _Nullable)copyPixelBuffer; /** * Called when the texture is unregistered. * * Called on the raster thread. */ @optional - (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture; @end FlutterTexture - (void)onTextureUnregistered:(NSObject<FlutterTexture>*)texture { dispatch_async(dispatch_get_main_queue(), ^{ [self dispose]; }); }
  53. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry
  54. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry
  55. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry … int64_t textureId = [_registry registerTexture:player]; …
  56. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry … int64_t textureId = [_registry registerTexture:player]; … textureId͕ฦΔ
  57. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry
  58. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry • FlutterʹTexture͕ߋ৽͞Εͨͱ఻͑Δ • FlutterTexture.copyPixcelBuffer()͕ݺͼ͞ΕΔ
  59. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry _displayLink = [CADisplayLink displayLinkWithTarget:frameUpdater selector:@selector(onDisplayLink:)]; ""... - (void)onDisplayLink:(CADisplayLink*)link { [_registry textureFrameAvailable:_textureId]; }
  60. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry _displayLink = [CADisplayLink displayLinkWithTarget:frameUpdater selector:@selector(onDisplayLink:)]; ""... - (void)onDisplayLink:(CADisplayLink*)link { [_registry textureFrameAvailable:_textureId]; }
  61. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry _displayLink = [CADisplayLink displayLinkWithTarget:frameUpdater selector:@selector(onDisplayLink:)]; ""... - (void)onDisplayLink:(CADisplayLink*)link { [_registry textureFrameAvailable:_textureId]; }
  62. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry
  63. iOS @protocol FlutterTextureRegistry <NSObject> /** * Registers a `FlutterTexture` for

    usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the * platform thread. */ - (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture; /** * Notifies Flutter that the content of the previously registered texture has been updated. * * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures * must be unregistered on the the platform thread. * * @param textureId The result that was previously returned from `registerTexture:`. */ - (void)unregisterTexture:(int64_t)textureId; @end FlutterTextureRegistry - (void)dispose:(FLTTextureMessage*)input error:(FlutterError**)error { … [_registry unregisterTexture:input.textureId.intValue];
  64. Android public interface TextureRegistry { /** * Creates and registers

    a SurfaceTexture managed by the Flutter engine. * * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); /** A registry entry for a managed SurfaceTexture. */ interface SurfaceTextureEntry { /** @return The managed SurfaceTexture. */ SurfaceTexture surfaceTexture(); /** @return The identity of this SurfaceTexture. */ long id(); /** Deregisters and releases this SurfaceTexture. */ void release(); } } TextureRegistry
  65. Android public interface TextureRegistry { /** * Creates and registers

    a SurfaceTexture managed by the Flutter engine. * * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); /** A registry entry for a managed SurfaceTexture. */ interface SurfaceTextureEntry { /** @return The managed SurfaceTexture. */ SurfaceTexture surfaceTexture(); /** @return The identity of this SurfaceTexture. */ long id(); /** Deregisters and releases this SurfaceTexture. */ void release(); } } TextureRegistry
  66. Android public interface TextureRegistry { /** * Creates and registers

    a SurfaceTexture managed by the Flutter engine. * * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); /** A registry entry for a managed SurfaceTexture. */ interface SurfaceTextureEntry { /** @return The managed SurfaceTexture. */ SurfaceTexture surfaceTexture(); /** @return The identity of this SurfaceTexture. */ long id(); /** Deregisters and releases this SurfaceTexture. */ void release(); } } TextureRegistry ... TextureRegistry.SurfaceTextureEntry handle = flutterState.textureRegistry.createSurfaceTexture(); ...
  67. Android public interface TextureRegistry { /** * Creates and registers

    a SurfaceTexture managed by the Flutter engine. * * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); /** A registry entry for a managed SurfaceTexture. */ interface SurfaceTextureEntry { /** @return The managed SurfaceTexture. */ SurfaceTexture surfaceTexture(); /** @return The identity of this SurfaceTexture. */ long id(); /** Deregisters and releases this SurfaceTexture. */ void release(); } } TextureRegistry
  68. Android public interface TextureRegistry { /** * Creates and registers

    a SurfaceTexture managed by the Flutter engine. * * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); /** A registry entry for a managed SurfaceTexture. */ interface SurfaceTextureEntry { /** @return The managed SurfaceTexture. */ SurfaceTexture surfaceTexture(); /** @return The identity of this SurfaceTexture. */ long id(); /** Deregisters and releases this SurfaceTexture. */ void release(); } } TextureRegistry ... surface = new Surface(textureEntry.surfaceTexture()); exoPlayer.setVideoSurface(surface); ...
  69. Android public interface TextureRegistry { /** * Creates and registers

    a SurfaceTexture managed by the Flutter engine. * * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); /** A registry entry for a managed SurfaceTexture. */ interface SurfaceTextureEntry { /** @return The managed SurfaceTexture. */ SurfaceTexture surfaceTexture(); /** @return The identity of this SurfaceTexture. */ long id(); /** Deregisters and releases this SurfaceTexture. */ void release(); } } TextureRegistry
  70. Android public interface TextureRegistry { /** * Creates and registers

    a SurfaceTexture managed by the Flutter engine. * * @return A SurfaceTextureEntry. */ SurfaceTextureEntry createSurfaceTexture(); /** A registry entry for a managed SurfaceTexture. */ interface SurfaceTextureEntry { /** @return The managed SurfaceTexture. */ SurfaceTexture surfaceTexture(); /** @return The identity of this SurfaceTexture. */ long id(); /** Deregisters and releases this SurfaceTexture. */ void release(); } } TextureRegistry textureId͕ฦΔ
  71. pigeon/messages.dart class LimitBitrateMessage { int textureId; int limitBitrate; } ...

    abstract class VideoPlayerApi { void initialize(); TextureMessage create(CreateMessage msg); void dispose(TextureMessage msg); void setLooping(LoopingMessage msg); void setVolume(VolumeMessage msg); void setPlaybackSpeed(PlaybackSpeedMessage msg); void play(TextureMessage msg); PositionMessage position(TextureMessage msg); void seekTo(PositionMessage msg); void pause(TextureMessage msg); void setMixWithOthers(MixWithOthersMessage msg); void setLimitBitrate(LimitBitrateMessage msg); }
  72. pigeon/messages.dart class LimitBitrateMessage { int textureId; int limitBitrate; } ...

    abstract class VideoPlayerApi { void initialize(); TextureMessage create(CreateMessage msg); void dispose(TextureMessage msg); void setLooping(LoopingMessage msg); void setVolume(VolumeMessage msg); void setPlaybackSpeed(PlaybackSpeedMessage msg); void play(TextureMessage msg); PositionMessage position(TextureMessage msg); void seekTo(PositionMessage msg); void pause(TextureMessage msg); void setMixWithOthers(MixWithOthersMessage msg); void setLimitBitrate(LimitBitrateMessage msg); }
  73. pigeon/messages.dart class LimitBitrateMessage { int textureId; int limitBitrate; } ...

    abstract class VideoPlayerApi { void initialize(); TextureMessage create(CreateMessage msg); void dispose(TextureMessage msg); void setLooping(LoopingMessage msg); void setVolume(VolumeMessage msg); void setPlaybackSpeed(PlaybackSpeedMessage msg); void play(TextureMessage msg); PositionMessage position(TextureMessage msg); void seekTo(PositionMessage msg); void pause(TextureMessage msg); void setMixWithOthers(MixWithOthersMessage msg); void setLimitBitrate(LimitBitrateMessage msg); }
  74. ϑΝΠϧͷੜ੒ $flutter pub run pigeon --input pigeons/messages.dart —dart_null_safety $(cd ../../../;

    ./script/incremental_build.sh format --travis --clang-format=clang-format-7)
  75. ϑΝΠϧͷੜ੒ $flutter pub run pigeon --input pigeons/messages.dart —dart_null_safety $(cd ../../../;

    ./script/incremental_build.sh format --travis --clang-format=clang-format-7)
  76. ϑΝΠϧͷੜ੒ $flutter pub run pigeon --input pigeons/messages.dart —dart_null_safety $(cd ../../../;

    ./script/incremental_build.sh format --travis --clang-format=clang-format-7)
  77. ϑΝΠϧͷੜ੒ $flutter pub run pigeon --input pigeons/messages.dart —dart_null_safety $(cd ../../../;

    ./script/incremental_build.sh format --travis --clang-format=clang-format-7) flutter/plugin ಠࣗͷϑΥʔϚοτ