Flutter圈子Flutter中文社区Flutter

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

2018-11-16  本文已影响25人  走路不穿鞋oO

版权声明:本文为本人原创文章,未经本人允许不得转载。

好久不见,最近工作比较忙,所以更新的有点慢。
老规矩废话少说,今天要实现的是根据之前我们添加的信息,实现首页右下角进入统计分析页面的功能。
还是那句话先看东西:


image.png

根据上图我们可以看到整体界面分为上下两部分,上面是整体统计,下面是按月分析。
所以ui布局就清楚了分两部分来做,而显示的数据那就是靠数据库查询获得。下面开撸

一、数据模型和数据库操作

在pages页面下新建diy_analysis.dart文件,我们将在这里编写整个统计分析页面的代码。
添加以下代码:
diy_analysis.dart

class monthAnalysis {
  /*
  这里是你想展示的统计的内容,我们这里自己定义了这么几个,一看就明白了,你可以按照自己想看的内容自定义。
  */
  String _month;//月份
  String _nums;//场次
  String _sumProfit;//总利润
  String _sumTotalAmount;//总流水
  String _sumUnPaidAmount;//总欠款

  monthAnalysis(this._month, this._nums, this._sumTotalAmount, this._sumProfit,
      this._sumUnPaidAmount);

  //我们所有的数据都是通过数据库查询得来,数据库返回的是map类型的数组,所以需要通过map来获得我们需要展现的数据对象实例
  monthAnalysis.fromMap(Map<String, dynamic> map) {
    _month = '${map['month']}月';
    _nums = map['nums'].toString();
    _sumTotalAmount = map['sumTotalAmount'].toString();
    _sumProfit = map['sumProfit'].toString();
    _sumUnPaidAmount = map['sumUnPaidAmount'].toString();
  }
}

然后要展示的内容我们定义了,那么我们需要去数据库文件里去查询,所以需要在数据库定义文件中添加以下代码:
data_base.dart

//按月分组查询统计项目属性内容
  Future<List> queryAll() async {
    Database database = await db;
    var result = await database.rawQuery(
        "select SUBSTR($columnDate,6,2) as month,count(*) as nums,sum($columnTotalAmount) as sumTotalAmount,sum($columnProfit) as sumProfit,sum($columnUnPaidAmount) as sumUnPaidAmount from $tableName group by SUBSTR($columnDate,6,2) order by SUBSTR($columnDate,6,12) desc");
    print('分组查询结果:$result');
    return result;
  }

  //查询总营收
  Future<int> queryTotalAmount() async {
    Database database = await db;
    var result = await database.rawQuery(
        "select sum($columnTotalAmount) as sumTotalAmount from $tableName");
    return result[0]['sumTotalAmount'];
  }

  //查询总利润
  Future<int> queryProfit() async {
    Database database = await db;
    var result = await database
        .rawQuery("select sum($columnProfit) as sumProfit from $tableName");
    return result[0]['sumProfit'];
  }

  //查询总欠款
  Future<int> queryUnPaid() async {
    Database database = await db;
    var result = await database.rawQuery(
        "select sum($columnUnPaidAmount) as UnPaidAmount from $tableName");
    return result[0]['UnPaidAmount'];
  }

以上代码如果你了解sql语法的话那肯定是没有问题的,如果您不了解,那么我建议你可以去了解下基本的增删改查sql语句,以后都会有用。
下面我们回到diy_analysis.dart,开始添加页面代码:
diy_analysis.dart

class DiyDataAnalysis extends StatefulWidget {
  DiyDataAnalysis({Key key, this.db}) : super(key: key);
  var db;

  @override
  State<StatefulWidget> createState() => new DiyDataAnalysisState();
}

class DiyDataAnalysisState extends State<DiyDataAnalysis> {
  //数据存放查询结果
  List<monthAnalysis> listItems = [];
  //初始化总金额是0
  String _totalAmount = '0';
  //初始化总利润是0
  String _profit = '0';
  //初始化总欠款是0
  String _unPaidAmount = '0';

  @override
  void initState() {
    super.initState();
    //每次页面初始化的时候进行数据查询用于展示
    _queryAll();
    _queryTotalAmount();
    _queryProfit();
    _queryUnPaidAmount();
  }

