0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ポッドキャストから音声データを取得して再生できるアプリをFlutterで試みる。

Last updated at Posted at 2021-08-09

AndroidStudioから入ってCreate New Flutter Projectを選択し、エディタ(vsCode)で開きます。main.dartを開いたらRun and Debugを押します。
スクリーンショット 2021-07-31 12.29.02.png
スクリーンショット 2021-07-31 12.21.13.png
スクリーンショット 2021-07-31 12.33.33.png
iosを選択してFlutter Demo Home Pageが出てきたら準備完了です。
スクリーンショット 2021-07-31 12.26.13.png
Textを取り除いて、試しに再生アイコンを作ってみます。
スクリーンショット 2021-07-31 14.05.25.png

Row(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  IconButton(
                    iconSize: 45.0,
                    color: Colors.black,
                    onPressed: () {},
                    icon: Icon(
                      Icons.skip_previous,
                    ),
                  ),
                  IconButton(
                    iconSize: 62.0,
                    color: Colors.black,
                    onPressed: () {},
                    icon: Icon(
                      Icons.play_arrow,
                    ),
                  ),
                    IconButton(
                    iconSize: 45.0,
                    color: Colors.black,
                    onPressed: () {},
                    icon: Icon(
                      Icons.skip_next,
                    ),
                  ),
                ],
            ),

実行してみると!?

スクリーンショット 2021-07-31 14.13.23.png
やる気出てきました!
次に、ポッドキャストのiTunesUPodcastRSS/XMLフィードを取得してみます。pubspec.yamlhttp: ^0.13.3xml* ^5.1.2を加えて、main.dartに以下を記述します。
スクリーンショット 2021-07-31 16.08.57.png

import 'package:http/http.dart' as http;
import 'package:xml/xml.dart';





@override
  void initState() {
    // アプリを起動時に一度だけ実行
    super.initState();
    
    Future(() async{
      var url = Uri.parse('http://www.nhk.or.jp/rj/podcast/rss/english.xml');
      print(await http.read(url));
      var response = await http.read(url);
      var document = XmlDocument.parse(response);
      final enclosures = document.findAllElements('enclosure');
      print(enclosures);
      enclosures.first.getAttribute("url");
      print(enclosures.first.getAttribute("url"));
    });
  }

実行してDEBUG CONSOLEを見てみると!それらしき物が取得できています!
スクリーンショット 2021-07-31 16.19.10.png

pubspec.yamlvideo_playerパッケージのVersion 2.0.2を入れます。
スクリーンショット 2021-08-09 17.26.43.png

ルート直下に、assetsディレクトリを作りイメージ画像をドラッグ&ドロップで入れます。これは後に使用する為の画像です!
スクリーンショット 2021-08-09 17.37.26.png

pubspec.yamlflutter:内、uses-material-design: trueの下にassets: - assets/を書きます。スクリーンショット 2021-08-09 17.42.27.png

