flutter 开发一个应用 5, tab的bug修正,添加下拉上拉

分支切换到dev_1.1了.

当切换多个tab的时候,会发现,原来的列表又初始化了一次.这个解决也简单,就是使用胶水类.

class _GankJsonListPageState extends State<GankJsonListPage>
    with AutomaticKeepAliveClientMixin {//加上这一句,
重写
@override
bool get wantKeepAlive => true; 
} 

这样就行了.这样做无疑会增加内存的消耗,如果页面比较多.

再将原来的列表项拆分:

Widget buildRow(int i) {
    var beans = gankToday.beans;
    //print('bean i:$i data:$beans');
    if (beans == null) {
      return Text("no items:");
    }
    var bean = beans[i];
    if (bean.images == null || bean.images.length < 1) {
      return GankListNoImageItem(
          bean: bean,
          onPressed: () {
            detail(bean);
          });
    } else {
      return GankListImageItem(
          bean: bean,
          onPressed: () {
            detail(bean);
          });
    }
  }

有图的项:

class GankListImageItem extends StatelessWidget {
  GankListImageItem({Key key, this.bean, this.onPressed}) : super(key: key);
  final GankBean bean;
  final VoidCallback onPressed;

  void detail(GankBean bean) {}

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        onPressed();
      },
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
              padding: EdgeInsets.only(
                  left: 10.0, right: 10.0, top: 5.0, bottom: 5.0),
              child: Text("Title:${bean.publishedAt}")),
          Padding(
              padding: EdgeInsets.only(
                  left: 10.0, right: 10.0, top: 5.0, bottom: 5.0),
              child: Text("Url:${bean.images[0]}")),
          Padding(
            padding: EdgeInsets.only(left: 10.0, right: 10.0),
            child: Image(
              image: CachedNetworkImageProvider(bean.images[0]),
              //width: album.images.width.toDouble(),
              //height: album.images.height.toDouble(),
              fit: BoxFit.fitWidth,
            ),
          ),
        ],
      ),
    );
  }
}

无图的就不粘贴了,上github看源码就行了.

如果添加进了SmartRefresher,在加载图片时,没有设置图片的高与宽,会出现RenderFlex overflowed异常.

所以,上拉与下拉的列表,就不在这修改了.

建立一个PullWidget.

这里参考了gsy的代码.
class PullWidget extends StatefulWidget {
  PullWidget(
      {Key key,
      this.pullController,
      this.items,
      this.itemBuilder,
      this.onLoadMore,
      this.onRefresh})
      : super(key: key);
  final List items;
  final PullWidgetController pullController;
  final IndexedWidgetBuilder itemBuilder;
  final RefreshCallback onLoadMore;
  final RefreshCallback onRefresh;

  @override
  _PullWidgetState createState() => new _PullWidgetState();
}

class _PullWidgetState extends State<PullWidget> {
  RefreshController _refreshController =
      RefreshController(initialRefresh: false);
  ScrollController _scrollController;

  _PullWidgetState() : super();

  @override
  void initState() {
    super.initState();
    _scrollController = new ScrollController();
    widget.pullController.needLoadMore?.addListener(() {
      _refreshController.loadComplete(); 加载完成,通过它刷新,如果列表项是StatefulWidget ,则无法刷新数据.
    });
    widget.pullController.needRefresh?.addListener(() {
      _refreshController.refreshCompleted();
    });

    ///增加滑动监听
    _scrollController.addListener(() {
      ///判断当前滑动位置是不是到达底部,触发加载更多回调
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        if (widget.pullController.needLoadMore.value) {
          widget.onLoadMore?.call();
        }
      }
    });
  }

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

  void _onRefresh() async {
    // monitor network fetch
    /*await Future.delayed(Duration(milliseconds: 1000));
    // if failed,use refreshFailed()
    _refreshController.refreshCompleted();*/
    widget.pullController.needRefresh.value = false;
    widget.onRefresh?.call();
  }

  void _onLoading() async {
    // monitor network fetch
    /*await Future.delayed(Duration(milliseconds: 1000));
    // if failed,use loadFailed(),if no data return,use LoadNodata()
    widget.items.add((widget.items.length + 1).toString());
    if (mounted) setState(() {});
    _refreshController.loadComplete();*/
    widget.pullController.needLoadMore.value = false;
    widget.onLoadMore?.call();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SmartRefresher(
        enablePullDown: true,
        enablePullUp: true,
        header: MaterialClassicHeader(),
        footer: CustomFooter(
          builder: (BuildContext context, LoadStatus mode) {
            Widget body;
            if (mode == LoadStatus.idle) {
              body = Text("pull up load");
            } else if (mode == LoadStatus.loading) {
              body = CircularProgressIndicator();
            } else if (mode == LoadStatus.failed) {
              body = Text("Load Failed!Click retry!");
            } else {
              body = Text("No more Data");
            }
            return Container(
              height: 55.0,
              child: Center(child: body),
            );
          },
        ),
        controller: _refreshController,
        onRefresh: _onRefresh,
        onLoading: _onLoading,
        child: new ListView.builder(
          ///保持ListView任何情况都能滚动,解决在RefreshIndicator的兼容问题。
          physics: const AlwaysScrollableScrollPhysics(),
          itemBuilder: (context, index) {
            return _getItem(index);
          },

          itemExtent: 100.0,
          itemCount: _getListCount(),
          controller: _scrollController,
        ),
      ),
    );
  }

  int _getListCount() {
    return widget.items.length;
  }

  _getItem(int index) {
    return widget.itemBuilder(context, index);
    //return Card(child: Center(child: Text(items[index])));
  }
}

