Nealyang / PersonalBlog

:memo: Nealyang personal blog

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

开源库、活动 UI & 功能 编写

Nealyang opened this issue · comments

前言

由于前面UI功能基本以及涵盖我们这一章节中所涵盖的知识点。所以活动和开源库的页面,我们将放到一小节中,将关键代码再熟悉一番。不再重复编写同样章节内容。完成该章节我们将完成如下功能开发:

img

代码地址为:代码地址

定义数据model

  • lib/model/activity_cell.dart
  import '../util/util.dart';
  class ActivityCell{
    String pic;
    String detailUrl;
    String title;
    String city;
    String time;
  
    ActivityCell({
      this.city,
      this.detailUrl,
      this.pic,
      this.time,
      this.title
    });
  
    factory ActivityCell.formJson(Map<String,dynamic> json){
      return ActivityCell(
        city: json['city'],
        detailUrl: json['eventUrl'],
        title:json['title'],
        pic:json['screenshot'],
        time: Util.getTimeDate(json['startTime'])
      );
    }
  }
  • 数据model的定义使我们每一个页面针对该页面的cell总结出来的所需字段,对于从接口获取的字段需要进行处理的数据也同样在这一层中处理,保证改model的数据都是cell可以拿来即用的。比如此处我们使用Util.getTimeDate的方法

具体方法如下:

  static String getTimeDate(String comTime) {
    var compareTime = DateTime.parse(comTime);
    String weekDay = '';
    switch (compareTime.weekday) {
      case 2:
        weekDay = '周二';
        break;
      case 3:
        weekDay = '周三';
        break;
      case 4:
        weekDay = '周四';
        break;
      case 5:
        weekDay = '周五';
        break;
      case 6:
        weekDay = '周六';
        break;
      case 7:
        weekDay = '周日';
        break;
      default:
        weekDay = '周一';
    }
    return '${compareTime.month}-${compareTime.day}  $weekDay';
  }

请求数据

首先定义基础请求api

  // 开源库
  static const String REPOS_LIST = 'https://repo-ms.juejin.im/v1/getCustomRepos';

  // 活动
  static const String ACTIVITY_CITY = 'https://event-storage-api-ms.juejin.im/v1/getCityList';
  static const String ACTIVITY_LIST = 'https://event-storage-api-ms.juejin.im/v2/getEventList';

再在lib/util/data_utils.dart中封装请求方法,这里也是举其一个例子

  // 活动列表
    static Future<List<ActivityCell>> getActivityList(Map<String,dynamic> params) async{
    List<ActivityCell> resultList = [];
    var response = await NetUtils.get(Api.ACTIVITY_LIST,params: params);
    var responseList = response['d'];
    for (int i = 0; i < responseList.length; i++) {
      ActivityCell activityCell;
      try {
        activityCell = ActivityCell.formJson(responseList[i]);
      } catch (e) {
        print("error $e at $i");
        continue;
      }
      resultList.add(activityCell);
    }
     return resultList;
  }
  • 由于我不确定字段是否齐全,毕竟这是掘金开放的api,笔者也没有跟开发约束字段,所以在处理的字段的时候添加了个异常捕获。及时出现了异常,也不会影响代码,并且还能定位哪一个数据有问题。

页面代码编写

这是我们改项目中编写的最后一个完整的页面,所以这里我展示下全部的代码,再最后说明下其中的一些注意事项

  • lib/page/repos_page.dart

        import 'package:flutter/material.dart';
        import '../util/data_utils.dart';
        import '../model/repos_cell.dart';
        import '../constants/constants.dart';
        import '../widgets/repos_list_cell.dart';
        import '../widgets/load_more.dart';
        import 'dart:core';
        import '../widgets/repos_cell_header.dart';
        
        class ReposPage extends StatefulWidget {
          _ReposPageState createState() => _ReposPageState();
        }
        
        class _ReposPageState extends State<ReposPage> {
          List<ReposCell> _listData = <ReposCell>[];
          int _indexPage = 0;
          Map<String, dynamic> _params = {"src": 'web', "limit": 20};
          bool _hasMore = true;
          ScrollController _scrollController = ScrollController();
           bool _isRequesting = false;
        
          @override
          void initState() {
            super.initState();
            _getListData(false);
            _scrollController.addListener(() {
              if (_scrollController.position.pixels ==
                  _scrollController.position.maxScrollExtent) {
                _getListData(true);
              }
            });
          }
      
        _getListData(bool isLoadMore) {
          if (_isRequesting || !_hasMore) return;
          if (isLoadMore) {
            _params['before'] = Constants.REPOS_BEFOR[_indexPage];
          }else{
            _indexPage = -1;
          }
          _isRequesting = true;
          DataUtils.getReposListData(_params).then((resultData) {
            if (this.mounted) {
              _indexPage+=1;
              List<ReposCell> resultList = [];
              if (isLoadMore) {
                resultList.addAll(_listData);
              }
              resultList.addAll(resultData);
      
              setState(() {
                _listData = resultList;
                _hasMore = _indexPage < Constants.REPOS_BEFOR.length;
                _isRequesting = false;
              });
            }
          });
        }
      
          @override
          void dispose() { 
            _scrollController.dispose();
            super.dispose();
          }
        
          Widget _itemBuilder(context,index){
            if(index == _listData.length+1){
              return LoadMore(_hasMore);
            }
            if(index == 0){
              return ReposCellHeader();
            }
            return ReposListCell(cellData: _listData[index-1]);
          }
        
          @override
          Widget build(BuildContext context) {
            return ListView.builder(
              itemBuilder: _itemBuilder,
              itemCount: _listData.length+2,
              controller: _scrollController,
            );
          }
        }

  • 顶部import相关组件,设计该页面的是repos_cell_header,repos_list_cell,data_util为页面请求数据封装的方法。load_more 是底部加载更多的组件,需要传递hasMore来确认UI长什么样子。dart:core为dart中的核心库,包含Uri的加密解密等
  • 页面继承StatefulWidget类,因为会涉及到页面UI的改变,请求数据我们需要在内部定义盛放数据的list,pageIndex、请求参数、是否还有下一页、是否正在请求(防止触底后不断发送页面请求)
  • 长列表使用ListViedw.builder方法来构建,毕竟性能好,构建需要初始化 ScrollController ,ScrollController可以给列表设置长度,并且还可以检测页面滚动,检测触底,在页面初始化的时候添加这些监听。
  • 在请求数据的时候,需要根据页面当前的状态(isRequesting、hasMore、isLoadMore)来决定进行什么操作
  • 最后由于我们初始化 ScrollController ,所以需要在页面 dispose的时候及时释放相应资源,以保证手机性能

完整代码地址:代码地址