Flutter收音机功能插件flutter_my_radio的使用

Flutter收音机功能插件flutter_my_radio的使用

flutter_my_radio 提供了一种在您的 Flutter 应用中轻松查找和播放任何网络广播电台的方法。

截图

该插件是 flutter 客户端,用于访问 radio-browser api。Radio-browser 是一个互联网广播电台数据库。它允许您根据多个条件搜索广播电台,如名称、标签、国家和语言。

此插件提供了大量的元数据,包括流媒体 URL、站点 URL 和其他指示信息。

使用方法

搜索广播电台

首先声明一个 MyRadioTools 对象:

class _MyAppState extends State<MyApp> {
  MyRadioTools radioTools = MyRadioTools();
  ...
}

然后执行搜索,指定关键词和搜索类型:

radios = await radioTools.findRadio("Europe 1", StationFilterTypes.byname);

插件会自动为找到的每个广播电台生成一个控件:

radios![index].widget()

搜索可以通过参数进行约束,如限制、偏移量和排序。为此,创建一个 SearchParameters 对象并指定参数:

var p = StationFilterTypes.byname.getParameters;
p.limit = 10;

然后在搜索时指定创建的 SearchParameters 对象:

radios = await radioTools.findRadio("Europe 1", StationFilterTypes.byname, parameters: p);

更新统计数据

Radio-browser 还提供了点击计数以了解广播电台的受欢迎程度。每次播放时,重要的是使用以下命令更新它:

radioTools.addClick(radios![index].stationUUID!);

获取可能的值

您还可以获取例如语言的值列表:

var lst = await radioTools.getList("", RadioListTypes.languages);

示例代码

以下是一个完整的示例代码,展示了如何使用 flutter_my_radio 插件在 Flutter 应用中实现广播电台的功能。

import 'package:flutter/material.dart';
import 'package:flutter_my_radio/objects/my_radio.dart';
import 'package:flutter_my_radio/objects/my_radio_tools.dart';
import 'package:flutter_my_radio/objects/search_parameter.dart';
import 'package:flutter_my_radio/objects/station_filter_types.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

enum FilterType { name, strictName, country, language }

class _MyAppState extends State<MyApp> {
  List<MyRadio>? radios = [];
  var search = TextEditingController();
  String lastSearch = "";
  ValueNotifier<int?> countListener = ValueNotifier<int?>(null);
  MyRadioTools radioTools = MyRadioTools();
  RadioPlayer? _currentRadio;
  late FilterType filterType;

