Flutter 1.12 個人練手學習小案例

洋小洋同學發表於2019-12-12

1. 前言

由於在實際的開發過程中會有遇到各式各樣的效果,會把一些常見的介面佈局與部件使用統一放在這個倉庫下,如果有遇到和你需求一致的直接拿走即可,感覺有幫助,star一下更好,不迷路。

  • 倉庫地址:github.com/yayxs/flutt…
  • star數:3(截至發稿)
  • 特別說明:有些效果是藉助於大佬的開原始碼
  • 閱讀預警:有表有圖有程式碼

2. 倉庫資料夾

由於是每個小案例新建的一個flutter專案,所以克隆下倉庫cd到資料夾flutter run即可。部分說明如下

資料夾 外掛 / 技術點 /第三方包 其他說明
cached_network_image_demo cached_network_image: ^1.1.3 這個主要用來驗證網路圖片批量載入的快取效果
color_decoration_test 佈局的修飾 這個主要是驗證修飾的時候colordecoration: BoxDecoration()不能夠同時使用
custom_scroll_view_demo flutter內建api 這個主要是滾動效果小案例
dismissible_demo flutter內建api 主要是測試滑動刪除的效果
fl_chart_scatter_demo fl_chart 主要是圖表相關
flutter_bar_chart_demo 圖表 這個主要是bar統計圖,我在上幾個專欄也提到過
flutter_dio_demo dio 網路請求庫簡單試水(沒有參考價值)
flutter_radar_chart_demo 圖表 這個是雷達圖效果的案例
flutter_select_demo direct_select_flutter: ^1.0.5 這個是選取效果
flutter_state_demo 狀態管理 我有提到要書寫一篇專欄分享一下我對狀態管理的理解,由於週六加班鴿了
json_to_model_demo json_to_dart 這個是AD一個json轉模型的測試
loading_demo loading: ^1.0.2 這個是loading載入的效果,就是網路請求的時候
long_press_delete_demo 點選 這個主要是使用者長按某塊進行刪除
new_provider_demo provider 這個是provider新版本更新過後廢棄掉builder之後的demo
ok_toast_demo ok_toast 這個主要是toast案例
search_demo flutter 內建api 這個主要是搜尋框
select_demo 選取 這個主要是按住拖動選擇
selection_callback_example_bar 這個主要是圖表可以點選的回撥測試
sliver_demo flutter sliver 這個主要是滾動大家族頂樑柱
spider_chart_demo 圖表 像個蜘蛛網一樣
url_launcher_demo url_launcher 更新頻次比較快的一個跳轉其他app的庫

3. 演示效果

1.dismissible_demo

這個有必要分享一下,也是參考了一位大佬的封裝,是類似於微信的左滑刪除等其他操作

  • 案例演示

Flutter 1.12 個人練手學習小案例

  • 核心程式碼
  Widget _createListView() {
   return ListView.builder(
     itemCount: _listData.length,
     itemBuilder: (context, index) {
       return Dismissible(
         // Key
         key: Key('key${_listData[index]}'),
         // Child
         child: ListTile(
           title: Text('${_listData[index]}'),
         ),
         onDismissed: (direction) {
           var _snackStr;
           if (direction == DismissDirection.endToStart) {
             // 從右向左  也就是刪除
             _snackStr = '刪除了${_listData[index]}';
           } else if (direction == DismissDirection.startToEnd) {
             _snackStr = '收藏了${_listData[index]}';
           }

           // 展示 SnackBar
           Scaffold.of(context).showSnackBar(SnackBar(
             content: Text(_snackStr),
             duration: Duration(milliseconds: 400),
           ));

           // 刪除後重新整理列表,以達到真正的刪除
           setState(() {
             _listData.removeAt(index);
           });
         },
         background: Container(
           color: Colors.green,
           // 這裡使用 ListTile 因為可以快速設定左右兩端的Icon
           child: ListTile(
             leading: Icon(
               Icons.bookmark,
               color: Colors.white,
             ),
           ),
         ),

         secondaryBackground: Container(
           color: Colors.red,
           // 這裡使用 ListTile 因為可以快速設定左右兩端的Icon
           child: ListTile(
             trailing: Icon(
               Icons.delete,
               color: Colors.white,
             ),
           ),
         ),

         confirmDismiss: (direction) async {
           var _confirmContent;

           var _alertDialog;

           if (direction == DismissDirection.endToStart) {
             // 從右向左  也就是刪除
             _confirmContent = '確認刪除${_listData[index]}?';
             _alertDialog = _createDialog(
               _confirmContent,
               () {
                 // 展示 SnackBar
                 Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text('確認刪除${_listData[index]}'),
                   duration: Duration(milliseconds: 400),
                 ));
                 Navigator.of(context).pop(true);
               },
               () {
                 // 展示 SnackBar
                 Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text('不刪除${_listData[index]}'),
                   duration: Duration(milliseconds: 400),
                 ));
                 Navigator.of(context).pop(false);
               },
             );
           } else if (direction == DismissDirection.startToEnd) {
             _confirmContent = '確認收藏${_listData[index]}?';
             _alertDialog = _createDialog(
               _confirmContent,
               () {
                 // 展示 SnackBar
                 Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text('確認收藏${_listData[index]}'),
                   duration: Duration(milliseconds: 400),
                 ));
                 Navigator.of(context).pop(true);
               },
               () {
                 // 展示 SnackBar
                 Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text('不收藏${_listData[index]}'),
                   duration: Duration(milliseconds: 400),
                 ));
                 Navigator.of(context).pop(false);
               },
             );
           }

           var isDismiss = await showDialog(
               context: context,
               builder: (context) {
                 return _alertDialog;
               });
           return isDismiss;
         },
       );
     },
   );
 }