  //按月查询数据并生成月分析数据包
  _queryAll() async {
    var result = await widget.db.queryAll();
    setState(() {
      result.forEach((value) {
        listItems.add(monthAnalysis.fromMap(value));
      });
    });
  }

  //计算所有营收
  _queryTotalAmount() async {
    var result = await widget.db.queryTotalAmount();
    if (result != null) {
      setState(() {
        _totalAmount = result.toString();
      });
    }
  }

  //计算所有利润
  _queryProfit() async {
    var result = await widget.db.queryProfit();
    if (result != null) {
      setState(() {
        _profit = result.toString();
      });
    }
  }

  //计算所有欠款
  _queryUnPaidAmount() async {
    var result = await widget.db.queryUnPaid();
    if (result != null) {
      setState(() {
        _unPaidAmount = result.toString();
      });
    }
  }

  @override
  Widget build(BuildContext context) {
  }  
}

以上代码中我们主要是针对需要展示的数据内容进行定义和初始化:

1、定义数组存放按月的查询结果
2、定义总营收、总利润、总欠款并赋予初值0
3、把查询以上这些数据的方法写好

以上代码把我们需要的数据源就全部实现了,下面我们开始进行UI部分实现。

二、UI绘制

1、上半部分整体统计实现

上半部分是一个自定义的appbar,背景采用了渐变色彩过渡,里面放一个column的控件,分别展示对应的数据。
先写一个自定义appbar,添加以下代码
diy_analysis.dart

class DiyDataAnalysisState extends State<DiyDataAnalysis> {
  ......
  @override
  Widget build(BuildContext context) {
  //自定义appbar
    Widget _analysisBar() {
      return new Stack(
        children: <Widget>[
          new Container(
            height: 250.0,
            decoration: new BoxDecoration(
                gradient: new LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [Colors.blue, Color(0xff191970)])),
          ),
          new AppBar(
            backgroundColor: Colors.transparent,
            elevation: 0.0,
            actions: <Widget>[
              new FlatButton(
                child: new Text(
                  '2018年',
                  style: new TextStyle(color: Colors.white),
                ),
              )
            ],
          )
        ],
      );
    }
  }

    return new Scaffold(
        body: new Stack(
          children: <Widget>[
            _analysisBar(),
          ]
        )
    )
  }
}

以上代码表示两个控件通过stack进行叠加,第一层是一个container,设备高度和背景的颜色渐变。第二层是一个appbar,设置无阴影,包含一个现实年份的按钮,这个是为以后预留,当跨年的时候可以选择哪一年,现在我们不实现它的具体功能。
跑起来看看效果:

image.png
然后我们把要展示的数据放进入,编写总体统计控件:
diy_analysis.dart
//上半部分整体统计封装
    Widget _analysisTotal() {
      return new Container(
        margin: const EdgeInsets.only(top: 8.0),
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            new Text(
              '流水',
              style: new TextStyle(color: Colors.white),
            ),
            new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new Text(
                  _totalAmount,
                  style: Theme.of(context)
                      .textTheme
                      .display1
                      .copyWith(color: Colors.white),
                ),
                new Text('  元', style: new TextStyle(color: Colors.white)),
              ],
            ),
            new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new Row(
                  children: <Widget>[
                    new Text('利润', style: new TextStyle(color: Colors.white)),
                    new SizedBox(
                      width: 15.0,
                    ),
                    new Text(
                      _profit,
                      style: Theme.of(context)
                          .textTheme
                          .title
                          .copyWith(color: Colors.white),
                    ),
                  ],
                ),
                new SizedBox(
                  width: 30.0,
                  child: new Text(
                    '|',
                    style: new TextStyle(color: Colors.white),
                    textAlign: TextAlign.center,
                  ),
                ),
                new Row(
                  children: <Widget>[
                    new Text('未结', style: new TextStyle(color: Colors.white)),
                    new SizedBox(
                      width: 15.0,
                    ),
                    new Text(
                      _unPaidAmount,
                      style: Theme.of(context)
                          .textTheme
                          .title
                          .copyWith(color: Colors.white),
                    ),
                  ],
                ),
              ],
            ),
          ],
        ),
      );
    }

以上代码控件应该大家都不陌生了吧,都是简单的控件组合,包括column、row、sizedbox以及他们的属性编辑,如果你对这些还不是很熟悉的话可以回顾下之前我对基础控件文章的介绍。
然后把上面的控件放到自定义appbar里面,如下:

//自定义appbar
    Widget _analysisBar() {
      return new Stack(
        children: <Widget>[
          new Container(
            height: 250.0,
            decoration: new BoxDecoration(
                gradient: new LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [Colors.blue, Color(0xff191970)])),
            child: _analysisTotal(),//添加这一行
          ),

热重载一下看看效果:


image.png

这样上半部分总体统计基本就做好了,当然具体摆放什么的见仁见智,你们可以按照自己想法来实现。
下面我们实现底下按月统计的部分

2、下半部分按月统计实现

首先添加下半部分的卡片,修改scaffold里面内容:

return new Scaffold(
      body: new Stack(
        children: <Widget>[
          _analysisBar(),
          new Padding(
            padding: const EdgeInsets.only(top: 210.0),
            child: new Card(
              margin: const EdgeInsets.all(8.0),
              child: new Column(
                children: <Widget>[
                  new Container(
                    padding: const EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 4.0),
                  ),
                ],
              ),
            ),
          )
        ],
      ),
    );

以上代码是在自定义appbar下面添加一个距离顶部210高度的卡片,我们将在这个卡片里填入按月统计的数据,运行下效果如下:


image.png

然后我们继续添加以下代码:

//sizedbox封装
    Widget _sizedBox(String text) {
      return new SizedBox(
          width: 45.0,
          child: new Text(
            text,
            textAlign: TextAlign.center,
          ));
    }

    //月统计区域的每一行封装
    Widget _containerShow(monthAnalysis monthAnalysis) {
      return new Container(
        padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0),
        decoration: new BoxDecoration(
            border: Border(bottom: new BorderSide(color: Colors.grey[200]))),
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            _sizedBox(monthAnalysis._month),
            _sizedBox(monthAnalysis._nums),
            _sizedBox(monthAnalysis._sumTotalAmount),
            _sizedBox(monthAnalysis._sumProfit),
            _sizedBox(monthAnalysis._sumUnPaidAmount),
          ],
        ),
      );
    }

以上代码分为两部分:sizedbox是每个最小内容区域的封装
_containerShow是每一行展示内容封装,内容不多,控件也不复杂,相信大家一看就会明白。

下面我们回到scaffold主体代码部分,把这些内容放进去:

return new Scaffold(
      body: new Stack(
        children: <Widget>[
          _analysisBar(),
          new Padding(
            padding: const EdgeInsets.only(top: 210.0),
            child: new Card(
              margin: const EdgeInsets.all(8.0),
              child: new Column(
                children: <Widget>[
                  new Container(
                    padding: const EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 4.0),
                    decoration: new BoxDecoration(
                        border: new Border(
                            bottom: new BorderSide(color: Colors.grey))),
                    child: new Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: <Widget>[
                        _sizedBox('月份'),
                        _sizedBox('场次'),
                        _sizedBox('营收'),
                        _sizedBox('利润'),
                        _sizedBox('未结'),
                      ],
                    ),
                  ),
                  new Flexible(
                    child: new ListView(
                      padding: EdgeInsets.zero,//这一行一定要添加,不然会与标题栏有一段空白。
                      children: listItems.map((listItem) {
                        return _containerShow(listItem);
                      }).toList(),
                    ),
                  )
                ],
              ),
            ),
          )
        ],
      ),
    );

以上代码我们首先在顶部添加显示数据的标题,包括月份、场次、营收等等。
下面是一个ListView,包含统计出来的每个月的数据信息。
运行后效果如下:


image.png

到这基本就实现了我们想要的统计分析功能了,大家在这个基础上可以根据自己的需要和喜好进行修改。

最后

flutter关于这种类似表格显示的有着自己的控件,我这个可能是自己通过其他控件来进行实现展示的,下次我会用flutter的表格控件来尝试下。你们自己也可以试试。

最后的最后

到这第八篇文章结束,整个项目的主体工程其实已经差不多算结束了,当初设计的主要功能也都一一实现,当然这还不代表就此结束,还有很多细节和功能可以完善,比如增加过渡动画,添加ios、android的双平台ui适配等等,还包括女王大人随时提出的新需求。所以后面我会持续更新这个项目。

如果你们觉得这些内容对你有帮助的话那就给我一个赞吧!!!

最后附上项目源码地址:https://gitee.com/xusujun33/ActivityRecord

上一篇下一篇

猜你喜欢

热点阅读