Flutter新人實戰—從0開始開發一個DIY活動記錄應用(八)統計分析功能

版權聲明:本文為本人原創文章,未經本人允許不得轉載。

好久不見,最近工作比較忙,所以更新的有點慢。
老規矩廢話少說,今天要實現的是根據之前我們添加的信息,實現首頁右下角進入統計分析頁面的功能。
還是那句話先看東西:


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/activity_record_jia.git

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容