1. 程式人生 > >Flutter 20: 圖解【分享頁面】底部對話方塊

Flutter 20: 圖解【分享頁面】底部對話方塊

      小菜在學習 Flutter 過程中需要處理一個類似 AndroidPopupWindow 效果的分享彈框頁。看似很簡單的頁面,裡面卻有很多值得嘗試的地方。

      小菜測試時主要用 GridViewBottomSheet 來實現的,當然也可以不用 GridView,小菜簡單介紹一下這兩組 Widget

GridView

      GridView 我們都很熟悉了,是日常中最常用到的控制元件之一,小菜前段時間學習了一下 ListView

的基本用法,GridView 的用法基本相同,小菜不再多說,只提醒一個屬性,用來設定 GridView item 基本屬性。

const SliverGridDelegateWithFixedCrossAxisCount({
   @required this.crossAxisCount, // 每行 item 個數
   this.mainAxisSpacing = 0.0,    // 列間距,即 item 左右間距
   this.crossAxisSpacing = 0.0,   // 行間距,即 item 上下間距
   this.childAspectRatio = 1.0,   // item 寬高比,預設1:1
})
Tips:注意設定 item 個數與列間距的配合,如果太大可能會造成頁面展示不全等異常情況。

BottomSheet

      BottomSheet 小菜理解為是從底部向上彈的工作表,主要分為兩種:

  1. Persistent 式工作表:類似於一個全新的頁面,完全展示 ScaffoldState.showBottomSheet
@override
Widget build(BuildContext context) {
return new Scaffold(
    appBar: new AppBar(
      title: new Text("分享頁面"),
    ),
    body: new Center(
      child: new Builder(builder: (BuildContext context) {
        return new FlatButton(
            onPressed: () {
              showBottomSheet(
                  context: context,
                  builder: (BuildContext context) {
                    return _showNomalWid(context);
                  });
            },
            child: new Text("我要分享"),
            color: Colors.blue);
      }),
    ));
}

Widget _showNomalWid(BuildContext context) {
    return new Container(
//        height: 320.0,
//      color: Colors.greenAccent,
      child: new GridView.builder(
        gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 4, mainAxisSpacing: 5.0, childAspectRatio: 1.0),
        itemBuilder: (BuildContext context, int index) {
          return new Column(
            children: <Widget>[
              new Padding(
                padding: EdgeInsets.fromLTRB(0.0, 6.0, 0.0, 6.0),
                child: new Image.asset(
                  'images/${urlItems[index]}', width: 50.0, height: 50.0, fit: BoxFit.fill,
                ),
              ),
              new Text(nameItems[index])
            ],
          );
        },
        itemCount: nameItems.length,
      ),
    );
}

1.1 若用 showBottomSheet 方式開啟工作表,同時內容 Widget 不限制寬高,效果為新開啟一個頁面,點選空白區不會消失,如圖:

1.2 若此時設定內容 Widget 寬高,會發現依舊是重新開啟一個頁面,高度從底向上佔據所設定高度,且點選空白區不會消失,如圖:

  1. Modal 式工作表:是一個半透明的頁面,預設佔據螢幕一半 ScaffoldState.showModalBottomSheet
@override
Widget build(BuildContext context) {
return new Scaffold(
    appBar: new AppBar(
      title: new Text("分享頁面"),
    ),
    body: new Center(
      child: new Builder(builder: (BuildContext context) {
        return new FlatButton(
            onPressed: () {
              showModalBottomSheet(
                  context: context,
                  builder: (BuildContext context) {
                    return _showNomalWid(context);
                  });
            },
            child: new Text("我要分享"),
            color: Colors.blue);
      }),
    ));
}

2.1 若用 showModalBottomSheet 方式開啟工作表,同時內容 Widget 不限制寬高,效果為開啟一個半透明頁面,預設佔據螢幕一半,點選空白區工作表消失,如圖:

2.2 若此時設定內容 Widget 寬高,會發現依舊是開啟一個半透明頁面,高度從底向上佔據所設定高度,且點選空白區會消失,如圖:

2.3 若此時設定內容 Widget 資料量很多,效果如何呢,這就是小菜選擇用 GridView 的原因,在現有寬高內進行可滑動操作即可,如圖:

核心原始碼

      小菜稍稍修飾了一下頁面效果,主要原始碼如下:

import 'package:flutter/material.dart';

class SharePopup extends StatelessWidget {
  List<String> nameItems = <String>[
    '微信', '朋友圈', 'QQ', 'QQ空間', '微博', 'FaceBook', '郵件', '連結'
  ];
  List<String> urlItems = <String>[
    'icon_wechat.png', 'icon_wechat_moments.png', 'icon_qq.png', 'icon_qzone.png',
    'icon_sina.png', 'icon_facebook.png', 'icon_email.png', 'icon_copylink.png'
  ];

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("分享頁面"),
        ),
        body: new Center(
          child: new Builder(builder: (BuildContext context) {
            return new FlatButton(
                onPressed: () {
                  showModalBottomSheet(
                      context: context,
                      builder: (BuildContext context) {
                        return _shareWidget(context);
                      });
                },
                child: new Text("我要分享"),
                color: Colors.blue);
          }),
        ));
  }

  Widget _shareWidget(BuildContext context) {
    return new Container(
      height: 250.0,
      child: new Column(
        children: <Widget>[
          new Padding(
            padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0),
            child: new Container(
              height: 190.0,
              child: new GridView.builder(
                gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 4,
                    mainAxisSpacing: 5.0,
                    childAspectRatio: 1.0),
                itemBuilder: (BuildContext context, int index) {
                  return new Column(
                    children: <Widget>[
                      new Padding(
                        padding: EdgeInsets.fromLTRB(0.0, 6.0, 0.0, 6.0),
                        child: new Image.asset( 'images/${urlItems[index]}', width: 50.0, height: 50.0, fit: BoxFit.fill, ) ),
                      new Text(nameItems[index])
                    ],
                  );
                },
                itemCount: nameItems.length,
              ),
            ),
          ),
          new Container( height: 0.5, color: Colors.blueGrey, ),
          new Center( child: new Padding(
              padding: EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0),
              child: new Text( '取  消', style: new TextStyle(fontSize: 18.0, color: Colors.blueGrey), ) ), )
        ],
      ),
    );
  }
}

      小菜剛接觸 Flutter 時間不長,還有很多不清楚和不理解的地方,如果有不對的地方還希望多多指教!