一步一步完成Flutter应用开发-掘金App个人中心互动效果

1,470 阅读3分钟

这遍文章主要是我的页面的个人中心页面.

tutieshi_640x1343_7s.gif 下面是实现的心路历程

SliverAppBar

即使满足不了现有的需求,抱着学习的想法,对其进行学习一下,其主要效果是通过一下属性

// 属性,分别有不同的效果,不是主要介绍就不列举了
 pinned: true,
 floating: ture,
 snap: true,
NestedScrollViewCustomScrollView
headerSliverBuilder构建slivers属性构建
------

NestedScrollView 代码例子

tutieshi_640x1343_5s.gif

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool b) {
          return [
            SliverAppBar(
              pinned: true,
              floating: false,
              expandedHeight: 200,
              flexibleSpace: FlexibleSpaceBar(
                collapseMode: CollapseMode.pin,
                background: Container(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      Container(
                        height: 200,
                        child: Image.network(
                          'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39c71164cc4a455b83ac6f579e2a39c1~tplv-k3u1fbpfcp-watermark.image',
                          fit: BoxFit.fill,
                          height: 220,
                          width: Get.width,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              bottom: TabBar(
                controller: tabController,
                tabs: <Widget>[
                  new Tab(
                    text: "动态",
                  ),
                  new Tab(
                    text: "文章",
                  ),
                  new Tab(
                    text: "沸点",
                  ),
                  new Tab(
                    text: "其他",
                  ),
                ],
              ),
            ),
          ];
        },
        body: TabBarView(
          controller: tabController,
          children: <Widget>[
          ],
        ),
      ),
    );
  }

CustomScrollView 代码例子

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
            elevation: 0,
            expandedHeight: 220,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('一天清晨'),
              background: Image.network(
                'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39c71164cc4a455b83ac6f579e2a39c1~tplv-k3u1fbpfcp-watermark.image',
                fit: BoxFit.cover,
              ),
            ),
          ),
          SliverPersistentHeader(
            pinned: true,
            delegate: TabBarDelegate(
              child: TabBar(
                labelColor: Colors.black,
                controller: this.tabController,
                tabs: <Widget>[
                  Tab(text: '动态'),
                  Tab(text: '文章'),
                  Tab(text: '沸点'),
                  Tab(text: '其他'),
                ],
              ),
            ),
          ),
          SliverFillRemaining(
            child: TabBarView(
              controller: this.tabController,
              children: <Widget>[
              ],
            ),
          ),
        ],
      ),
    );
    };
    
class TabBarDelegate extends SliverPersistentHeaderDelegate {
  final TabBar child;

  TabBarDelegate({@required this.child});

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

  @override
  double get maxExtent => this.child.preferredSize.height;

  @override
  double get minExtent => this.child.preferredSize.height;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }
}

即使没有达到想要的效果,通过自定义头部组件是不是可以达到效果呢?因为在SliverPersistentHeaderDelegate里面的build方法有个shrinkOffset字段,这个是什么意思,打印一下看看:

WechatIMG209.png 这个值为滑动的偏移量的值, 这样就可以使用起来了, 利用透明度来完成一些操作,最终效果:

tutieshi_640x1343_4s.gif 直接在TabBarDelegate类里面构建