libディレクトリのmain.dartを書き替えてとりあえず再生出来るようにうして行きます。

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:xml/xml.dart';
import 'package:video_player/video_player.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late VideoPlayerController _controller;
  @override
  void initState() {
    // アプリを起動時に一度だけ実行
    super.initState();

    Future(() async {
      var url = Uri.parse('http://www.nhk.or.jp/rj/podcast/rss/english.xml');
      // print(await http.read(url));
      var response = await http.read(url);
      var document = XmlDocument.parse(response);
      final enclosures = document.findAllElements('enclosure');
      // print(enclosures);
      enclosures.first.getAttribute("url");
      print(enclosures.first.getAttribute("url"));
      _controller = VideoPlayerController.network(
          (enclosures.first.getAttribute("url")).toString());
      _controller.initialize().then((_) {
        setState(() {});
      });
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            padding: EdgeInsets.all(16),
            child: Icon(Icons.music_video, size: 256),
          ),
          VideoProgressIndicator(
            _controller,
            allowScrubbing: true,
          ),
          _ProgressText(controller: _controller),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              IconButton(
                onPressed: () {
                  _controller
                      .seekTo(Duration.zero)
                      .then((_) => _controller.play());
                },
                icon: Icon(Icons.refresh),
              ),
              IconButton(
                onPressed: () {
                  _controller.play();
                },
                icon: Icon(Icons.play_arrow),
              ),
              IconButton(
                onPressed: () {
                  _controller.pause();
                },
                icon: Icon(Icons.pause),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class _ProgressText extends StatefulWidget {
  final VideoPlayerController controller;

  const _ProgressText({
    Key? key,
    required this.controller,
  }) : super(key: key);

  @override
  __ProgressTextState createState() => __ProgressTextState();
}

class __ProgressTextState extends State<_ProgressText> {
  late VoidCallback _listener;

  __ProgressTextState() {
    _listener = () {
      setState(() {});
    };
  }

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(_listener);
  }

  @override
  void deactivate() {
    widget.controller.removeListener(_listener);
    super.deactivate();
  }

  @override
  Widget build(BuildContext context) {
    final String position = widget.controller.value.position.toString();
    final String duration = widget.controller.value.duration.toString();
    return Text('$position / $duration');
  }
}

それらしいのが作成出来ました。参照にさせて頂いたURL貼らせて頂きます。
Fultterで始めるアプリ開発「音楽・動画を再生する」

スクリーンショット 2021-08-09 8.31.55.png

しっかりと再生出来ました!
ただ、これだと思っていたものと違うので装飾していきます。

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:xml/xml.dart';
import 'package:video_player/video_player.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
//アプリケーションのルートウィジェット  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
//こちらをfalseにすることでデバッグを消せます
      debugShowCheckedModeBanner: false,
      home: MyHomePage(title: ''),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late VideoPlayerController _controller;
//最初は再生させない
 bool playing = false;
//再生ボタンアイコン
  IconData playBtn = Icons.play_arrow;
//オブジェクトの宣言
  Duration position = new Duration();
  Duration musicLength = new Duration();
//プレーヤーの初期化
  @override
  void initState() {
// アプリを起動時に一度だけ実行
    super.initState();

    Future(() async {
      var url = Uri.parse('http://www.nhk.or.jp/rj/podcast/rss/english.xml');
      // print(await http.read(url));
      var response = await http.read(url);
      var document = XmlDocument.parse(response);
      final enclosures = document.findAllElements('enclosure');
      // print(enclosures);
      enclosures.first.getAttribute("url");
      print(enclosures.first.getAttribute("url"));
      _controller = VideoPlayerController.network(
          (enclosures.first.getAttribute("url")).toString());
      _controller.initialize().then((_) {
        setState(() {});
      });
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
//メイン画面
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
            width: double.infinity,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [
                    Colors.green.shade800,
                    Colors.green.shade200,
                  ]),
            ),
            child: Padding(
                padding: EdgeInsets.only(
                  top: 48.0,
                ),
                child: Container(
                    child: Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                      Padding(
                        padding: const EdgeInsets.only(left: 12.0),
                        child: Text(
                          "NHK WORLD RADIO JAPAN",
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 38.0,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.only(left: 12.0),
                        child: Text(
                          "This is the latest news in English from NHK WORLD RADIO JAPAN.",
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 24.0,
                            fontWeight: FontWeight.w400,
                          ),
                        ),
                      ),
                      SizedBox(
                        height: 24.0,
                      ),
//こちらでアセッツに入れた画像を使います
                      Center(
                        child:Container(
                          width: 280.0,
                          height: 280.0,
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(30.0),
                            image: DecorationImage(
                              image: AssetImage("assets/image.jpg"),
                            )),
                        ),
                      ),
                      SizedBox(
                        height: 18.0,
                      ),
                      Center(
                        child: Text(
                          "English News",
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 32.0,
                            fontWeight: FontWeight.w600,
                          ),
                        ),
                      ),
                      SizedBox(
                        height: 30.0,
                      ),
                      Expanded(
                          child: Container(
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(30.0),
                            topRight: Radius.circular(30.0),
                          ),
                        ),
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
//プログレスバーを表示
                            VideoProgressIndicator(
                              _controller,
                              allowScrubbing: true,
                            ),
                            _ProgressText(controller: _controller),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                              children: [
                                IconButton(
                                  iconSize: 62.0,
                                  color: Colors.blue,
                                  onPressed: () {
//再生ボタンの機能追加、停止の場合は音を止める
                                    if (!playing) {
                                      _controller.play();
                                      setState(() {
                                        playBtn = Icons.pause;
                                        playing = true;
                                      });
                                    } else {
                                      _controller.pause();
                                      setState(() {
                                        playBtn = Icons.play_arrow;
                                        playing = false;
                                      });
                                    }
                                  },
                                  icon: Icon(
                                    playBtn,
                                  ),
                                ),
//リロード
                                IconButton(
                                  iconSize: 62.0,
                                  color: Colors.red,
                                  onPressed: () {
                                    _controller
                                        .seekTo(Duration.zero)
                                        .then((_) => _controller.pause());
                                  },
                                  icon: Icon(Icons.refresh),
                                ),
                              ],
                            ),
                          ],
                        ),
                      ))
                    ]
                  )
                )
              )
            )
          );
  }
}
//現在の再生時間を表示する
class _ProgressText extends StatefulWidget {
  final VideoPlayerController controller;

  const _ProgressText({
    Key? key,
    required this.controller,
  }) : super(key: key);

  @override
  __ProgressTextState createState() => __ProgressTextState();
}

class __ProgressTextState extends State<_ProgressText> {
  late VoidCallback _listener;

  __ProgressTextState() {
    _listener = () {
      setState(() {});
    };
  }

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(_listener);
  }

  @override
  void deactivate() {
    widget.controller.removeListener(_listener);
    super.deactivate();
  }

  @override
  Widget build(BuildContext context) {
    final String position = widget.controller.value.position.toString();
    final String duration = widget.controller.value.duration.toString();
    return Text('$position / $duration');
  }
}

実行してみましょう。

スクリーンショット 2021-08-09 16.19.57.png
良い感じに仕上がりました。次回は実機を取り入れアプリを開発してみようと思います。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?