阅读 263

一步一步完成Flutter应用开发-掘金App首页功能完善

接着上文,把掘金app的首页完善一下。不同标签下的展示是不同的样式。

根据标签改变内容

效果:

tutieshi_640x1343_5s.gif

思路是把点击标签的事件传递出来,然后判断标签展示不用的内容

点击事件传递home_top.dart文件中,tagButton方法里面传递参数,这里只截取一部分代码

 tagButton({title = '标签'}) {
    return FlatButton(
        onPressed: () {
          setState(() {
            currentString = title;
          });
          widget.onPress(title);
        },
 )}

复制代码

在home_list.dart中接受参数,显示不同内容

@override
  Widget build(BuildContext context) {
    return CommonListWiget(
      scrollController: _scrollController,
      networkApi: (currentPage) async {
        Future.delayed(Duration(seconds: 2)).then((value) => {
              dataList.addAll(['1', '2'])
            });
        return dataList;
      },
      itemBuilder: (BuildContext context, int position) {
        if (position == 0) {
          return widget.tag == '关注' ? peopleItems() : hotWidget();
        }
        return listItemWidget();
      },
    );
  }
复制代码

其中的peopleItems方法内容就是页面构造的内容:

// Global.randomColor() 写的一个随机颜色的取值方法,写在了Utils文件夹的Global文件里面
renderAvatar(index) {
    return Positioned(
        right: 18 + index * 16.toDouble(),
        top: 15,
        child: ClipOval(
          child: Container(
            height: 20,
            width: 20,
            decoration: BoxDecoration(color: Global.randomColor()),
          ),
        ));
  }

peopleItems() {
    return Container(
      height: 50,
      padding: EdgeInsets.symmetric(horizontal: 10),
      margin: EdgeInsets.only(top: 10),
      decoration: BoxDecoration(color: Colors.white),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text(
            '发现更多掘金优秀作者',
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          Expanded(
              child: Stack(
            children: [
              renderAvatar(0),
              renderAvatar(1),
              renderAvatar(2),
              renderAvatar(3),
              Positioned(
                right: 0,
                top: 15,
                child: Icon(
                  Icons.keyboard_arrow_right,
                  color: Colors.grey,
                  size: 18,
                ),
              )
            ],
          )),
        ],
      ),
    );
  }
复制代码

展示不同标签下的功能标签

效果:

思路是在home_top.dart中写逻辑代码

 String currentTag = '全部';
 
 renderTagType() {
    return Offstage(
      offstage: currentString == '关注' || currentString == '推荐',
      child: Container(
        height: 50,
        width: Get.width,
        padding: EdgeInsets.symmetric(horizontal: 10),
        margin: EdgeInsets.only(top: 95 + (0 - 40 * widget.alpha / 255)),
        child: Row(
          children: [
            Expanded(
                child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: [
                  renderTagTypeButton('全部'),
                  renderTagTypeButton('Java'),
                  renderTagTypeButton('后端'),
                  renderTagTypeButton('Spring Boot'),
                  renderTagTypeButton('Go'),
                  renderTagTypeButton('Python'),
                  renderTagTypeButton('MySQL'),
                  renderTagTypeButton('Spring'),
                  renderTagTypeButton('Redius'),
                  renderTagTypeButton('JVM'),
                  renderTagTypeButton('算法'),
                  renderTagTypeButton('数据库'),
                ],
              ),
            )),
            InkWell(
              onTap: () {},
              child: Icon(
                Icons.keyboard_arrow_right,
                color: Colors.grey,
                size: 18,
              ),
            ),
          ],
        ),
      ),
    );
  }
  
  renderTagTypeButton(title) {
    return Padding(
        padding: EdgeInsets.symmetric(horizontal: 5),
        child: FlatButton(
            height: 30,
            minWidth: 20,
            shape: StadiumBorder(),
            padding: EdgeInsets.symmetric(horizontal: 10),
            color: currentTag == title
                ? Colors.blue
                : Colors.grey.withOpacity(0.2),
            onPressed: () {
              setState(() {
                currentTag = title;
              });
            },
            child: Text(
              title,
              style: TextStyle(
                  fontSize: 14,
                  color: currentTag == title ? Colors.white : Colors.grey),
            )));
  }