Color headerTextColor(shrinkOffset, isIcon) {
    if (shrinkOffset <= 50) {
      return isIcon ? Colors.white : Colors.transparent;
    } else {
      final int alpha = (shrinkOffset / (this.maxExtent - this.minExtent) * 255)
          .clamp(0, 255)
          .toInt();
      return Colors.black.withAlpha(alpha);
    }
  }

  int headerBgColor(shrinkOffset) {
    final int alpha = (shrinkOffset / (this.maxExtent - this.minExtent) * 255)
        .clamp(0, 255)
        .toInt();
    return alpha;
  }
  
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      height: this.maxExtent,
      width: Get.width,
      child: Stack(
        fit: StackFit.expand,
        children: [
          Container(
            child: Image.network(
                'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39c71164cc4a455b83ac6f579e2a39c1~tplv-k3u1fbpfcp-watermark.image',
                fit: BoxFit.cover),
          ),
          Positioned(
            left: 0,
            right: 0,
            top: 0,
            child: Container(
              color: Colors.white
                  .withAlpha(this.headerBgColor(shrinkOffset)),
              child: SafeArea(
                bottom: false,
                child: Container(
                  height: 40,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      IconButton(
                        icon: Icon(
                          Icons.arrow_back_ios,
                          color: this
                              .headerTextColor(shrinkOffset, true),
                        ),
                        onPressed: () => Navigator.pop(context),
                      ),
                      Expanded(
                          child: Row(
                        children: [
                          Container(
                            height: 28,
                            width: 28,
                            margin: EdgeInsets.only(right: 8),
                            decoration: BoxDecoration(
                                color: Colors.red.withAlpha(
                                    this.headerBgColor(shrinkOffset)),
                                borderRadius: BorderRadius.circular(14)),
                          ),
                          Text(
                            '一天清晨',
                            style: TextStyle(
                              fontSize: 20,
                              fontWeight: FontWeight.w500,
                              color: headerTextColor(
                                  shrinkOffset, false),
                            ),
                          ),
                        ],
                      )),
                      IconButton(
                        icon: Icon(
                          Icons.share,
                          color: this
                              .headerTextColor(shrinkOffset, true),
                        ),
                        onPressed: () {},
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
          Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: Container(
                child: this.child,
                decoration: BoxDecoration(color: Colors.blueGrey[100]),
              )),
        ],
      ),
      alignment: Alignment.bottomCenter,
    );
  }

  @override
  double get maxExtent => 500;

  @override
  double get minExtent => Get.context.mediaQueryPadding.top + 88;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }

效果基本上都已经可以了,剩下的就是列表的构建,和基本信息的构建了,首先是基本信息的构建

Positioned(
              bottom: 48,
              left: 0,
              right: 0,
              child: Container(
                width: Get.width,
                height: 180,
                decoration: BoxDecoration(color: Colors.white),
                padding: EdgeInsets.only(left: 20, right: 20),
                child: Column(
                  children: [
                    Padding(padding: EdgeInsets.only(top: 50)),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Column(
                          mainAxisAlignment: MainAxisAlignment.start,
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Row(
                              children: [
                                Text(
                                  '一天清晨',
                                  style: TextStyle(
                                      fontSize: 18, color: Colors.black),
                                ),
                                Container(
                                  margin: EdgeInsets.only(left: 10),
                                  padding: EdgeInsets.symmetric(
                                      horizontal: 5, vertical: 3),
                                  decoration: BoxDecoration(
                                      color: Colors.blue,
                                      borderRadius: BorderRadius.circular(10)),
                                  child: Text(
                                    'LV2',
                                    style: TextStyle(
                                        fontSize: 12,
                                        color: Colors.white,
                                        fontWeight: FontWeight.bold),
                                  ),
                                )
                              ],
                            ),
                            Text(
                              '一天清晨',
                              style:
                                  TextStyle(fontSize: 16, color: Colors.grey),
                            ),
                          ],
                        ),
                        FlatButton(
                          onPressed: () {},
                          child: Text(
                            '编辑',
                            style: TextStyle(color: Colors.blue),
                          ),
                          shape: StadiumBorder(),
                          padding: EdgeInsets.symmetric(horizontal: 10),
                          color: Colors.grey.withOpacity(0.2),
                        )
                      ],
                    ),
                    Padding(padding: EdgeInsets.only(top: 20)),
                    Row(
                      children: [
                        Container(
                          margin: EdgeInsets.only(left: 0),
                          child: Column(
                            children: [
                              Text(
                                '9999',
                                style: TextStyle(
                                    fontSize: 14, color: Colors.black),
                              ),
                              Text(
                                '关注',
                                style:
                                    TextStyle(fontSize: 12, color: Colors.grey),
                              ),
                            ],
                          ),
                        ),
                        Container(
                          margin: EdgeInsets.only(left: 40),
                          child: Column(
                            children: [
                              Text(
                                '9999',
                                style: TextStyle(
                                    fontSize: 14, color: Colors.black),
                              ),
                              Text(
                                '关注者',
                                style:
                                    TextStyle(fontSize: 12, color: Colors.grey),
                              ),
                            ],
                          ),
                        ),
                        Container(
                            margin: EdgeInsets.only(left: 40),
                            child: Column(
                              children: [
                                Text(
                                  '99999',
                                  style: TextStyle(
                                      fontSize: 14, color: Colors.black),
                                ),
                                Text(
                                  '掘力值',
                                  style: TextStyle(
                                      fontSize: 12, color: Colors.grey),
                                ),
                              ],
                            ))
                      ],
                    )
                  ],
                ),
              )),
          Positioned(
              left: 0,
              right: 0,
              bottom: 190,
              child: UnconstrainedBox(
                alignment: Alignment.centerLeft,
                child: Container(
                  width: 80,
                  height: 80,
                  margin: EdgeInsets.only(left: 20),
                  decoration: BoxDecoration(
                      color: Colors.blue,
                      borderRadius: BorderRadius.circular(40),
                      border: Border.all(color: Colors.white, width: 4)),
                ),
              )),

接下来就是列表页面的构建了,这部分就比较简单了,所以直接写一个 直接在SliverFillRemaining里面用我们之前封装好的CommonListWiget就好了

 SliverFillRemaining(
            child: TabBarView(
              controller: tabController,
              children: <Widget>[
                CommonListWiget(
                  networkApi: (currentPage) async {
                    return ['1', '1', '1', '1'];
                  },
                  itemBuilder: (BuildContext context, int position) {
                    return itemWidget(true);
                  },
                ),
                CommonListWiget(
                  networkApi: (currentPage) async {
                    return ['1', '1', '1', '1'];
                  },
                  itemBuilder: (BuildContext context, int position) {
                    return itemWidget(true);
                  },
                ),
                CommonListWiget(
                  networkApi: (currentPage) async {
                    return ['1', '1', '1', '1'];
                  },
                  itemBuilder: (BuildContext context, int position) {
                    return itemWidget(true);
                  },
                ),
                CommonListWiget(
                  networkApi: (currentPage) async {
                    return ['1', '1', '1', '1'];
                  },
                  itemBuilder: (BuildContext context, int position) {
                    return itemWidget(true);
                  },
                ),
              ],
            ),
          ),

over~~~