阅读 506

一步一步完成Flutter应用开发-掘金App沸点页面

这篇主要写的是掘金app的沸点页面,效果如下:

先加个Tabbar

之前的文章写过Tabbar的写法。所以直接用就可以。传送门

WechatIMG194.jpeg

沸点页面头部内容构建

这部分内容跟首页的类似,可以封装成一个组件,传递数据进去,再应用就可以了。目前是这个效果:

tutieshi_640x1343_4s.gif

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class SearchTop extends StatefulWidget {
  SearchTop({Key key}) : super(key: key);

  @override
  _SearchTopState createState() => _SearchTopState();
}

class _SearchTopState extends State<SearchTop> {
  String currentString = '推荐';

  tagButton({title = '推荐'}) {
    return FlatButton(
        onPressed: () {
          setState(() {
            currentString = title;
          });
        },
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Padding(
              padding: EdgeInsets.symmetric(vertical: 5),
              child: Text(title,
                  style: currentString == title
                      ? TextStyle(fontSize: 20, color: Colors.blue)
                      : TextStyle(fontSize: 18, color: Colors.grey)),
            ),
            Offstage(
              offstage: currentString != title,
              child: Container(
                height: 2,
                width: title.length.toDouble() * 20,
                decoration: BoxDecoration(color: Colors.blue),
              ),
            )
          ],
        ));
  }

  renderTag() {
    return Container(
      width: Get.width,
      decoration: BoxDecoration(color: Colors.white),
      padding: EdgeInsets.only(
          top: Get.context.mediaQueryPadding.top, left: 10, right: 10),
      child: Row(
        children: [
          Expanded(
              child: SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            child: Row(
              children: [
                '推荐',
                '热门',
                '关注',
                '前端',
                '上班摸鱼',
                '内推招聘',
                '树洞一下',
                '今天学到了',
                '一图胜千言',
                '每日算法题',
              ].map<Widget>((e) => tagButton(title: e)).toList(),
            ),
          )),
          InkWell(
            onTap: () {},
            child: Container(
              padding: EdgeInsets.only(left: 10),
              child: Icon(
                Icons.list_alt,
                color: Colors.grey,
                size: 24,
              ),
            ),
          )
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return renderTag();
  }
}

复制代码

下半部分列表构建

先分析一波页面,分上下两部分,上部分是一个横向滚动的列表,下半部分是沸点内容列表,有图片类型,文字类型,链接类型

横向滚动的列表构建

Simulator Screen Shot - iPhone 11 - 2021-03-17 at 13.10.22.png 控制在列表中返回的第一个item为横向滚动视图, 新建search_hot.dart文件

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class SearchHot extends StatefulWidget {
  SearchHot({Key key}) : super(key: key);

  @override
  _SearchHotState createState() => _SearchHotState();
}

class _SearchHotState extends State<SearchHot> {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      width: Get.width - 20,
      margin: EdgeInsets.only(top: 10, left: 10),
      padding: EdgeInsets.symmetric(horizontal: 20),
      decoration: BoxDecoration(
          color: Colors.white, borderRadius: BorderRadius.circular(5)),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Expanded(
              child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                '🔥有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看',
                style: TextStyle(fontSize: 18),
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
              ),
              Padding(
                padding: EdgeInsets.only(top: 5),
                child: Text(
                  '点赞89·阅读123·一天清晨',
                  style: TextStyle(fontSize: 14, color: Colors.grey),
                ),
              )
            ],
          )),
          Offstage(
            offstage: false,
            child: Container(
              height: 60,
              width: 60,
              decoration: BoxDecoration(color: Colors.red),
            ),
          )
        ],
      ),
    );
  }
}

复制代码

在页面使用

@override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
      children: [
        SearchTop(),
        Expanded(
            child: CommonListWiget(
          networkApi: (currentPage) async {
            Future.delayed(Duration(milliseconds: 500)).then((value) => {
                  dataList.addAll(['1', '2'])
                });
            return dataList;
          },
          itemBuilder: (BuildContext context, int position) {
            if (position == 0) {
              return SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: Row(
                  children: [
                    SearchHot(),
                    SearchHot(),
                    SearchHot(),
                  ],
                ),
              );
            }
            return ListTile(
              title: Text('测试' + position.toString()),
              subtitle: Text('一些描述'),
            );
          },
        )),
      ],
    ));
  }
复制代码

底部列表构建

首先新建search_item.dart文件,先分析主要有,全文字, 文字加图片组合 一步一步来

沸点信息

Simulator Screen Shot - iPhone 11 - 2021-03-17 at 13.24.56.png

