美文网首页Flutter圈子Flutter中文社区Flutter
Flutter新人实战—从0开始开发一个DIY活动记录应用(三)

Flutter新人实战—从0开始开发一个DIY活动记录应用(三)

作者: 走路不穿鞋oO | 来源:发表于2018-11-03 22:44 被阅读5次

    上篇文章我们基本介绍了如何分析布局一个应用的整体框架,以及介绍了不带数据传递的不同页面之间的导航。
    今天我们开始对页面要展示的内容进行实现,很显然今天涉及的就是Flutter的各类控件了。

    数据模型建立

    在界面ui之前呢,我们先把活动项目的数据属性先建立起来,在后面的内容我们会用到。
    在lib目录下新建文件夹model,用于存放数据模型,新建diy_project.dart文件:

    image.png
    完成数据属性的说明:
    diy_project.dart
    /*
    diy项目类,说明diy项目的对象属性
    */
    
    class DiyProject {
      int _id; //项目id
      String _name; //项目名字
      String _date; //项目时间
      String _place; //项目地点
      String _contact; //项目联系人
      String _imagePath; //项目照片地址
    
      int _singlePrice; //项目单价
      int _nums; //项目份数
      int _totalAmount; //项目总价
      int _itemCost; //物料成本
      int _laborCost; //人员成本
      int _profit; //项目利润
    
      bool _isCheckOut; //钱款是否结清
    
      //活动项目构造函数
      DiyProject(
        this._name,
        this._date,
        this._place,
        this._contact,
        this._imagePath,
        this._singlePrice,
        this._nums,
        this._totalAmount,
        this._itemCost,
        this._laborCost,
        this._profit,
        this._isCheckOut,
      );
    
      /*
      下面是获取活动项目各属性值得方法
      */
      String get name => _name;
      String get date => _date;
      String get place => _place;
      String get contact => _contact;
      String get imagePath => _imagePath;
    
      int get id => _id;
      int get singlePrcie => _singlePrice;
      int get nums => _nums;
      int get totalAmount => _totalAmount;
      int get itemCost => _itemCost;
      int get laborCost => _laborCost;
      int get profit => _profit;
      bool get isCheckOut => _isCheckOut;
    }
    

    然后我们在home_page文件中初始化一组项目数据,用于后面的数据展示,代码如下:
    home_page.dart

    //初始化三个项目数据
      List<DiyProject> _diyProjects = [
        new DiyProject('多肉种植', '2018-11-2', '万达广场', '苏苏', 'images/4.jpg', 30, 50,
            1500, 500, 300, 700, false),
        new DiyProject('彩绘尤克里里', '2018-10-22', '寰宇城', '盼盼', 'images/2.jpg', 20, 30,
            600, 500, 500, 1500, false),
        new DiyProject('小饼干制作', '2018-9-15', '滨江新城', '磊磊', 'images/5.jpg', 40, 50,
            2000, 600, 200, 800, false),
      ];
    

    UI绘制

    数据模型属性我们已经准备完毕,下面我们开始一步步实现首页的展示效果
    我们要实现的首页展示效果如下:


    首页.PNG

    简单分析以后我们可以看得出首先这是一个可以滚动的ListView,里面包含的是一个个的Card,Card里对应的是项目活动的属性(展示照片、名称、时间、地点等等)。这些属性又是按照一定的方向进行排列的,其实很多教程和文章里都有说到如何分析一个页面的布局,那要点就是一个字:拆

    我们对这个card进行拆解:

    1、card里是一个Colum的垂直排列
    2、从上往下分别是图片、名称日期组合、联系人地点组合、金额
    3、其中名称日期和联系人地点是一个Row控件实现的横向排列

    下面我们边撸码边看效果:
    首先为了整体代码文件的清晰整洁,我们在lib下新建ui文件夹,在ui文件夹里新建diy_list_show.dart

    image.png
    然后在文件中根据上面拆解的结构绘制ui控件:
    diy_list_show.dart
    import 'package:activity_record/model/diy_project.dart';
    import 'package:flutter/material.dart';
    
    class DiyListShow extends StatefulWidget {
      //将项目对象作为参数配置给DiyListShow的构造函数
      DiyListShow({Key key, this.diyItem}) : super(key: key);
      DiyProject diyItem;
      @override
      State<StatefulWidget> createState() => new DiyListShowState();
    }
    
    class DiyListShowState extends State<DiyListShow> {
      @override
      Widget build(BuildContext context) {
        //card第一行时间和预留按钮菜单
        Widget _rowTime() {
          return new Container(
            //设置距离左边8.0的内间距
            padding: const EdgeInsets.only(left: 8.0),
            child: new Row(
              //表示两个子空间头尾分布
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                new Text(widget.diyItem.date),
                new IconButton(
                  icon: new Icon(Icons.more_horiz),
                  onPressed: () {},
                )
              ],
            ),
          );
        }
    
        //card第三行名称和地点
        Widget _rowNameAndPlace() {
          return new Container(
            padding: const EdgeInsets.all(8.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                new Text(widget.diyItem.name,
                    style:
                        new TextStyle(fontSize: 17.0, fontWeight: FontWeight.bold)),
                new Text(widget.diyItem.place),
              ],
            ),
          );
        }
    
        //进行card里的内容组合
        Widget _diyContentShow() {
          return Container(
            height: 288.0,
            child: new Column(
              children: <Widget>[
                _rowTime(),
                //使用expanded将填充控件的剩余空间
                new Expanded(
                    //flex代表这个控件在父控件里的范围比例,默认是1,这里表示在高度288的容器里,图片会填满剩余的所有空间
                    flex: 3,
                    child: new Image.asset(
                      widget.diyItem.imagePath,
                      fit: BoxFit.cover,
                      width: 400.0,
                    )),
                _rowNameAndPlace(),
                new Padding(
                  padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 8.0),
                  child: new Row(
                    children: <Widget>[
                      new Text(
                        '${widget.diyItem.singlePrcie.toString()}元',
                        style: new TextStyle(
                            fontSize: 15.0,
                            color: Theme.of(context).primaryColor,
                            fontWeight: FontWeight.bold),
                      ),
                      new SizedBox(
                        width: 20.0,
                      ),
                      new Text(
                        '${widget.diyItem.nums.toString()}份',
                        style: new TextStyle(
                            fontSize: 15.0,
                            color: Theme.of(context).primaryColor,
                            fontWeight: FontWeight.bold),
                      )
                    ],
                  ),
                )
              ],
            ),
          );
        }
    
        //将整个项目展示内容包裹在card里
        return new Card(
          margin: const EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 9.0), //设置外边距18
          //card形状设置顶部圆形弧度12,底部没有
          shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.vertical(top: Radius.circular(12.0))),
          //inkwell是一个带水波纹触摸效果的控件,预留点击回调作为以后点击响应事件
          child: new InkWell(
            onTap: () {},
            child: _diyContentShow(),
          ),
        );
      }
    }
    

    里面的主要知识点我进行了注释,细心的朋友可能发现了一个知识点——image.asset,下面介绍使用方法:

    1. 首先需要在lib同级新建图片文件夹images


      image.png
    2. 将想使用的图片拷贝到目录下
    3. pubsec.yaml文件里配置图片的位置引用
      引用方法如下:
      image.png

    这样我们就能使用image.asset('图片地址')展示图片了。
    Card的UI我们写完了,下面我们回到home_page.dart,修改body代码看看card得显示效果如何:
    home_page.dart

    body: new ListView.builder(
            itemCount: 3,
            itemBuilder: (context, index) {
              return new DiyListShow(
                diyItem: _diyProjects[index],
              );
            },
          ),
    

    写完后可能发现DiyListShow()下面有警告,因为我们还没引用这个dart文件,所以在开头我们导入文件:
    import 'package:activity_record/ui/diy_list_show.dart';包括以后在引用或使用其他dart文件里的方法内容的时候都要记得先导入需要引用的文件。
    以上代码中ListView是列表滚动控件,他有两个构造函数,分别是ListView()和ListView.builder()
    其中ListView()适合内容不多的情况使用,因为flutter会全部渲染完成后展现,如果是数据量很多或者无限数据的话就要使用ListView.builder(),这个构造方法是根据用户滚动的情况,实时渲染需要展现的数据,当滑出屏幕后就会回收对应的数据。

    写了这么多,是时候看下效果了,UI界面的调整都是边看边改的,对于新人我们不可能一步到位直接写好,边写边热重载边修改非常关键,好了运行后效果如下:


    image.png
    image.png

    感觉是不是还可以,哈哈 自恋下。可能有人觉得奇怪,这和上面开头的图的布局不一样啊,这是因为女王大人给我提了新的需求,她对之前的效果提出了修改意见,要知道用户的需求才是关键嘛,毕竟我们做完是给他们用的,所以我做了调整和之前的不一样了,不过只要你掌握了方法,无论怎么改我相信你都可以做出来的啦。

    最后总结

    今天我们主要介绍了flutter里的基础控件,虽然用到的不多,但是都是非常基础和常用的,包括listview、container、row、column、sizedbox、card、text、iconbutton、expanded、padding等等,每个控件里都有很多属性,对于新人来说不要记得那么多,主要记住每个控件常用的属性就可以啦,其他的后面再慢慢学习就好了。
    试试看在每个内容前加上小的示意图标(比如在时间前加上代表时间的icon),美化整个card效果,动手试试吧!或者你也可以根据自己想要的样子设计一个展示UI,然后自己实现一下吧。

    相关文章

      网友评论

        本文标题:Flutter新人实战—从0开始开发一个DIY活动记录应用(三)

        本文链接:https://www.haomeiwen.com/subject/jqzkxqtx.html