flutter实现首页

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/villa_mou/article/details/85123152

首先了解几个widget

SliverAppBar

Flutter中的Slivers大家族基本都是配合CustomScrollView来实现的,除了上面提到的滑动布局嵌套,你还可以使用Slivers来实现页面头部展开/收起、 AppBar随手势变换等等功能。

给SliverAppBar设置flexibleSpace(展开后的内容)和expandedHeight(展开高度)属性,就可以轻松完成AppBar展开/收起的功能

  • floating

如果设置floating属性为true,那么AppBar会在你做出下拉手势时就立即展开(即使ListView并没有到达顶部),该展开状态不显示flexibleSpace:

  • snap

如果同时设置floating和snap属性为true,那么AppBar会在你做出下拉手势时就立即全部展开(即使ListView并没有到达顶部),该展开状态显示flexibleSpace:

  • pinned

如果不想上滑到顶时AppBar消失,则设置pinned属性为true即可:

  • preferredSize

只要你的控件实现了 preferredSize,就可以放到 AppBar 的 bottom 中使用。比如下图搜索栏,这是TabView下的页面又实用了AppBar。

SliverList

SliverChildListDelegate一般用来构item建数量明确的列表,会提前build好所有的子item,所以在效率上会有问题,适合item数量不多的情况(不超过一屏)。
SliverChildBuilderDelegate构建的列表理论上是可以无限长的,因为使用来lazily construct优化。
(两者的区别有些类似于ListView和ListView.builder()的区别。)

   new SliverAppBar( //头部为一个SliverAppBar,折叠部分的内容都放在了flexibleSpace中。
                pinned: true,
                expandedHeight: 300.0,
                // 这个高度必须比flexibleSpace高度大
               forceElevated: innerBoxIsScrolled,
                bottom: PreferredSize(
                    child: new Container(
                      child: new TabBar(
                        tabs: tabs,
                        labelColor: Colors.green,
                        unselectedLabelColor: Colors.grey,),
                      color: Colors.white,
                    ),
                    preferredSize: new Size(double.infinity, 46.0)
                ),

                flexibleSpace: new Container(
                  child: new Column(
                    children: <Widget>[
                      new AppBar(
                        title: Text("首页"),
                        centerTitle: true,
                      ),
                      new Expanded(
                        child: new Container(
                          child: Image.asset(
                            "images/temp2.jpg",
                            repeat: ImageRepeat.repeat,
                          ),
                          width: double.infinity,
                        ),
                      )
                    ],
                  ),
                ),
              ),

SliverPersistentHeader

SliverPersistentHeader顾名思义,就是给一个可滑动的视图添加一个头(实际上,在CustomScrollView的slivers列表中,header可以出现在视图的任意位置,不一定要是在顶部)。这个Header会随着滑动而展开/收起,使用pinned和floating属性来控制收起时Header是否展示(pinned和floating属性不可以同时为true),pinned和floating属性的具体意义和SliverAppBar中相同,这里就不再次解释了。

写一个自定义SliverPersistentHeaderDelegate很简单,只需重写build()、get maxExtent、get minExtent和shouldRebuild()这四个方法,上面就是一个最简单的SliverPersistentHeaderDelegate的实现。其中,maxExtent表示header完全展开时的高度minExtent表示header在收起时的最小高度。因此,对于我们上面的那个自定义Delegate,如果将minHeight和maxHeight的值设置为相同时,header就不会收缩了,这样的Header跟我们平常理解的Header更像。
例子:

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => max(maxHeight, minHeight);

  @override
  Widget build(BuildContext context, double shrinkOffset,
      bool overlapsContent) {
    return child;
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

SliverToBoxAdapter

SliverPersistentHeader一般来说都是会展开/收起的(除非minExtent和maxExtent值相同),那么如果想要在滚动视图中添加一个普通的控件,那么就可以使用SliverToBoxAdapter来将各种视图组合在一起,放在CustomListView中。

看下我实现的效果:
在这里插入图片描述

代码:

import 'package:flutter/material.dart';
import 'package:mou/navigationbar/item/HomeGridItem.dart';
import 'package:mou/navigationbar/item/HomeItem.dart';
import 'package:mou/toast/Toast.dart';
import 'dart:math';

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new HomeState();
  }
}

class HomeState extends State<HomePage> {
  List<HomeItem> widgets = [];
  List<HomeGridItem> Gridwidgets = [];

