Flutter ——发现页面和我的页面搭建

952

这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战

上文中进行了基础页面的搭建,并且创建了底部导航栏,那么今天就开始搭建发现页面和我的页面。 先来到上次的rootpage中将_currentIndex改为2,这样默认选择的就是发现页面,方便进行页面搭建。 要搭建页面,心里就要大概思考要用什么部件来搭建这个页面。

1.发现页面

那么看到这个界面,看到有AppBar,那么就想到要用scaffold,然后在body用container包着row来实现。

在这里插入图片描述

这里将AppBar的Title 颜色改成黑色,然后背景色改为灰色,并且将底部阴影的大小改为0,并且为安卓设置centerTitle为true,这样切出去的时候标题就会在中间, 然后创建一个container。

class _DiscoverPageState extends State<DiscoverPage> {
  Color _themColor = Color.fromRGBO(220, 220, 220, 1.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('发现页面',style: TextStyle(color: Colors.black),),
        backgroundColor: _themColor,
        elevation: 0,
        centerTitle: true,
      ),
      body:  Container(
        height: 800,
        color: _themColor,
      ),
    );
  }
}

接下来创建discover cell。日常开发中,都是先使用StatelessWidget,当后面需要保留状态时才修改成StatefulWidget。所以这里先用StatelessWidget来创建DiscoverCell,然后创建四个需要传进来的参数,创建构造方法,其中title和imageName必须要有所以是required。这里页面的话思考使用Row,并且在Row里面也使用2个Row,一个左边一个右边 ,所以这里用到spacebetween。

class DiscoverCell extends StatelessWidget {
  final String? title;
  final String? imageName;
  final String? subTitle;
  final String? subImageName;

  DiscoverCell({required this.title, required this.imageName, this.subTitle, this.subImageName}): assert(title != null,"title 不能为空"),assert(imageName != null,"imageName 不能为空");

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 55,
      color: Colors.white,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          // left
          Container(),
          // right
          Container(),
        ],
      ),
    );
  }
}

这里添加所需要的部件,并且给给container添加内边距padding。

Container(
            padding: EdgeInsets.all(10),
            child: Row(
              children: [
                //image
                Image(
                  image: AssetImage(imageName!),
                  width: 20,
                ),
                const SizedBox(
                  width: 15,
                ),
                //title
                Text(title!),
              ],
            ),
          ),
          // right
          Container(
              padding: EdgeInsets.all(10),
              child: Row(
                children: [
                  // subtitle
                  Text(subTitle ?? ""),
                  // subimage
                  Image(
                    image: AssetImage(subImageName ?? ""),
                    width: 20,
                  ),
                  //箭头
                  Image(
                    image: AssetImage('images/icon_right.png'),
                    width: 15,
                  ),
                ],
              )),
        ],
      ),
    );

DiscoverCell创建完,就要构建discover page。在之前的scaffold的body的container中添加child:ListView,然后添加上需要的Cell,用SizedBox创建间距。这里使用Row里面包含两个Container的方法来实现分割线前部分白色,后部分灰色的效果。

child: ListView(
          children: <Widget>[
            DiscoverCell(
              imageName: 'images/朋友圈.png',
              title: '朋友圈',
            ),
            const SizedBox(
              height: 10,
            ),
            DiscoverCell(
              imageName: 'images/扫一扫2.png',
              title: '扫一扫',
            ),
            Row(
              children: <Widget>[
                Container(width: 50, height: 0.5, color: Colors.white),
                Container(height: 0.5, color: Colors.grey)
              ],
            ),
            DiscoverCell(
              imageName: 'images/摇一摇.png',
              title: '摇一摇',
            ),
            SizedBox(
              height: 10,
            ),
            DiscoverCell(
              imageName: 'images/看一看icon.png',
              title: '看一看',
            ),
            Row(
              children: <Widget>[
                Container(width: 50, height: 0.5, color: Colors.white),
                Container(height: 0.5, color: Colors.grey)
              ],
            ),
            DiscoverCell(
              imageName: 'images/搜一搜 2.png',
              title: '搜一搜',
            ),
            SizedBox(
              height: 10,
            ),
            DiscoverCell(
              imageName: 'images/附近的人icon.png',
              title: '附近的人',
            ),
            SizedBox(
              height: 10,
            ),
            DiscoverCell(
              imageName: 'images/购物.png',
              title: '购物',
              subTitle: '618限时特价',
              subImageName: 'images/badge.png',
            ),
            Row(
              children: <Widget>[
                Container(width: 50, height: 0.5, color: Colors.white),
                Container(height: 0.5, color: Colors.grey)
              ],
            ),
            DiscoverCell(
              imageName: 'images/游戏.png',
              title: '游戏',
            ),
            SizedBox(
              height: 10,
            ),
            DiscoverCell(
              imageName: 'images/小程序.png',
              title: '小程序',
            ),
          ],
        ),
        ),

