Flutter实战 | 从 0 搭建「网易云音乐」APP(9、搜索页面、底部播放控制栏)

本系列可能会伴随你们很长时间,这里我会从0开始搭建一个「网易云音乐」的APP出来。git

下面是该APP 功能的思惟导图:github

前期回顾:微信

  1. Flutter实战 | 从 0 搭建「网易云音乐」APP(1、建立项目、添加插件、通用代码)
  2. Flutter实战 | 从 0 搭建「网易云音乐」APP(2、Splash Page、登陆页、发现页)
  3. Flutter实战 | 从 0 搭建「网易云音乐」APP(3、每日推荐、推荐歌单)
  4. Flutter实战 | 从 0 搭建「网易云音乐」APP(4、排行榜、播放页面)
  5. Flutter实战 | 从 0 搭建「网易云音乐」APP(5、播放功能逻辑)
  6. Flutter实战 | 从 0 搭建「网易云音乐」APP(6、歌词(一))
  7. Flutter实战 | 从 0 搭建「网易云音乐」APP(7、歌词(二))
  8. Flutter实战 | 从 0 搭建「网易云音乐」APP(8、个人页面)

本篇为第九篇,在这里咱们会搭建「搜索页面、底部播放控制栏」。ide

搜索页 搜索结果页

0. 写在前面

上一周一直没更新代码与文章,是由于公司公费去厦门旅游来着,因此好好放松了一周。布局

这周开始恢复代码与文章的更新,最近收到不少童鞋反馈说比较卡,字体

我建议加我我的微信「17610912320」,来探讨一下是哪一个地方,具体在哪里卡。ui

也欢迎 PR,让咱们一块儿为这个项目添砖加瓦!this

1. 搜索页

话很少说,接着就来咱们的搜索页,先看一下图,而后梳理一下需求:spa

  1. 历史记录(无历史记录的时候不显示)
  2. 热搜榜

1. 历史记录

先来搞历史记录,历史记录确定是要存在咱们本地的,那就须要用到 shared_preferences了。插件

这方面的就很少说了,看看文档,都懂的。

而后是 UI,不知道有没有童鞋记得我之前写过一篇文章:Flutter Wrap & Chip。

在这里彻底就能用得上,并且不须要那么多花里胡哨的,只有一个文字就好了。

来看一下如何定义:

Wrap(
  spacing: ScreenUtil().setWidth(20),
  children: historySearchList
  .map((v) => GestureDetector(
    onTap: () {
      searchText = v;
      _search();
    },
    child: Chip(
      label: Text(
        v,
        style: common14TextStyle,
      ),
      backgroundColor: Color(0xFFf2f2f2),
    ),
  ))
  .toList(),
),
复制代码

逻辑以下:

  1. Wrap 包裹住 chip
  2. 并设置每一个 chip 的间隔为 20
  3. 而后根据 historySearchList 的数据来返回 chip
  4. 最后改变一下 chip 的背景颜色便可

可是,不要忘了咱们还有「清空历史记录」的功能,页面以下:

在点击 小垃圾桶的时候弹出,这个也很简单:

IconButton(
  icon: Icon(
    Icons.delete_outline,
    color: Colors.grey,
  ),
  onPressed: () {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          content: Text(
            "肯定清空所有历史记录?",
            style: common14GrayTextStyle,
          ),
          actions: <Widget>[
            FlatButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: Text('取消'),
              textColor: Colors.red,
            ),
            FlatButton(
              onPressed: () {
                setState(() {
                  historySearchList.clear();
                  Application.sp.remove("search_history");
                });
                Navigator.of(context).pop();
              },
              child: Text('清空'),
              textColor: Colors.red,
            ),
          ],
        );
      });
  },
)
复制代码

在点击这个 IconButton 的时候调用 showDialog 方法,而后根据点击的按钮作相应的操做便可。

2. 热搜榜

热搜榜这个就更简单了,直接就是一个 ListView

刚开始看到这个布局的时候想到的是 ListTile,可是间距什么的很差控制,因此只能本身写了。

Text(
  curData.searchWord,
  style: index < 3
  ? w500_16TextStyle
  : common16TextStyle,
),
复制代码

最前面排名字体颜色的控制直接用 index 就ok,后面的也没什么好说的,直接撸就完了。

2. 搜索结果页

搜索结果页实际上是和「搜索页」在一块儿的,由搜索状态控制:

_isSearching ? _buildSearchingPage() : _buildUnSearchingPage();
复制代码

搜索结果页分 7 页:

  1. 综合
  2. 单曲
  3. 专辑
  4. 歌手
  5. 歌单
  6. 用户
  7. 视频

其中「综合」页面包含了剩下的每一页的UI,因此咱们在写「综合」页面的时候每个控件都封装一下。

具体UI上面就不说了,有一个须要注意的地方就是:

在综合页面须要跳转别的页面,这里我使用的是在建立「综合」页面的时候传入点击事件,而后在点击的时候调用:

SearchMultipleResultPage(this.keywords,
                         {@required this.onTapMore, @required this.onTapSimText});
复制代码

最后在主页面用 controller 来控制就行了。

3. 底部播放控制栏

接到不少人反馈说找不到当前听的是哪首歌😅,

当时以为这个东西比较简单,就没有写,昨天花了一点时间给写完了。

我为何说他简单呢。。。不是我装x,是真的简单,听我说!

咱们在编写播放页面的时候就已经把关于歌曲播放功能的 model:PlaySongsModel 给写好了,全部的功能都在这里,因此咱们想要写一个「播放控制栏」真的是分分钟搞定。

so,控制栏逻辑以下:

  1. 在播放的时候保存当前歌曲列表和当前 index 到本地
  2. 在从新打开 APP 的时候点击播放能够播放上次播放的歌曲

第一个保存,很简单了,使用 shared_preferences

// 保存当前歌曲到本地
void saveCurSong(){
  Application.sp.remove('playing_songs');
  Application.sp.setStringList('playing_songs', _songs.map((s) => FluroConvertUtils.object2string(s)).toList());
  Application.sp.setInt('playing_index', curIndex);
}
复制代码

第二个取出数据:

// 判断是否有保存的歌曲列表
if(Application.sp.containsKey('playing_songs')){
  List<String> songs = Application.sp.getStringList('playing_songs');
  playSongsModel.addSongs(songs.map((s) => Song.fromJson(FluroConvertUtils.string2map(s))).toList());
  int index = Application.sp.getInt('playing_index');
  playSongsModel.curIndex = index;
}
复制代码

关于UI更新什么的根本不须要考虑,Provider 直接搞定了,什么逻辑都不用写。

暂停播放之类的,点击事件以下:

GestureDetector(
  onTap: (){
    if(model.curState == null){
      model.play();
    }else {
      model.togglePlay();
    }
  }
)
复制代码

当咱们从新打开APP的时候,这个时候 curState 是 null,这个时候咱们调用 恢复/暂停 方法是没有效果的,因此咱们要先调用 play() 方法。

还有一个地方须要注意,在 iPhone 上有些是有「控制条」的,因此咱们要加上这个高度:

height: ScreenUtil().setWidth(110) + Application.bottomBarHeight
复制代码

写好之后在须要使用的页面加上就好了。

4. 总结

感受大部分功能已经完成,可是看了一下思惟导图。。。慢慢来吧!

你们若是有好的建议的话,欢迎提 issue,我会在第一时间回复。

该系列文章代码已传至 GitHub:github.com/wanglu1209/…

另我我的建立了一个「Flutter 交流群」,能够添加我我的微信 「17610912320」来入群。

相关文章
相关标签/搜索