  @override
  Widget build(BuildContext context) {
    return new DefaultTabController(
        length: 2,
        child: new Scaffold(
          body: new CustomScrollView(
              slivers: <Widget>[
                new SliverAppBar( //头部为一个SliverAppBar,折叠部分的内容都放在了flexibleSpace中。
                  pinned: true,
//                    floating: true,//那么AppBar会在你做出下拉手势时就立即展开
                  expandedHeight: 220.0,
                  // 这个高度必须比flexibleSpace高度大
//            forceElevated: innerBoxIsScrolled,
                  bottom: PreferredSize(
                      child: new Container(
                          width: 500,
                          height: 46,
                          color: Colors.white,
                          padding: EdgeInsets.only(left: 15.0, right: 15.0,top: 5.0, bottom: 5.0),
                          child: new Material(
                            color: Colors.white,
                            borderRadius: BorderRadius.circular(20.0),
                            elevation: 5.0,
                            child: new Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: <Widget>[
                                new Icon(Icons.search),
                                new Text('点击搜索',
                                    textAlign: TextAlign.center,
                                    style: new TextStyle(
                                        color: Colors.grey,
                                        fontSize: 16
                                    )
                                ),
                              ],
                            ),
                          )
                      ),
                      //只要你的控件实现了 preferredSize,就可以放到 AppBar 的 bottom 中使用。比如下图搜索栏,这是TabView下的页面又实用了AppBar。
                      preferredSize: new Size(double.infinity, 46.0)
                  ),

                  flexibleSpace: new Container(
                    child: new Column(
                      children: <Widget>[
                        new AppBar(
                          title: Text("首页"),
                          centerTitle: true,
                        ),
                        new Expanded(
                          child: new Container(
                            child: Image.asset(
                              "images/temp2.jpg",
                              fit: BoxFit.cover,
                            ),
                            width: double.infinity,
                          ),
                        )
                      ],
                    ),
                  ),
                ),
//                SliverPersistentHeader(
////                pinned: true,
////                floating: true,
//                  delegate: _SliverAppBarDelegate(
//                      minHeight: 50.0,
//                      maxHeight: 50.0,
//                      child: new Container(
//                          width: 500,
//                          margin: EdgeInsets.only(
//                              left: 15.0, right: 15.0, top: 10.0),
//                          child: new Material(
//                            color: Colors.white,
//                            borderRadius: BorderRadius.circular(20.0),
//                            elevation: 5.0,
//                            child: new Center(
//                              child: new Text('点击搜索',
//                                  textAlign: TextAlign.center,
//                                  style: new TextStyle(
//                                      color: Colors.grey,
//                                      fontSize: 18
//                                  )
//                              ),
//                            ),
//                          )
//                      )
//                  ),
//                ),

                new SliverList(
                  delegate: SliverChildBuilderDelegate((BuildContext context,
                      int index) {
                    return getItem(index);
                  },
                    childCount: widgets.length,
                  ),
                ),


                new SliverToBoxAdapter(
                  child: new Container(
                      margin: EdgeInsets.only(top: 15.0,),
                      child: new Row(
                        children: <Widget>[
                          new Expanded(child: new Container(
                            child: new Text("新闻内容1",
                              style: new TextStyle(
                                  color: Colors.black45
                              ),
                            ),
                            padding: EdgeInsets.only(left: 20.0, top: 20.0),
                            margin: EdgeInsets.only(left: 15.0, right: 5.0),
                            height: 150,
                            decoration: new BoxDecoration(
                                borderRadius: BorderRadius.all(
                                    const Radius.circular(5.0)),
                                image: DecorationImage(
                                    image: AssetImage("images/temp3.jpg"),
                                    fit: BoxFit.cover)
                            ),
                          )
                          ),
                          new Expanded(child: new Column(
                            children: <Widget>[
                              new Container(
                                child: new Text("新闻内容2",
                                  style: new TextStyle(
                                      color: Colors.black45
                                  ),
                                ),
                                height: 70,
                                padding: EdgeInsets.only(left: 10.0, top: 10.0),
                                margin: EdgeInsets.only(
                                  left: 5.0,
                                  right: 15.0,
                                  bottom: 5.0,),
                                width: double.infinity,
                                decoration: new BoxDecoration(
                                    borderRadius: BorderRadius.all(
                                        const Radius.circular(5.0)),
                                    image: DecorationImage(
                                        image: AssetImage("images/temp3.jpg"),
                                        fit: BoxFit.fill)
                                ),
                              ),
                              new Container(
                                padding: EdgeInsets.only(left: 10.0, top: 10.0),
                                margin: EdgeInsets.only(
                                    left: 5.0,
                                    right: 15,
                                    top: 5.0),
                                child: new Text("新闻内容3",
                                  style: new TextStyle(
                                      color: Colors.black45
                                  ),),
                                height: 70,
                                width: double.infinity,
                                decoration: new BoxDecoration(
                                    borderRadius: BorderRadius.all(
                                        const Radius.circular(5.0)),
                                    image: DecorationImage(
                                        image: AssetImage("images/temp3.jpg"),
                                        fit: BoxFit.fill)
                                ),
                              )
                            ],
                          ))
                        ],
                      )
                  ),
                ),

                new SliverList(
                  delegate: SliverChildBuilderDelegate((BuildContext context,
                      int index) {
                    return getItem(index);
                  },
                    childCount: widgets.length,
                  ),
                ),

                SliverPersistentHeader(
//                pinned: true,
//                floating: true,
                  delegate: _SliverAppBarDelegate(
                      minHeight: 60.0,
                      maxHeight: 180.0,
                      child: new Card(
                        margin: EdgeInsets.only(left: 10.0, right: 10.0),
                        elevation: 5.0,
                        child: new Container(
                            padding: EdgeInsets.only(bottom: 20.0),
                            alignment: Alignment.bottomCenter,
                            decoration: new BoxDecoration(
                                image: DecorationImage(image: NetworkImage(
                                    "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1134704593,3837890513&fm=26&gp=0.jpg"),
                                  fit: BoxFit.cover,
                                )
                            ),
                            child: new Text("这是中间一个条目",
                              style: new TextStyle(
                                color: Colors.white,
                                fontSize: 26,
                              ),
                            )
                        ),
                      )
                  ),
                ),

                SliverGrid(
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 2, //一行多少个
                  ),
                  delegate: SliverChildBuilderDelegate(
                        (BuildContext context, int index) {
                      return getGridItem(index);
                    },
                    childCount: Gridwidgets.length,
                  ),

                ),
              ]
          ),
        )
    );
  }

  @override
  void initState() {
    super.initState();
    for (var i = 0; i < 2; i++) {
      widgets.add(HomeItem(
          title: "大学生活",
          position: i,
          content: "啊啊啊啊啊啊啊啊啊啊啊啊啊",
          time: "2018-10-2",
          coverUrl: "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3155928136,23109883&fm=27&gp=0.jpg"));
    }

    for (var i = 0; i < 5; i++) {
      Gridwidgets.add(HomeGridItem(
          title: "大学生活",
          position: i,
          content: "啊啊啊啊啊啊啊啊啊啊啊啊啊",
          time: "2018-10-2",
          coverUrl: "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3155928136,23109883&fm=27&gp=0.jpg"));
    }
  }

  Widget getItem(int i) {
    return new HomeItem(
      title: widgets[i].title,
      position: widgets[i].position,
      content: widgets[i].content,
      time: widgets[i].time,
      coverUrl: widgets[i].coverUrl,
      callBack: (postition, item) {
        Toast.toast(context, "position$postition");
      },
    );
  }

  Widget getGridItem(int i) {
    return new HomeGridItem(
      title: Gridwidgets[i].title,
      position: Gridwidgets[i].position,
      content: Gridwidgets[i].content,
      time: Gridwidgets[i].time,
      coverUrl: Gridwidgets[i].coverUrl,
      callBack: (postition, item) {
        Toast.toast(context, "position$postition");
      },
    );
  }
}