複製程式碼

2.long_press_delete_demo

這個效果是長按的時候進行刪除,有個小彈窗

  • 專案演示

  • 121210.gif

  • 核心程式碼

class PlusMinusEntry extends PopupMenuEntry<int> {
 @override
 final double height = 100;

 @override
 bool represents(int n) => n == 1 || n == -1;

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

class PlusMinusEntryState extends State<PlusMinusEntry> {
 void _plus1() {
   // 這是關閉彈出選單並返回使用者選擇的方式。 1 刪除 -1 取消
   Navigator.pop<int>(context, 1);
 }

 void _minus1() {
   Navigator.pop<int>(context, -1);
 }

 @override
 Widget build(BuildContext context) {
   return Opacity(
     child: Container(
         color: Color.fromRGBO(0, 0, 0, 0.4),
         child: Row(
           children: <Widget>[
             Expanded(child: FlatButton(onPressed: _plus1, child: Text('刪除'))),
             Expanded(
                 child: FlatButton(onPressed: _minus1, child: Text('取消'))),
           ],
         )),
     opacity: 0.3,
   );
 }
}

複製程式碼

3.new_provider_demo

這個是 provider 廢棄掉 builder 之後的用法

  • 核心用法
providers: [
       ChangeNotifierProvider(create: (_) => Counter()),
     ],
複製程式碼

4.ok_toast_demo

這個是一個 toast,提醒時候用

  • 專案演示
    121208.gif

5.scatter_plot_chart

這個主要是畫點(沒什麼內容)

  • 效果演示
    121207.png
  • 核心程式碼
class MyPainter extends CustomPainter {
 @override
 void paint(Canvas canvas, Size size) {
   var paint = Paint();

   _myDrawCircle(double offsetX, double offsetY, double radius, Paint paint) {
     canvas.drawCircle(
       Offset(offsetX, offsetY),
       radius,
       paint,
     );
   }

   for (int i = 0; i <= listData.length; ++i) {
     paint
       ..style = PaintingStyle.fill
       ..color = Colors.lightBlue;
     if (i == 0) {
       _myDrawCircle(50.0, 50.0, 10.0, paint);
     } else {
       _myDrawCircle(50.0 + 50 * i, 50, 10.0, paint);
     }
   }
 }

 //在實際場景中正確利用此回撥可以避免重繪開銷
 @override
 bool shouldRepaint(CustomPainter oldDelegate) => true;
}

複製程式碼

6.search_demo

這個主要是搜尋的效果,並不是在 appBar 上

  • 演示效果
    121206.gif
  • 核心程式碼
 Widget buildTextField() => TextField(
        cursorColor: Colors.black, // 游標顏色
        // 預設設定
        decoration: InputDecoration(
            contentPadding: const EdgeInsets.symmetric(vertical: 10.0),
            border: InputBorder.none,
            icon: Icon(Icons.search),
            hintText: "教師姓名",
            hintStyle: new TextStyle(
                fontSize: 14, color: Color.fromARGB(50, 0, 0, 0))),
        style: new TextStyle(fontSize: 14, color: Colors.black),
      );
複製程式碼

7.select_demo

這個主要是選擇,滑動進行選擇的效果