接下来要点击cell之后跳转页面。要响应点击是在,在DiscoverCell外面套一层GestureDetector。GestureDetector是一个用于手势识别的功能性组件,我们通过它可以来识别各种手势。

在这里插入图片描述

在这里插入图片描述

创建一个跳转页面:

class DiscoverChildPage extends StatelessWidget {
  final String title;
  DiscoverChildPage({required this.title});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("$title"),
      ),
      body: Center(
        child: Text("$title"),
      ),
    );
  }
}

然后在点击的时候添加跳转方法。

  onTap: () {
          Navigator.of(context).push(MaterialPageRoute(
              builder: (BuildContext context) =>
                  DiscoverChildPage(title: '$title')));
          print('$title');
        },

接下来给cell添加一个点击变灰色的效果,那么就说明,cell要变成有状态的,那么DiscoverCell就要改成StatefulWidget。整个cell有状态,并不代表渲染是整体渲染。Widget是界面的描述,不是界面,将整个cell改成有状态的,界面的描述会被重新创建,而不是界面。如果为了追求性能,可以把container抽取出来做一个StatefulWidget。这里为了方便直接修改整个DiscoverCell为StatefulWidget。

class DiscoverCell extends StatefulWidget {

  final String? title;
  final String? imageName;
  final String? subTitle;
  final String? subImageName;

  DiscoverCell(
      {required this.title,
        required this.imageName,
        this.subTitle,
        this.subImageName})
      : assert(title != null, "title 不能为空"),
        assert(imageName != null, "imageName 不能为空");

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

class _DiscoverCellState extends State<DiscoverCell> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: () {
          Navigator.of(context).push(MaterialPageRoute(
              builder: (BuildContext context) =>
                  DiscoverChildPage(title: '$widget.title')));
          print('$widget.title');
        },
        onTapCancel: () {},
        onTapDown: (TapDownDetails details) {},
        child: Container(
          height: 55,
          color: Colors.white,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              // left
              Container(
                padding: EdgeInsets.all(10),
                child: Row(
                  children: [
                    //image
                    Image(
                      image: AssetImage(widget.imageName!),
                      width: 20,
                    ),
                    const SizedBox(
                      width: 15,
                    ),
                    //title
                    Text(widget.title!),
                  ],
                ),
              ),
              // right
              Container(
                  padding: EdgeInsets.all(10),
                  child: Row(
                    children: [
                      // subtitle
                      Text(widget.subTitle ?? ""),
                      // subimage
                      widget.subImageName != null
                          ? Image(
                        image: AssetImage(widget.subImageName!),
                        width: 12,
                      )
                          : Container(),
                      //箭头
                      Image(
                        image: AssetImage('images/icon_right.png'),
                        width: 15,
                      ),
                    ],
                  )),
            ],
          ),
        ));
  }
}

添加一个_currentColor,然后在onTapDown里面修改_currentColor为灰色,onTap里面修改为白色,_currentColor用来作为Container的颜色。

在这里插入图片描述

2. 我的页面

我的页面和发现页面实际上差不多,就是头部不一样了,没有了Appbar并且多了一个相机。 在这里插入图片描述 要创建我的页面,先将_currentIndex改为3方便创建。因为这里的相机是悬浮的,所以这里使用stack。这里简单的创建一个Positioned来放相机,然后cell用之前DiscoverCell,之后就可以专心做头部的界面了,这里创建headerWidget来专门做头部界面。这里Positioned放在后面因为相机需要在上层。