renderAvatar() {
    return Row(
      children: [
        ClipOval(
          child: Container(
            height: 40,
            width: 40,
            decoration: BoxDecoration(color: Colors.red),
          ),
        ),
        Container(
          margin: EdgeInsets.symmetric(horizontal: 10),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                '树洞robot',
                style: TextStyle(fontSize: 16, color: Colors.black),
              ),
              Text(
                '自动匿名机器人 @ #树洞一下#·31分钟前',
                style: TextStyle(fontSize: 14, color: Colors.grey),
              ),
            ],
          ),
        )
      ],
    );
  }
  
  @override
  Widget build(BuildContext context) {
    return Container(
      width: Get.width,
      decoration: BoxDecoration(color: Colors.white),
      padding: EdgeInsets.all(15),
      margin: EdgeInsets.only(top: 10),
      child: Column(
        children: [
          renderAvatar(),
        ],
      ),
    );
  }
复制代码

接下来是根据type区分显示文字还是图片

不同状态显示

显示文字,其中包含功能,行数大于3之后,显示展开按钮,点击即展开。

tutieshi_640x1343_4s.gif

//TQExpandableText 为封装的一个工具widget
renderType() {
    if (widget.type == 1) {
      //显示文字
      return Container(
        margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
        child: TQExpandableText(
          '有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看有新工具了,赶紧来看看来看看',
          expandText: '全文',
          collapseText: '收起',
          maxLines: 3,
          style: TextStyle(fontSize: 14, color: Colors.black.withOpacity(0.8)),
        ),
      );
    } else if (widget.type == 2) {
      //显示文字加图片
    } else if (widget.type == 3) {
      // 显示文字加图片
    }
    return Container();
  }
复制代码

显示图片,点击图片有放大效果,主要使用Hero动画完成的

tutieshi_640x1343_2s.gif

主要代码(ps: 其中图片是在掘金沸点找的一张网络图片😄)

//显示文字加图片
      return Hero(
          tag: 'imgaesView',
          child: Material(
            child: InkWell(
                onTap: () {
                  Navigator.push(
                      context,
                      new PageRouteBuilder(
                        transitionDuration: const Duration(milliseconds: 250),
                        pageBuilder: (context, _, __) => Scaffold(
                          body: InkWell(
                            onTap: () {
                              Navigator.of(context).pop();
                            },
                            child: Hero(
                                tag: 'imgaesView',
                                child: Container(
                                  width: Get.width,
                                  height: Get.height,
                                  child: Image.network(
                                      'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39c71164cc4a455b83ac6f579e2a39c1~tplv-k3u1fbpfcp-watermark.image'),
                                )),
                          ),
                        ),
                        transitionsBuilder: (_, Animation<double> animation, __,
                                Widget child) =>
                            new SlideTransition(
                                position: new Tween<Offset>(
                                  begin: Offset(0.0, 1.0),
                                  end: Offset(0.0, 0.0),
                                ).animate(animation),
                                child: child),
                      ));
                },
                child: Container(
                  margin: EdgeInsets.only(top: 10, left: 10),
                  width: 200,
                  height: 140,
                  child: Image.network(
                      'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39c71164cc4a455b83ac6f579e2a39c1~tplv-k3u1fbpfcp-watermark.image'),
                )),
          ));
复制代码

最后联动效果

底部列表滑动,标签随之滑动。 tutieshi_640x1343_4s.gif 修改search_page.dart文件,就ok了。search_hot.dart的滑动和之前写的首页的滑动是一样的,就不把代码贴出来了。一样的方式

@override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
      children: [
        SearchTop(
          dataList: tag,
          currentIndex: currentIndex,
          onTag: (value) {
            setState(() {
              currentIndex = value;
            });
            int index = tag.indexWhere((element) => element == value);
            controller.animateToPage(index,
                duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
          },
        ),
        Expanded(
            child: PageView.builder(
          controller: controller,
          itemBuilder: (context, index) {
            return CommonListWiget(
              networkApi: (currentPage) async {
                Future.delayed(Duration(milliseconds: 500)).then((value) => {
                      dataList.addAll(['1', '2'])
                    });
                return dataList;
              },
              itemBuilder: (BuildContext context, int position) {
                if (position == 0) {
                  return SingleChildScrollView(
                    scrollDirection: Axis.horizontal,
                    child: Row(
                      children: [
                        SearchHot(),
                        SearchHot(),
                        SearchHot(),
                      ],
                    ),
                  );
                }
                return SearchItem(type: position);
              },
            );
          },
          scrollDirection: Axis.horizontal,
          onPageChanged: (index) {
            print(tag[index]);
            setState(() {
              currentIndex = tag[index];
            });
            print(currentIndex);
          },
          itemCount: tag.length,
        )),
      ],
    ));
  }
复制代码
文章分类
Android
文章标签