  [@override](/user/override)
  void initState() {
    filterType = FilterType.name;
    radioTools.updateRadioBrowserApiUrls();
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('我的广播插件'),
        ),
        body: Padding(
          padding: const EdgeInsets.fromLTRB(15, 5, 15, 15),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Stack(alignment: AlignmentDirectional.topEnd, children: [
                Padding(
                  padding: const EdgeInsets.only(right: 8, left: 8, bottom: 8, top: 13),
                  child: SizedBox(
                    height: 40,
                    child: SearchBar(
                      leading: const Icon(Icons.search),
                      trailing: [menu(context)],
                      controller: search,
                      onSubmitted: (v) {
                        setState(() {});
                      },
                    ),
                  ),
                ),
                countWidget,
              ]),
              const SizedBox(
                height: 10,
              ),
              Expanded(
                child: FutureBuilder(
                  future: getRadios()..then((value) => countListener.value = value.length),
                  builder: (BuildContext context, snapshot) {
                    if (snapshot.connectionState == ConnectionState.waiting) {
                      return const Center(
                        child: CircularProgressIndicator(
                          color: Colors.deepPurpleAccent,
                        ),
                      );
                    }
                    if (snapshot.connectionState == ConnectionState.done) {
                      if (snapshot.hasError) {
                        return Center(
                          child: Text(
                            '发生错误:${snapshot.error}',
                            style: const TextStyle(fontSize: 18, color: Colors.red),
                          ),
                        );
                      } else if (snapshot.hasData) {
                        radios = snapshot.data;

                        return radios == null
                            ? const Text("未找到结果")
                            : ListView.builder(
                                itemCount: radios!.length,
                                itemBuilder: (context, index) {
                                  return radios![index].widget(beforeStart: (p) async {
                                    await _currentRadio?.player
                                        .fadeOut(duration: const Duration(milliseconds: 1000));
                                    _currentRadio = p;
                                    if (radios![index].stationUUID != null) radioTools.addClick(radios![index].stationUUID!);
                                  }, onStop: (p) {
                                    _currentRadio = null;
                                  });
                                });
                      }
                    }

                    return const Center(
                      child: CircularProgressIndicator(),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget menu(BuildContext context) {
    return MenuAnchor(
      builder: (BuildContext context, MenuController controller, Widget? child) {
        return IconButton(
          onPressed: () {
            controller.isOpen ? controller.close() : controller.open();
          },
          icon: const Icon(Icons.more_horiz),
          tooltip: '筛选类型',
        );
      },
      menuChildren: List<MenuItemButton>.generate(
        FilterType.values.length,
        (int index) => MenuItemButton(
          style: TextButton.styleFrom(
              visualDensity: VisualDensity.compact,
              alignment: AlignmentDirectional.center,
              backgroundColor: FilterType.values[index] == filterType ? Theme.of(context).colorScheme.primary : null),
          onPressed: () => setState(() {
            filterType = FilterType.values[index];
            search.text = lastSearch = "";
            radios?.clear();
          }),
          child: Text(
            ["名称", "严格名称", "国家", "语言"][index],
            style: const TextStyle()
                .copyWith(color: FilterType.values[index] == filterType ? Theme.of(context).colorScheme.onPrimary : null),
          ),
        ),
      ),
    );
  }

  Future<List<MyRadio>> getRadios() {
    if (lastSearch == search.text) return Future.value(radios);

    SearchParameters? p;
    p = StationFilterTypes.byname.getParameters;
    p.hidebroken = true;

    lastSearch = search.text;
    return search.text == ""
        ? Future.value([])
        : radioTools.findRadio(search.text, filterType.toStationFilter, forceHttps: true, parameters: p);
  }

  Widget get countWidget => ValueListenableBuilder<int?>(
      valueListenable: countListener,
      builder: (context, int? data, child) {
        return Badge(
            isLabelVisible: data != null,
            largeSize: 25,
            label: Container(
                alignment: AlignmentDirectional.center,
                constraints: const BoxConstraints(minWidth: 17),
                child: Text(data.toString(), style: const TextStyle().copyWith(color: Colors.white))));
      });
}

extension FilterExt on FilterType {
  StationFilterTypes get toStationFilter {
    switch (this) {
      case FilterType.name:
        return StationFilterTypes.byname;
      case FilterType.strictName:
        return StationFilterTypes.bynameexact;
      case FilterType.country:
        return StationFilterTypes.bycountry;
      case FilterType.language:
        return StationFilterTypes.bylanguage;
    }
  }
}

更多关于Flutter收音机功能插件flutter_my_radio的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter收音机功能插件flutter_my_radio的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于如何使用Flutter中的flutter_my_radio插件来实现收音机功能,下面是一个基本的代码示例。这个示例展示了如何集成和使用flutter_my_radio插件来播放电台流。

首先,确保你的Flutter项目中已经添加了flutter_my_radio依赖。在你的pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_my_radio: ^最新版本号  # 请替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

接下来,在你的Dart代码中,你可以按照以下步骤实现收音机功能:

import 'package:flutter/material.dart';
import 'package:flutter_my_radio/flutter_my_radio.dart';

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

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

class RadioScreen extends StatefulWidget {
  @override
  _RadioScreenState createState() => _RadioScreenState();
}

class _RadioScreenState extends State<RadioScreen> {
  FlutterMyRadio? _radioPlayer;
  String _radioUrl = 'http://your-radio-stream-url.com/stream';  // 替换为你的电台流URL
  bool _isPlaying = false;

  @override
  void initState() {
    super.initState();
    _radioPlayer = FlutterMyRadio();
    _radioPlayer!.initRadioPlayer()!;
  }

  @override
  void dispose() {
    _radioPlayer!.dispose()!;
    super.dispose();
  }

  void _playRadio() async {
    if (!_isPlaying) {
      await _radioPlayer!.startRadio(_radioUrl)!;
      setState(() {
        _isPlaying = true;
      });
    } else {
      await _radioPlayer!.stopRadio()!;
      setState(() {
        _isPlaying = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Radio'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              _isPlaying ? 'Playing...' : 'Stopped',
              style: TextStyle(fontSize: 24),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _playRadio,
              child: Text(_isPlaying ? 'Stop' : 'Play'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 添加依赖:在pubspec.yaml文件中添加了flutter_my_radio依赖。
  2. 初始化插件:在RadioScreen组件的initState方法中初始化了FlutterMyRadio实例。
  3. 播放控制:创建了一个_playRadio方法来控制电台的播放和停止,并根据当前状态更新UI。
  4. UI布局:使用ScaffoldAppBarCenterColumnElevatedButton等Flutter组件创建了简单的UI,包含一个文本显示当前状态和一个按钮来控制播放/停止。

请注意,你需要将_radioUrl变量替换为你实际的电台流URL。此外,确保flutter_my_radio插件的API与你使用的版本相匹配,因为插件的API可能会随着版本的更新而发生变化。

这个示例提供了一个基础框架,你可以在此基础上添加更多功能,如音量控制、进度条、电台列表等。

回到顶部