import 'package:flutter/material.dart';

import 'discover/discover_cell.dart';

class MinePage extends StatefulWidget {
  const MinePage({Key? key}) : super(key: key);

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

class _MinePageState extends State<MinePage> {
  Widget headerWidget() {
    return Container();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Stack(
          children: [
            Container(
              child: ListView(
                children: [
                  headerWidget(),
                  SizedBox(
                    height: 10,
                  ),
                  DiscoverCell(
                    imageName: 'images/微信 支付.png',
                    title: '支付',
                  ),
                  SizedBox(
                    height: 10,
                  ),
                  DiscoverCell(
                    imageName: 'images/微信收藏.png',
                    title: '收藏',
                  ),
                  Row(
                    children: <Widget>[
                      Container(width: 50, height: 0.5, color: Colors.white),
                      Container(height: 0.5, color: Colors.grey)
                    ],
                  ), //分割线
                  DiscoverCell(
                    imageName: 'images/微信相册.png',
                    title: '相册',
                  ),
                  Row(
                    children: <Widget>[
                      Container(width: 50, height: 0.5, color: Colors.white),
                      Container(height: 0.5, color: Colors.grey)
                    ],
                  ), //分割线
                  DiscoverCell(
                    imageName: 'images/微信卡包.png',
                    title: '卡包',
                  ),
                  Row(
                    children: <Widget>[
                      Container(width: 50, height: 0.5, color: Colors.white),
                      Container(height: 0.5, color: Colors.grey),
                    ],
                  ),
                  DiscoverCell(
                    imageName: 'images/微信表情.png',
                    title: '表情',
                  ),
                  SizedBox(
                    height: 10,
                  ),
                  DiscoverCell(
                    imageName: 'images/微信设置.png',
                    title: '设置',
                  ),
                ],
              ),
            ),
            Positioned(
              child: Image.asset(
                "images/相机.png",
                width: 25,
              ),
              right: 10,
              top: 40,
            ),
          ],
        ),
      ),
    );
  }
}

给headerWidget里面的Container高度以及颜色,就可以在屏幕上看到了

 Widget headerWidget() {
    return Container(
      height: 300,
      color: Colors.red,
    );
  }

这里看到Container没有在顶部,这是Flutter为刘海屏设置的一个Padding。

在这里插入图片描述

那么要去掉这个padding,就需要使用MediaQuery.removePadding,这里有removeTop,removeBottom,removeLeft,removeRight四个选项。

在这里插入图片描述

这样padding就没了。

在这里插入图片描述

回到headerWidget,在container里面添加一个Container,并且为其添加margin,这样这个Container就会在外面container的下部分。

  return Container(
      height: 200,
      color: Colors.white,
      child: Container(
        color: Colors.red,
        margin: EdgeInsets.only(top: 100,bottom: 20,left: 10,right: 10),
      ),
    );

在这里插入图片描述

在里面的container里面child添加row,然后先添加头像。这里使用ClipRRect来给图片添加圆角。

child: Row(
          children: [
            //头像
            Container(
              child: ClipRRect(
                child: Image(image: AssetImage('images/Hank.png')),
                borderRadius: BorderRadius.circular(5.0),
              ),
            ),
            //右边的部分
          ],
        ),

接下来右边的部分用Stack + Positioned就可以啦。

 Container(
              margin: EdgeInsets.only(left: 10),
              width: MediaQuery.of(context).size.width - 20 - 90,
              height: 100,
              child: Stack(
                children: [
                  Positioned(
                    child: Text("Hank",
                      style: TextStyle(
                        fontSize: 28,
                        fontWeight: FontWeight.bold,
                      ),),
                    left: 0,
                    top: 0,

                  ),
                  Positioned(
                    child: Text(
                      "微信号:1234",
                      style: TextStyle(
                        color: Color.fromRGBO(144, 144, 144, 1.0),
                      ),
                    ),
                    left: 0,
                    bottom: 0,
                  ),
                  Positioned(
                    child:  Image(
                      image: AssetImage('images/icon_right.png'),
                      width: 15,
                    ),
                    right: 0,
                    bottom: 0,
                  ),
                ],
              ),
            ),