class PullWidgetController {
  List dataList = new List();

  ValueNotifier<bool> needLoadMore = new ValueNotifier(false);
  ValueNotifier<bool> needRefresh = new ValueNotifier(false);
}

建立一个测试页::

class TestListPage extends StatefulWidget {
  TestListPage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _TestListPageState createState() => new _TestListPageState();
}

class _TestListPageState extends State<TestListPage>
    with AutomaticKeepAliveClientMixin {
  List items = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "11",
    "12",
    "13",
    "14",
    "15",
    "16",
    "17",
    "18",
  ];
PullWidgetController _pullController = new PullWidgetController();

  @override
  void initState() {
    super.initState();
    _pullController.dataList = items;
  }

  @override
  bool get wantKeepAlive => true;
  int page = 1;
  bool isLoading = false;
  bool isRefreshing = false;
  bool isLoadMoring = false;

  _lockToAwait() async {
    ///if loading, lock to await
    doDelayed() async {
      await Future.delayed(Duration(seconds: 1)).then((_) async {
        if (isLoading) {
          return await doDelayed();
        } else {
          return null;
        }
      });
    }

    await doDelayed();
  }

  @protected
  Future<Null> handleRefresh() async {
    if (isLoading) {
      if (isRefreshing) {
        return null;
      }
      await _lockToAwait();
    }
    isLoading = true;
    isRefreshing = true;
    page = 1;
    var res = await requestRefresh();
    print("res:$res;");
    if (res != null) {
      resolveRefreshResult(res);
      setState(() {
        _pullController.needRefresh.value =  true;
      });
    }
    isLoading = false;
    isRefreshing = false;
    return null;
  }

  @protected
  resolveRefreshResult(res) {
    if (res != null) {
      _pullController?.dataList?.clear();
      setState(() {
        _pullController?.dataList?.addAll(res);
        //print("resolveRefreshResult:$res;");
      });
    }
  }

  @protected
  Future<Null> onLoadMore() async {
    if (isLoading) {
      if (isLoadMoring) {
        return null;
      }
      await _lockToAwait();
    }
    isLoading = true;
    isLoadMoring = true;
    page++;
    var res = await requestLoadMore();
    if (res != null) {
      setState(() {
        _pullController?.dataList?.addAll(res);
      });
    }
    setState(() {
      _pullController.needLoadMore.value = (res != null);
    });
    isLoading = false;
    isLoadMoring = false;
    return null;
  }

  //下拉刷新数据
  @protected
  requestRefresh() async {
    return [
      "11",
      "12",
      "13",
      "14",
      "15",
      "16",
      "17",
      "18",
    ];
  }

  ///上拉更多请求数据
  @protected
  requestLoadMore() async {
    return [(_pullController.dataList.length + 1).toString()];
  }

  _renderItem(int index) {
    if (_pullController.dataList.length == 0) {
      return null;
    }
    //print("item:${_pullController.dataList[index]}");
    switch (index) {
      case 2:
        return new TestListItem(bean: _pullController.dataList[index], onPressed: () {});
      default:
        return new TestListItem(bean: _pullController.dataList[index], onPressed: () {});
    }
  }

//关键部分,把这个参数传入,这样,pullwidget是可重用的. 外部关心的是加载数据与渲染列表项.
  @override
  Widget build(BuildContext context) {
    return new PullWidget(
      pullController: _pullController,
      items: _pullController.dataList,
      itemBuilder: (BuildContext context, int index) => _renderItem(index),
      onLoadMore: onLoadMore,
      onRefresh: handleRefresh,
    );
  }
}

列表项就是普通的控件

class TestListItem extends StatelessWidget {
  TestListItem({this.bean, this.onPressed}) : super();
  final String bean;
  final VoidCallback onPressed;

  void detail(String bean) {}

  @override
  Widget build(BuildContext context) {
    return Card(child: Center(child: Text(bean)));
  }
}

把这两个 列表放到TabBarPageWidget里面;

_renderPage() {
  return [
    new GankJsonListPage(),
    new TestListPage(),
    new TestListPage(),
    new TestListPage(),
  ];
}

就样,就可以切换tab了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值