复制代码

最终把方法加入到build中

//renderTag 是标签方法,被提取出来放到一个方法里面
 @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(top: Get.context.mediaQueryPadding.top),
      decoration: BoxDecoration(color: Colors.white),
      child: Stack(
        children: [renderSearch(), renderTag(), renderTagType()],
      ),
    );
  }
复制代码

实现最终效果

分两步来实现,第一步点击展开的时候,将标签展开

第一步

tutieshi_640x1343_4s.gif 其中WidgetUtil方法用到之前集成的库flustars获取的每个标签的宽度,存储到scrollerIndex数组里面。这样每次点击可以进行滑动到指定位置操作。

//判断是否展开
bool isExp = false;
ScrollController tagScroller = new ScrollController();
// 记录滚动位置数组
List scrollerIndex = [];


renderTagTypeButton(title) {
   return Builder(builder: (BuildContext context) {
     Rect rect = WidgetUtil.getWidgetBounds(context);
     double width = rect.right +
         (scrollerIndex.length > 0
             ? scrollerIndex[scrollerIndex.length - 1]
             : 0);
     if (scrollerIndex.length < data.length) {
       scrollerIndex.add(width);
     }
     return Padding(
         padding: EdgeInsets.symmetric(horizontal: 5),
         child: FlatButton(
             height: 30,
             minWidth: 20,
             shape: StadiumBorder(),
             padding: EdgeInsets.symmetric(horizontal: 10),
             color: currentTag == title
                 ? Colors.blue
                 : Colors.grey.withOpacity(0.2),
             onPressed: () {
               setState(() {
                 currentTag = title;
                 isExp = false;
               });
               int index = data.indexWhere((element) => element == title);
               Future.delayed(Duration(milliseconds: 200)).then((value) => {
                     tagScroller.jumpTo(
                         scrollerIndex.length > 0 && index - 3 > 0
                             ? scrollerIndex[index - 3]
                             : 0)
                   });
             },
             child: Text(
               title,
               style: TextStyle(
                   fontSize: 14,
                   color: currentTag == title ? Colors.white : Colors.grey),
             )));
   });
 }

复制代码

然后在添加下拉的点击方法中isExp字段判断,如果展开是用Wrap去构建。

renderTagType() {
   return Offstage(
     offstage: currentString == '关注' || currentString == '推荐',
     child: Container(
       width: Get.width,
       padding: EdgeInsets.symmetric(horizontal: 10),
       margin: EdgeInsets.only(top: 95 + (0 - 40 * widget.alpha / 255)),
       child: Row(
         children: [
           Expanded(
               child: isExp
                   ? Wrap(
                       children: data
                           .map<Widget>((e) => renderTagTypeButton(e))
                           .toList(),
                     )
                   : SingleChildScrollView(
                       scrollDirection: Axis.horizontal,
                       controller: tagScroller,
                       child: Row(
                         children: data
                             .map<Widget>((e) => renderTagTypeButton(e))
                             .toList(),
                       ),
                     )),
           Offstage(
               offstage: isExp,
               child: InkWell(
                 onTap: () {
                   setState(() {
                     isExp = !isExp;
                   });
                 },
                 child: Container(
                   padding: EdgeInsets.only(left: 10),
                   child: Icon(
                     Icons.keyboard_arrow_down,
                     color: Colors.grey,
                     size: 24,
                   ),
                 ),
               ))
         ],
       ),
     ),
   );
 }
复制代码

第二步

效果:

tutieshi_640x1343_4s (1).gif 以上代码还没有完全实现其功能,在展开的时候列表还是可以滑动的,这个时候我们去给列表添加一个遮盖,使其不能操作即可。 其实很好实现,在home_list.dart文件中,增加stack组件,判断是否是展开状态,如果是则添加遮盖,隔断响应手势的方法。就可以了。 首先我们需要把是否展开的状态传递出去,在home_top.dart中定义

final Function onTypePress;
复制代码

点击对应部分传参数出去

//true展开,false未展开
widget.onTypePress(false);
复制代码

对应位置就是点击标签之后

renderTagTypeButton(
    ...省略代码
    
);
tagButton(
 ...省略代码
)
复制代码

