代码取自于不倦APP(简洁的三方网易播放器),作者sixbugs。
我使用的是vscode环境,环境搭建可参考此文最后一段。环境搭建好后新建一个项目,打开pubspec.yaml文件设置要引用的包。
dependencies:
get: ^3.26.0
path_provider: ^2.0.1
color_thief_flutter: ^1.0.2
cached_network_image: ^2.5.1
flutter_placeholder_textlines: ^1.0.4
starry:
path: starry
shared_preferences: ^2.0.4
cookie_jar: ^2.0.0
encrypt: ^3.0.0
we_slide: ^2.1.0
pull_to_refresh: ^1.6.5
sleek_circular_slider: ^2.0.0
dots_indicator: ^2.0.0
progress_state_button: ^1.0.2
direct_select_flutter: ^1.1.0
on_audio_query: ^1.0.6
flutter_screenutil: ^5.0.0
dio: ^4.0.0
url_launcher: ^6.0.3
package_info: ^2.0.0
flutter:
sdk: flutter
设置后点击右上角的“Get Packages”下载依赖,再打开lib下的main.dart文件。
代码结构
import 'api/answer.dart';
import 'api/netease_cloud_music.dart';
import 'global/global_binding.dart';
import 'global/global_config.dart';
import 'global/global_theme.dart';
main.dart中引用的包主要是数据接口类和全局类。数据接口类调用的是网页云音乐开放的api,API文档地址参考为这里。
全局类
使用DEBUG模式进入APP,将断点搭载runAPP一行,F11单点调试观察程序的运行情况。
调试状态
随着进入程序,可以发现首先在GlobalBinding中初始化了GlobalController、FindController、UserController三个实例。
GlobalController
GlobalControllerGlobalController主要用于初始化播放器。
GlobalController下的addSliderListener方法如下,其用于监听点击底部播放栏状态的变化情况,使用了we_slide插件。
void addSliderListener(weSlideController,PreloadPageController pageController) {
this.weSlideController = weSlideController;
this.pageController = pageController;
weSlideController.addListener(() {
if (weSlideController.isOpened) {
HomeController.to.resumeStream();
} else {
pageController?.jumpToPage(0);
HomeController.to.pauseStream();
}
});
}
在音乐播放的部分使用了作者自己封装的StarrySky插件。
///播放或暂停
playOrPause() async {
// if (playState.value == PlayState.ERROR || playState.value == PlayState.STOP)
// return;
if (song.value.musicId == '-99') return;
if (playState.value == PlayState.PLAYING) {
await Starry.pauseMusic();
} else {
await Starry.restoreMusic();
}
}
///设置播放模式
changePlayMode() {
if (playListMode.value == PlayListMode.SONG) {
//1->2->3
var value = 1;
switch (playMode.value) {
case 1:
value = 2;
break;
case 2:
value = 3;
break;
case 3:
value = 1;
break;
}
Starry.setPlayMode(value);
}
}
FindController
FindControllerFindController主要用于初始化发现页。
FindController下的loadTodaySheet方法如下,将获取的数据刷新至发现页上。
loadTodaySheet({forcedRefresh = false}) {
NetUtils()
.getRecommendResource(forcedRefresh: forcedRefresh)
.then((personalEntity) {
if (personalEntity != null && personalEntity.code == 200) {
var sheets = personalEntity.result;
allSheet
..clear()
..addAll(sheets);
sheet..clear();
if (sheets.length == 6) {
var i = sheets.length ~/ 3;
for (int j = 0; j < i; j++) {
sheet.add(sheets.sublist(j * 3, (j + 1) * 3));
}
}
} else {
loadState.value = LoadState.FAIL;
}
});
loadNewSong();
}
UserController
UserControllerUserController主要用于初始化用户页。
页面类
回到Main.Dart中继续跟踪程序的运行,进入runApp下的initialRoute,在app_outes.dart中对整个程序的路由绑定了对应的页面。
class AppPages {
static const INITIAL = '/home';
static final routes = [
GetPage(name: '/home', page: () => HomeView(), binding: HomeBinding()),
GetPage(name: '/today', page: () => TodayView(), binding: TodayBinding()),
GetPage(
name: '/sheet',
page: () => SheetInfoView(),
binding: SheetInfoBinding()),
GetPage(
name: '/profile', page: () => ProfileView(), binding: ProfileBinding()),
GetPage(
name: '/setting', page: () => SettingView(), binding: SettingBinding()),
GetPage(name: '/login', page: () => LoginView(), binding: LoginBinding()),
GetPage(name: '/cloud', page: () => CloudView(), binding: CloudBinding()),
GetPage(
name: '/sheet_classify',
page: () => SheetClassifyView(),
binding: SheetClassifyBinding()),
GetPage(
name: '/music_talk',
page: () => MusicTalkView(),
binding: MusicTalkBinding()),
GetPage(
name: '/all_song',
page: () => AllSongView(),
binding: AllSongBinding()),
GetPage(
name: '/search_details',
page: () => SearchDetailsView(),
binding: SearchDetailBinding()),
GetPage(name: '/local', page: () => MusicView(), binding: MusicBinding()),
GetPage(name: '/top', page: () => TopView(), binding: TopBinding()),
GetPage(
name: '/local_album',
page: () => LocalAlbumView(),
binding: LocalAlbumBinding()),
GetPage(
name: '/donate', page: () => DonateView(), binding: DonateBinding()),
GetPage(name: '/about', page: () => AboutView()),
GetPage(name: '/album', page: () => AlbumView(), binding: AlbumBinding()),
GetPage(
name: '/history', page: () => HistoryView(), binding: HistoryBinding()),
GetPage(name: '/radio', page: () => RadioView(), binding: RadioBinding()),
GetPage(
name: '/radio_detail',
page: () => RadioDetailView(),
binding: RadioDetailBinding()),
GetPage(
name: '/play_list_manager',
page: () => PlayListManagerView(),
binding: PlayListManagerBinding()),
];
}
本项目的路由管理用到了GetX包,GetX 是 Flutter 上的一个轻量且强大的解决方案:高性能的状态管理、智能的依赖注入和便捷的路由管理。
controller定义初始数据源 ,需要继承 GetxController ;binding用于将控制器推入getx中,可以理解为要注册控制器;view中使用 GetView<T> 语法将 controller 和 binding 进行关联 ,内部可以直接使用 controller的实例调用数据和方法。
使用 Obx(()=> widget) 语法,返回你要变更的组件, 让数据在视图上更新,并且是局部刷新的。 home_view的目录结构
home_controller的目录结构
以home页面为例,home_view主要由各类widget组成,home_controller里用于实现业务逻辑。
网友评论