class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => max(maxHeight, minHeight);

  @override
  Widget build(BuildContext context, double shrinkOffset,
      bool overlapsContent) {
    return child;
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

import 'package:flutter/material.dart';
import 'package:mou/toast/Toast.dart';

class HomeGridItem extends StatelessWidget {
  HomeGridItem({this.title,
    this.position,
    this.content,
    this.time,
    this.coverUrl,
    this.callBack,})
      : super(key: new ObjectKey(position)); //ObjectKey唯一的标识位
  String title;
  int position;
  String content;
  String coverUrl;
  String time;
  CallBack callBack;

  @override
  Widget build(BuildContext context) {
    print('bookId:$position');
    Widget titleSection = new Container(
        padding: const EdgeInsets.fromLTRB(10.0, 8.0, 10.0, 8.0),
        child:
        new Card(
          elevation: 5.0,
          child:new Container(
            padding: const EdgeInsets.all(10.0),
            child: new Column(
              children: <Widget>[
                new Container(
                  width: double.infinity,
                  height: 100.0,
                  margin: EdgeInsets.fromLTRB(0, 0, 0, 10.0),
                  child: new Image.network(coverUrl,
                    fit: BoxFit.cover,
                  ),
                ),
                new Text(
                  title,
                  style: new TextStyle(
                      fontSize: 18,
                      color: Color(0xFF333333)
                  ),
                ),
              ],
            ),
          )
        )
    );
    return new GestureDetector(
      child: titleSection,
      onTap: () {
        callBack(position, this);
      },
    );
  }
}

typedef void CallBack(int position,
    HomeGridItem item,);

猜你喜欢

转载自blog.csdn.net/villa_mou/article/details/85123152