两个方法中使用 接着在home_list.dart文件中添加遮盖。

final bool isExp;

@override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        CommonListWiget(
          scrollController: _scrollController,
          networkApi: (currentPage) async {
            Future.delayed(Duration(seconds: 2)).then((value) => {
                  dataList.addAll(['1', '2'])
                });
            return dataList;
          },
          itemBuilder: (BuildContext context, int position) {
            if (position == 0 && widget.tag == '关注') {
              return peopleItems();
            } else if (position == 0 && widget.tag == '推荐') {
              return hotWidget();
            }
            return listItemWidget();
          },
        ),
        Positioned(
            top: widget.isExp ? 0 : Get.height,
            child: InkWell(
              onTap: () {},
              child: Container(
                height: Get.height,
                width: Get.width,
                decoration: BoxDecoration(
                  color: Colors.black.withOpacity(0.1),
                ),
              ),
            )),
      ],
    );
  }
复制代码

最后在home_page.dart文件中去传递参数

@override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Color.fromRGBO(235, 242, 244, 1),
        body: Column(
          children: [
            HomeTopPage(
                onSearchPress: () {},
                onTagPress: () {},
                alpha: _alpha,
                onTypePress: (isBool) {
                  setState(() {
                    isExp = isBool;
                  });
                },
                onPress: (title) {
                  setState(() {
                    tag = title;
                  });
                }),
            Expanded(
                child: HomeList(
                    onScroll: (value) {
                      setState(() {
                        _alpha = value;
                      });
                    },
                    tag: tag,
                    isExp: isExp)),
          ],
        ));
  }
复制代码

搜索页面

这个页面需要注意的问题,没有太多,都是一些基本的逻辑和编码,所以直接看效果上代码了,两个页面的代码都在下面了

tutieshi_640x1343_10s.gif

import 'package:flutter/material.dart';
import 'package:flutter_common_app/widget/common_list.dart';
import 'package:get/get.dart';
import 'package:sp_util/sp_util.dart';

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

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

class _HomeSearchPageState extends State<HomeSearchPage> {
  String currentString = '综合';
  PageController controller = new PageController();
  List dataList = [];
  List historyList = [];

  @override
  void initState() {
    super.initState();
    List temp = SpUtil.getStringList('historyKey').toList();
    setState(() {
      historyList = temp;
    });
  }