  • 演示效果
    121205.gif
  • 核心程式碼
   body: DirectSelectContainer(
        child: SingleChildScrollView(
          child: Container(
            width: 150,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              verticalDirection: VerticalDirection.down,
              children: <Widget>[
                Column(
                  children: <Widget>[
                    Row(children: <Widget>[
                      Expanded(
                          child: Container(
                        decoration: _getShadowDecoration(),
                        child: Card(
                            child: Row(
                          mainAxisSize: MainAxisSize.max,
                          children: <Widget>[
                            Expanded(
                                child: Padding(
                              padding: EdgeInsets.only(left: 22),
                              child: DirectSelectList<String>(
                                  values: _dataList,
                                  defaultItemIndex: selectedIndex,
                                  itemBuilder: (String value) =>
                                      getDropDownMenuItem(value),
                                  focusedItemDecoration: _getDslDecoration(),
                                  onUserTappedListener: () {
                                    _showScaffold();
                                  },
                                  // 選中之後的回撥
                                  onItemSelectedListener:
                                      (item, index, context) {
                                    setState(() {
                                      selectedIndex = index;
                                    });
                                  }),
                            )),
                            Padding(
                              padding: EdgeInsets.only(right: 8),
                              child: _getDropdownIcon(),
                            )
                          ],
                        )),
                      )),
                    ]),
                  ],
                ),
              ],
            ),
          ),
        ),
複製程式碼

8.selection_callback_example_bar

這個主要是條形統計圖

  • 效果演示
    121204.png
  • 核心程式碼
 child: Stack(
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
                border: Border.all(width: 1, color: Colors.blueAccent)),
            width: double.infinity,
            height: ScreenUtil.getInstance().setHeight(500),
            child: charts.BarChart(
              //通過下面獲取資料傳入
              ChartFlutterBean.createSampleData(),
              //配置項,以及設定觸發的函式
              selectionModels: [
                charts.SelectionModelConfig(
                  type: charts.SelectionModelType.info,
                  changedListener: _onSelectionChanged,
                )
              ],
            ),
          ),
          _showMask(flag, 4.0)
        ],
複製程式碼

9.sliver_demo

這個主要是滑動效果,Sliver,這個滑動效果,比 ta 其他的兄弟家族要順滑很多

  • 沒有使用第三方包
  • 演示效果
    121203.gif
  • 核心程式碼
SliverPadding(
          padding: const EdgeInsets.all(8.0),
          sliver: new SliverGrid(
            //Grid
            gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 3, //Grid按兩列顯示
              mainAxisSpacing: 10.0,
              crossAxisSpacing: 10.0,
              childAspectRatio: 4.0,
            ),
            delegate: new SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                //建立子widget
                return Container(
                    padding: EdgeInsets.all(5),
                    width: 200,
                    height: 200,
                    child: Image.network('https://yayxs.github.io/head.jpg'));
              },
              childCount: 27,
            ),
          ),
        ),
        //List
        new SliverFixedExtentList(
          itemExtent: 50.0,
          delegate:
              new SliverChildBuilderDelegate((BuildContext context, int index) {
            //建立列表項
            return new Container(
              alignment: Alignment.center,
              color: Colors.lightBlue[100 * (index % 9)],
              child: new Text('list item $index'),
            );
          }, childCount: 50 //50個列表項
                  ),
        ),
複製程式碼

10.spider_chart_demo

這個主要是圖表相關的,類似於一個蜘蛛一樣的雷達圖

  • 第三方包 spider_chart: ^0.1.4
  • 演示效果
    121202.png
  • 核心程式碼
  child: Container(
          width: 300,
          height: 300,
          child: SpiderChart(
              data: [49.98, 24.4, 8.2, 41.47, 15.29],
              maxValue: 100,
              colors: <Color>[
                Colors.red,
                Colors.orange,
                Colors.green,
                Colors.yellow,
                Colors.indigo,
              ],
              labels: <String>[
                "吃",
                "喝",
                "玩",
                "樂",
                "學",
              ]),
        ),
複製程式碼

11.url_launcher_demo

這個主要是外部連線到其他 app,也就是說在 Flutter 應用內跳轉到瀏覽器,打電話,發簡訊等

  • 第三方包 url_launcher: ^5.2.7
  • 演示效果
    121201.gif
  • 核心程式碼
class _TestState extends State<Test> {
  @override
  Widget build(BuildContext context) {
    return Container(
        child: Column(
      children: <Widget>[
        RaisedButton(child: Text('我是一個按鈕'), onPressed: _launchURL)
      ],
    ));
  }

  _launchURL() async {
    String url = 'https://www.baidu.com/s?wd=';
    String keyWords = '中';
    String urlParams = '$url$keyWords';
    print('${urlParams}');
    if (await canLaunch(urlParams)) {
      await launch(urlParams);
    } else {
      throw 'Could not launch $url';
    }
  }
}
複製程式碼

4. 寫在最後

很高興你能看到這兒,我也是今天看了下,Flutter.dev官網,其版本更新到1.12了,希望加油,最後也期待都有收穫。我還想對你說


Although it is over, thank you

相關文章