  renderHistory() {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
      height: 40,
      decoration: BoxDecoration(color: Colors.white),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Text(
                '搜索历史',
                style: TextStyle(
                    fontSize: 18, color: Colors.grey.withOpacity(0.8)),
              ),
              IconButton(
                  icon: Icon(
                    Icons.auto_delete,
                    size: 18,
                    color: Colors.grey,
                  ),
                  onPressed: () {
                    SpUtil.putStringList('historyKey', []);
                    setState(() {
                      historyList = [];
                    });
                  })
            ],
          ),
          Wrap(
            children: historyList
                .map<Widget>((e) => FlatButton(
                    height: 30,
                    minWidth: 20,
                    shape: StadiumBorder(),
                    padding: EdgeInsets.symmetric(horizontal: 10),
                    color: Colors.grey.withOpacity(0.2),
                    onPressed: () {},
                    child: Text(
                      e,
                      style: TextStyle(fontSize: 14, color: Colors.grey),
                    )))
                .toList(),
          )
        ],
      ),
    );
  }

  tagButton({title = '综合'}) {
    return FlatButton(
        onPressed: () {
          setState(() {
            currentString = title;
          });
          int tag = 0;
          if (title == '综合') {
            tag = 0;
          } else if (title == '文章') {
            tag = 1;
          } else if (title == '标签') {
            tag = 2;
          } else if (title == '用户') {
            tag = 3;
          }
          controller.animateToPage(tag,
              duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
        },
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Padding(
              padding: EdgeInsets.symmetric(vertical: 5),
              child: Text(title,
                  style: currentString == title
                      ? TextStyle(fontSize: 18, color: Colors.blue)
                      : TextStyle(fontSize: 18, color: Colors.grey)),
            ),
            Offstage(
              offstage: currentString != title,
              child: Container(
                height: 2,
                width: 40,
                decoration: BoxDecoration(color: Colors.blue),
              ),
            )
          ],
        ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
      children: [
        Container(
          decoration: BoxDecoration(color: Colors.white),
          padding: EdgeInsets.only(top: Get.context.mediaQueryPadding.top),
          child: Row(
            children: [
              Expanded(
                  child: Container(
                margin: EdgeInsets.only(left: 10),
                height: 40,
                decoration: BoxDecoration(
                    color: Colors.grey.withOpacity(0.2),
                    borderRadius: BorderRadius.circular(8)),
                child: Row(
                  children: [
                    Padding(padding: EdgeInsets.symmetric(horizontal: 5)),
                    Icon(
                      Icons.search,
                      size: 18,
                      color: Colors.grey.withOpacity(0.5),
                    ),
                    Padding(padding: EdgeInsets.symmetric(horizontal: 5)),
                    Expanded(
                        child: TextField(
                      autofocus: true,
                      onSubmitted: (value) {
                        List temp = SpUtil.getStringList('historyKey').toList();
                        temp.add(value);
                        SpUtil.putStringList('historyKey', temp);
                        setState(() {
                          historyList = temp;
                          dataList = value.contains('一天清晨') ? ['1', '1'] : [];
                        });
                      },
                      decoration: InputDecoration(
                        hintText: '搜索文章/标签/用户',
                        // border: InputBorder.none,
                        contentPadding: EdgeInsets.only(top: 5),
                        border: OutlineInputBorder(
                            borderSide: BorderSide(
                                width: 0, color: Colors.transparent)),
                        focusedBorder: OutlineInputBorder(
                            borderSide: BorderSide(
                                width: 0, color: Colors.transparent)),
                        disabledBorder: OutlineInputBorder(
                            borderSide: BorderSide(
                                width: 0, color: Colors.transparent)),
                        enabledBorder: OutlineInputBorder(
                            borderSide: BorderSide(
                                width: 0, color: Colors.transparent)),
                      ),
                      style: TextStyle(fontSize: 14, color: Colors.grey),
                    ))
                  ],
                ),
              )),
              FlatButton(
                  minWidth: 20,
                  onPressed: () {
                    Get.back();
                  },
                  child: Text('取消'),
                  textColor: Colors.blue),
            ],
          ),
        ),
        Container(
          decoration: BoxDecoration(color: Colors.white),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              tagButton(title: '综合'),
              tagButton(title: '文章'),
              tagButton(title: '标签'),
              tagButton(title: '用户'),
            ],
          ),
        ),
        Expanded(
            child: dataList.length == 0
                ? renderHistory()
                : PageView.builder(
                    controller: controller,
                    itemBuilder: (context, index) {
                      return CommonListWiget(
                        networkApi: (currentPage) async {
                          // Future.delayed(Duration(seconds: 2)).then((value) => {
                          //       dataList.addAll(['1', '2'])
                          //     });
                          return dataList;
                        },
                        itemBuilder: (BuildContext context, int position) {
                          return Container(
                            padding: EdgeInsets.all(10),
                            margin: EdgeInsets.only(top: 5),
                            decoration: BoxDecoration(color: Colors.white),
                            child: Row(
                              children: [
                                Container(
                                  height: 50,
                                  width: 50,
                                  margin: EdgeInsets.only(right: 10),
                                  decoration: BoxDecoration(
                                      color: Colors.red,
                                      borderRadius:
                                          BorderRadius.all(Radius.circular(5))),
                                ),
                                Expanded(
                                    child: Container(
                                  height: 50,
                                  child: Column(
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceBetween,
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                        '前端',
                                        style: TextStyle(
                                            fontSize: 16, color: Colors.grey),
                                      ),
                                      Text(
                                        '50.0w 人关注·5.2w文章',
                                        style: TextStyle(
                                            fontSize: 14, color: Colors.grey),
                                      ),
                                    ],
                                  ),
                                )),
                                Container(
                                  height: 30,
                                  width: 70,
                                  alignment: Alignment.center,
                                  margin: EdgeInsets.only(left: 10),
                                  decoration: BoxDecoration(
                                    color: Colors.grey.withOpacity(0.3),
                                    borderRadius: BorderRadius.circular(15),
                                  ),
                                  child: Text('已关注',
                                      style: TextStyle(
                                          fontSize: 14, color: Colors.grey)),
                                )
                              ],
                            ),
                          );
                        },
                      );
                    },
                    scrollDirection: Axis.horizontal,
                    onPageChanged: (index) {
                      String tag = '';
                      if (index == 0) {
                        tag = '综合';
                      } else if (index == 1) {
                        tag = '文章';
                      } else if (index == 2) {
                        tag = '标签';
                      } else if (index == 3) {
                        tag = '用户';
                      }
                      setState(() {
                        currentString = tag;
                      });
                    },
                    itemCount: 4,
                  ))
      ],
    ));
  }
}

复制代码

标签页面

同上个页面

import 'package:flutter/material.dart';
import 'package:flutter_common_app/widget/common_app_bar.dart';
import 'package:flutter_common_app/widget/common_list.dart';

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

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

class _HomeTagPageState extends State<HomeTagPage> {
  String currentString = '全部标签';
  PageController controller = new PageController();
  List dataList = ['1', '1', '1'];

  tagButton({title = '全部标签'}) {
    return FlatButton(
        onPressed: () {
          setState(() {
            currentString = title;
          });
          controller.animateToPage(title == '全部标签' ? 0 : 1,
              duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
        },
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Padding(
              padding: EdgeInsets.symmetric(vertical: 5),
              child: Text(title,
                  style: currentString == title
                      ? TextStyle(fontSize: 18, color: Colors.blue)
                      : TextStyle(fontSize: 18, color: Colors.grey)),
            ),
            Offstage(
              offstage: currentString != title,
              child: Container(
                height: 2,
                width: 80,
                decoration: BoxDecoration(color: Colors.blue),
              ),
            )
          ],
        ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: customAppbar(title: '标签管理'),
      body: Column(
        children: [
          Container(
            decoration: BoxDecoration(color: Colors.white),
            margin: EdgeInsets.only(top: 5),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                tagButton(title: '全部标签'),
                tagButton(title: '已关注标签'),
              ],
            ),
          ),
          Expanded(
              child: PageView.builder(
            controller: controller,
            itemBuilder: (context, index) {
              return CommonListWiget(
                networkApi: (currentPage) async {
                  Future.delayed(Duration(seconds: 2)).then((value) => {
                        dataList.addAll(['1', '2'])
                      });
                  return dataList;
                },
                itemBuilder: (BuildContext context, int position) {
                  return Container(
                    padding: EdgeInsets.all(10),
                    margin: EdgeInsets.only(top: 5),
                    decoration: BoxDecoration(color: Colors.white),
                    child: Row(
                      children: [
                        Container(
                          height: 50,
                          width: 50,
                          margin: EdgeInsets.only(right: 10),
                          decoration: BoxDecoration(
                              color: Colors.red,
                              borderRadius:
                                  BorderRadius.all(Radius.circular(5))),
                        ),
                        Expanded(
                            child: Container(
                          height: 50,
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                '前端',
                                style:
                                    TextStyle(fontSize: 16, color: Colors.grey),
                              ),
                              Text(
                                '50.0w 人关注·5.2w文章',
                                style:
                                    TextStyle(fontSize: 14, color: Colors.grey),
                              ),
                            ],
                          ),
                        )),
                        Container(
                          height: 30,
                          width: 70,
                          alignment: Alignment.center,
                          margin: EdgeInsets.only(left: 10),
                          decoration: BoxDecoration(
                            color: Colors.grey.withOpacity(0.3),
                            borderRadius: BorderRadius.circular(15),
                          ),
                          child: Text('已关注',
                              style:
                                  TextStyle(fontSize: 14, color: Colors.grey)),
                        )
                      ],
                    ),
                  );
                },
              );
            },
            scrollDirection: Axis.horizontal,
            onPageChanged: (index) {
              setState(() {
                currentString = index == 0 ? '全部标签' : '已关注标签';
              });
            },
            itemCount: 2,
          ))
        ],
      ),
    );
  }
}

复制代码

完成。项目纯属没事写着完呢。 欢迎指导留言谈论

one more things...

  • 掘金app沸点

文章分类
Android
文章标签